Safely maintain a Postfix mail queue with postsuper
You will use postsuper to move, release, requeue, expire or delete Postfix queue messages without guessing which queue a command touches. Allow about 15 minutes for a single-message operation, longer if you are checking a busy production queue. The examples assume Postfix 3.8.6, installed from the Ubuntu package 3.8.6-1ubuntu0.1 on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
Privilege boundary: postsuper is a privileged maintenance command. Run it as root, or as the local account permitted to administer the Postfix queue. Listing and flushing mail are different jobs: use postqueue for unprivileged queue operations where appropriate.
1. Confirm the installation and configuration
Check the binary and the settings that decide where queue files live. These commands 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 installed manual says that MAIL_CONFIG can select another directory containing main.cf, and -c CONFIG_DIR does the same explicitly. Use one deliberately when administering a non-default Postfix instance:
# postsuper -c /etc/postfix-instance2 -v -s
Checkpoint
Verify the configuration directory and queue directory before using any option that changes mail state. If they are not the instance you intended, stop and correct the command before adding sudo.
2. Understand what no option does
With no action option, postsuper performs its normal purge and structure-check operations across the Postfix queue directories. The -p operation removes old temporary files left by crashes. The -s operation checks queue structure, moves files to the right place and can repair names after a queue restore or hashing change.
Do not use a bare invocation as a harmless probe on a live system. It can change queue files. A structure check should normally be done before Postfix starts, and the manual's long-ID migration procedure first stops Postfix. For a migration from long to short queue IDs, the documented sequence is:
# postfix stop
# postconf enable_long_queue_ids=no
# postsuper
# postsuper
Repeat the final command until it stops reporting file-name changes, then start Postfix using your normal service procedure. Do not run this migration sequence merely to make queue IDs shorter.
3. Hold one message before investigating 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 diagnostic line and its wording vary by Postfix build and logging configuration. The useful result is a successful exit status and a report that one message was held. A held message is moved to the hold queue. It does not expire while held, even when its normal queue lifetime has passed.
To hold all messages in a particular queue, the safety-sensitive word must be uppercase:
# postsuper -h ALL deferred
Read that command twice before pressing Enter. ALL is not a placeholder here. A lower-case value such as all does not activate the all-messages safety escape.
4. Release or requeue a held message
Release a held message 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: if you need to stop delivery again, hold the message by its ID after it has returned to an ordinary queue. For held mail that has already spent a significant part of its allowed lifetime on hold, 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 equivalent to receiving the message again over SMTP. It is not subjected to smtpd_milters or non_smtpd_milters, but it is subjected again to address rewriting and the content-filter settings used for new local submissions. This can be useful after changing rewriting or filtering configuration, but it can also produce a different result from the original submission.
5. Force a message to expire
Use -e for a message in the normal queues, or -f when a held message must also be released so that the queue manager can return it to the sender:
# 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
These options are available from Postfix 3.5, including the installed 3.8.6 release. They request expiration; the return message is generated when the queue manager attempts delivery. The reason recorded for a deferred message includes its delay reason. For other queues it is administratively expired.
There is no undo for an expired message. Confirm the ID, sender, recipient and business approval before using this operation. If the real requirement is only to pause delivery, use -h instead.
6. Delete only when removal is intentional
Warning
Deletion is destructive. There is no postsuper undo command, and queue IDs can be reused while Postfix is delivering mail. A small race can therefore target a new message with the same ID. Confirm the ID immediately before 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
To delete all deferred messages, the queue name narrows the scope but does not make the action safe:
# postsuper -d ALL deferred
For a reviewed list, send one ID per line on standard input. This keeps selection separate from the destructive action:
$ 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 command output as an audit record. Do not pipe an unreviewed search directly into postsuper -d -. If a message may be needed for evidence, use postcat or your normal incident-preservation process before deletion.
7. Verify the result and diagnose failures
The command 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 requested maintenance command completed; it does not prove that a message was delivered. Check that the message is no longer in hold, or that it appears in the queue state you expect. If an ID cannot be found, refresh the queue listing rather than retrying blindly: the message may already have been delivered, returned or removed.
Use -v for more diagnostic logging, and repeat it for increased verbosity. If a command reports permission, configuration or queue-path errors, fix the underlying instance selection or privilege issue first. Do not solve a missing queue ID by switching to ALL.
Done means
- You confirmed the Postfix version, configuration directory and queue directory.
- You used
-hto pause delivery when investigation, not removal, was required. - You chose
-H,-r,-eor-faccording to the intended state change. - You treated
-dand everyALLcommand as destructive and reviewed the exact scope. - You checked the exit status, queue listing and Postfix diagnostics after the operation.