Manage the Postfix Mail Queue with postsuper
postsuper is the tool for holding, releasing or binning a stuck Postfix message without guessing which queue it sits in. Allow about 15 minutes for a single message, longer if you are working through a busy production queue. The examples below assume Postfix 3.8.6, installed from the Ubuntu package 3.8.6-1ubuntu0.1.
The route
Jump straight to the step you need, or tick off Done means at the end.
Warning
Postsuper is a privileged maintenance command. Run it as root, or as the account permitted to administer the Postfix queue. Listing and flushing mail are different jobs: use postqueue for the unprivileged queue checks.
1. Confirm the Install and the Config Paths
Check the binary and the settings that decide where queue files live. Both commands below only read configuration:
$ command -v postsuper
/usr/sbin/postsuper
$ postconf mail_version config_directory queue_directory enable_long_queue_ids
mail_version = 3.8.6
config_directory = /etc/postfix
queue_directory = /var/spool/postfix
enable_long_queue_ids = no
Your paths and queue-ID setting may differ. The manual says MAIL_CONFIG can point at another directory containing main.cf, and -c CONFIG_DIR does the same thing explicitly. Use one deliberately when you are administering a non-default Postfix instance:
# postsuper -c /etc/postfix-instance2 -v -s
Checkpoint
Verify the configuration directory and queue directory before touching any option that changes mail state. If they are not the instance you meant, stop and fix the command before adding sudo.
2. Know What a Bare Invocation Does
With no action option, postsuper still does something: it runs its normal purge and structure-check pass across the Postfix queue directories.
- -p removes crash debris. Old temporary files left behind by a crash get cleared out.
- -s repairs structure. It checks queue structure, moves files to the right place, and can repair names after a queue restore or a hashing change.
Do not run a bare invocation as a harmless probe on a live system: it can change queue files. A structure check should normally happen before Postfix starts. The manual's long-ID migration procedure stops Postfix first:
# postfix stop
# postconf enable_long_queue_ids=no
# postsuper
# postsuper
Repeat the final command until it stops reporting file-name changes, then start Postfix with your normal service procedure. Do not run this migration sequence just to shorten queue IDs for the sake of it.
3. Hold One Message Before You Investigate It
Holding a message stops delivery attempts. Replace QUEUE_ID with an exact ID from your queue listing:
# postsuper -h QUEUE_ID
$ printf 'postsuper exit status: %s\n' "$?"
postsuper exit status: 0
The wording of the diagnostic line varies by Postfix build and logging configuration. What matters is a successful exit status and a report that one message was held. A held message moves to the hold queue and does not expire while held, even past its normal queue lifetime.
To hold every message in a particular queue, the safety word must be uppercase:
# postsuper -h ALL deferred
Read that command twice before pressing Enter. ALL is not a placeholder here: a lower-case all does not trigger the all-messages behaviour.
4. Release or Requeue a Held Message
Release a held message back into the deferred queue with -H:
# postsuper -H QUEUE_ID
$ printf 'postsuper exit status: %s\n' "$?"
postsuper exit status: 0
This is reversible in the operational sense: hold the message again by its ID if you need to stop delivery once more. For mail that has already spent most of its allowed hold lifetime, use -r instead. Requeueing sends the message through the maildrop queue, where it is copied into a new queue file:
# postsuper -r QUEUE_ID
$ printf 'postsuper exit status: %s\n' "$?"
postsuper exit status: 0
Requeueing is not the same as receiving the message again over SMTP: it skips smtpd_milters and non_smtpd_milters, but it goes through address rewriting and the content-filter settings used for new local submissions again. Handy after a rewriting or filtering change, but it can produce a different result from the original submission.
5. Force a Message to Expire
Use -e for a message sitting in the normal queues, or -f when a held message needs releasing first so the queue manager can bounce it:
# postsuper -e QUEUE_ID
$ printf 'postsuper exit status: %s\n' "$?"
postsuper exit status: 0
# postsuper -f HELD_QUEUE_ID
$ printf 'postsuper exit status: %s\n' "$?"
postsuper exit status: 0
Both options have been available since Postfix 3.5, including the installed 3.8.6. They request expiration; the queue manager generates the bounce when it next attempts delivery. A deferred message's bounce includes its delay reason; other queues get marked administratively expired.
Warning
There is no undo for an expired message. Confirm the ID, sender, recipient and business approval before running this. If you only need to pause delivery, use -h instead.
6. Delete Only When You Mean It
Warning
Deletion is destructive and there is no postsuper undo. Queue IDs can be reused while Postfix is delivering mail, so a small race can target a new message that happens to share the old ID. Confirm the ID immediately before running the command, and avoid broad deletion during an active incident unless you accept that risk.
# postsuper -d QUEUE_ID
$ printf 'postsuper exit status: %s\n' "$?"
postsuper exit status: 0
Naming a queue narrows the scope but does not make deletion safe:
# postsuper -d ALL deferred
For a reviewed list, feed one ID per line on standard input. This keeps selection separate from the destructive step:
$ printf '%s\n' QUEUE_ID_1 QUEUE_ID_2 > reviewed-queue-ids.txt
# postsuper -d - < reviewed-queue-ids.txt
$ printf 'postsuper exit status: %s\n' "$?"
postsuper exit status: 0
Keep the review file and the command output as an audit record. Never pipe an unreviewed search straight into postsuper -d -. If a message might be needed as evidence, run it through postcat or your incident-preservation process first.
7. Verify the Result and Diagnose Failures
postsuper reports counts to standard error and to Postfix logging. Capture both the exit status and the relevant queue listing:
# postsuper -H QUEUE_ID
$ status=$?
$ printf 'postsuper exit status: %s\n' "$status"
postsuper exit status: 0
$ postqueue -p
A zero status means the maintenance command completed, not that a message was delivered. Check that it has left hold, or that it now sits in the queue state you expect. If an ID cannot be found, refresh the queue listing instead of retrying blindly: the message may already have been delivered, bounced or removed.
Add -v for more diagnostic logging, and repeat it for more verbosity still. A permission, configuration or queue-path error means you should fix the instance selection or privilege problem first. Never solve a missing queue ID by switching to ALL.
Done means
- You confirmed the version and paths. Postfix version, configuration directory and queue directory all checked out.
- You used
-hto pause, not remove. Investigation did not turn into deletion by accident. - You picked the right state change.
-H,-r,-eor-fmatched what you actually meant to happen. - You treated
-dandALLas destructive. Scope reviewed before either one ran. - You checked the aftermath. Exit status, queue listing and Postfix diagnostics all confirmed the result.