From a5b9a98baad663f6c2ec773645898e8c78f1efd4 Mon Sep 17 00:00:00 2001 From: link2xt Date: Tue, 19 May 2026 22:46:59 +0200 Subject: [PATCH] fix: limit the number of LMTP clients for filtermail-transport to 1000 Postfix does not have jitter for deferred mails and scans the queue periodically every queue_run_delay (). As a result it is likely to try delivering many deferred messages at the same time. Normally the number of outgoing connections should be low even with unreachable destinations, but after the server downtime or if admin flushes the queue manually it is possible that a lot of messages to the same unreachable destination expire at once and are moved from "deferred" into the "active" queue. Trying to deliver them all at once may make the server run out of memory by starting many LMTP clients. Limiting the number of LMTP processes turns OOM problem into head of line blocking problem. Messages sent to reachable destinations will be delayed as well, but at least deferred messages will get distributed over time. In this case "active" queue may grow (up to qmgr_message_active_limit defaulting to 20000), but then admin may notice the problem and solve it e.g. by making the destinations reachable or setting up a transport map to route messages for known dead servers into discard transport. Eventually the problem should be solved by filtermail-transport quickly returning temporary errors for destinations which already have many messages queued, then we can reduce "maxproc" further. --- cmdeploy/src/cmdeploy/postfix/main.cf.j2 | 12 ++++++++++++ cmdeploy/src/cmdeploy/postfix/master.cf.j2 | 10 +++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/cmdeploy/src/cmdeploy/postfix/main.cf.j2 b/cmdeploy/src/cmdeploy/postfix/main.cf.j2 index 6f88277c..9ec9f039 100644 --- a/cmdeploy/src/cmdeploy/postfix/main.cf.j2 +++ b/cmdeploy/src/cmdeploy/postfix/main.cf.j2 @@ -102,6 +102,18 @@ smtpd_peername_lookup = no # so instead this is handled in filtermail. # We use LMTP instead SMTP so we can communicate per-recipient errors back to postfix. default_transport = lmtp-filtermail:inet:[127.0.0.1]:{{ config.filtermail_lmtp_port_transport }} + +# All deliveries over lmtp-filtermail are treated +# as having the same destination [127.0.0.1], +# so it is not possible to limit per-destination concurrency here, +# it is a job for filtermail-transport. +# Total number of parallel deliveries is limited +# by "maxproc" column in /etc/postfix/master.cf for lmtp-filtermail. +# Settings below are to prevent Postfix queue manager +# from limiting the number of LMTP connections to filtermail-transport. +# Read and +# for the details +# of the Postfix algorithm that we effectively disable here. lmtp-filtermail_initial_destination_concurrency=10000 lmtp-filtermail_destination_concurrency_limit=10000 diff --git a/cmdeploy/src/cmdeploy/postfix/master.cf.j2 b/cmdeploy/src/cmdeploy/postfix/master.cf.j2 index 84c17413..f704c96c 100644 --- a/cmdeploy/src/cmdeploy/postfix/master.cf.j2 +++ b/cmdeploy/src/cmdeploy/postfix/master.cf.j2 @@ -105,7 +105,15 @@ filter unix - n n - - lmtp authclean unix n - - - 0 cleanup -o header_checks=regexp:/etc/postfix/submission_header_cleanup -lmtp-filtermail unix - - y - 10000 lmtp +# Reducing `maxproc` here may result in a head of line blocking +# when there are many messages sent to unreachable destinations +# at the same time. +# LMTP clients here talk to filtermail-transport. +# LMTP has no pipelining, +# so while filtermail-transport tries to deliver the message, +# possibly waiting for a long connection timeout +# or talking to a slow server, LMTP client cannot be reused. +lmtp-filtermail unix - - y - 1000 lmtp -o syslog_name=postfix/lmtp-filtermail -o lmtp_header_checks= -o lmtp_tls_security_level=none