Tune Postfix Queue Delivery Safely with qmgr
Mail piling up in the Postfix deferred queue? Before you crank up qmgr's concurrency, learn what it is actually doing. You will see where the queue manager fits, inspect the live queue defaults, and make one controlled delivery change that survives a reload. Allow about 20 minutes, plus time to watch the result. The examples are for the installed Postfix 3.8.6 package on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
1. Check the service and configuration
qmgr is a daemon, not a command-line queue viewer. Postfix's master process starts it, and it arranges delivery through the delivery processes. Do not try to launch qmgr by hand.
Start with these unprivileged checks:
$ postconf mail_version queue_directory config_directory
mail_version = 3.8.6
queue_directory = /var/spool/postfix
config_directory = /etc/postfix
Your package revision may differ while the major and minor Postfix version stay the same.
Tip
The queue manager is a persistent process that reads main.cf settings, so editing the file alone does not change its running behaviour.
Checkpoint
You know which Postfix installation you are inspecting and where its queue and configuration live.
2. Map the queues before changing anything
The queue manager keeps a separate directory for each stage:
incomingholds new mail waiting to be picked up.activeholds messages opened for delivery.deferredholds messages that previously got a temporary failure.holdholds mail deliberately kept back.corruptis where damaged queue files go.bounce,deferandtracekeep delivery status data separately.
Listing them is read-only, but the spool is normally restricted, so use elevated privileges for this inspection only:
$ sudo find "$(postconf -h queue_directory)" -maxdepth 1 -mindepth 1 -type d -printf '%f\n' | sort
active
bounce
corrupt
defer
deferred
hold
incoming
trace
The exact list depends on the installation. A large deferred queue means delivery has been postponed; it does not tell you why. Read the Postfix log and the destination's response before increasing concurrency.
3. Inspect qmgr's important defaults
Start with the settings for retry timing, per-destination parallelism and recipients held in memory:
$ postconf minimal_backoff_time maximal_backoff_time queue_run_delay maximal_queue_lifetime
minimal_backoff_time = 300s
maximal_backoff_time = 4000s
queue_run_delay = 300s
maximal_queue_lifetime = 5d
$ postconf initial_destination_concurrency default_destination_concurrency_limit default_destination_recipient_limit
initial_destination_concurrency = 5
default_destination_concurrency_limit = 20
default_destination_recipient_limit = 50
These are defaults, not promises that every message uses all of them.
- Transports can override. A destination's transport can override the matching
transport_...parameter. - Slow start and feedback. A destination that fails during connection or handshake can have delivery suspended, while successful deliveries let concurrency grow.
- History. The qmgr manual notes that
minimal_backoff_timedefaulted to 1000s before Postfix 2.4, so old tuning advice may quote that figure.
Two easy traps:
queue_run_delayis not a retry schedule. It is the interval between deferred-queue scans. Each deferred message also has its own backoff and timestamp, so nothing guarantees a retry exactly on that schedule.default_destination_concurrency_limitis per destination, not a server-wide delivery cap.
Checkpoint
Save the output in your change record, and decide whether the bottleneck is retry timing, destination concurrency, recipient batching or the remote system.
4. Make one small, reversible change
For a temporary remote outage, easing pressure is usually safer than making Postfix more aggressive. For example, set a longer queue scan interval so deferred mail is checked less often. This changes the persistent configuration:
$ sudo postconf -e 'queue_run_delay = 600s'
$ postconf queue_run_delay
queue_run_delay = 600s
The command edits /etc/postfix/main.cf through Postfix's configuration tool. It does not restart the service and does not immediately alter the running queue manager. Record the old value first so recovery is exact.
Tip
Only shorten the scan interval when you have a real reason to check deferred mail more often; that can increase disk and network activity.
Warning
Do not lower retry intervals or raise concurrency to hide remote 4xx responses, rate limits or connection failures. That can amplify load and make the outage worse. Change one setting at a time, and never edit the queue directories by hand.
5. Reload and verify the running daemon
After a main.cf change you must reload Postfix.
Warning
A reload is a service-disrupting control action. Use a maintenance window if your mail flow is sensitive.
$ sudo postfix reload
$ postconf queue_run_delay
queue_run_delay = 600s
The reload asks the Postfix process manager to apply configuration to persistent daemons, including qmgr.
Checkpoint
The system's Postfix log shows the reload and subsequent delivery activity. The qmgr manual says problems and transactions are logged through syslogd or postlogd; the exact log file is distribution-specific.
Recovery
If the change was wrong, restore the recorded value and reload again. That is the undo path for this example.
$ sudo postconf -e 'queue_run_delay = 300s'
$ sudo postfix reload
$ postconf queue_run_delay
queue_run_delay = 300s
Warning
Do not remove deferred messages to make a queue count look better. Deleting queued mail is a separate, potentially irreversible operation.
6. Diagnose symptoms without guessing
A few qmgr behaviours explain most "why is this mail still stuck?" moments:
- Leaky bucket. The active queue is limited so a burst cannot consume unbounded memory.
- Fair shares. When active space is free, qmgr alternates between incoming and deferred work, and picks destinations round-robin.
- Exponential backoff. The retry interval doubles after each temporary failure.
Together these explain why a new message can be delivered while older mail stays deferred, and why repeatedly forcing scans is not a cure.
- Keep corrupt files. Corrupt queue files are moved to
corruptfor inspection. Preserve them and the related log entries until you understand the failure. - Look local before global. If only one remote destination is failing, inspect its response and the transport-specific settings before changing global qmgr limits.
Done means
- Know the install. You can name the Postfix version, queue directory and configuration directory.
- Spool untouched. You checked queue state and qmgr defaults without editing spool files.
- Old value recorded. You wrote down the previous value before making one configuration change.
- Reloaded and verified. You reloaded Postfix after editing
main.cfand confirmed the setting. - Rollback ready. You have a tested rollback command and are watching the logs for the destination's response.