Retry Deferred Mail Safely with Postfix flush
Every time you ask postqueue to retry a stuck message, it is Postfix's flush(8) service doing the actual work behind the scenes. This guide covers retrying the whole queue, one destination, or one queue ID, in about five minutes if Postfix is already running. You need shell access to the mail host, and the queue-control commands normally need the Postfix administrator's privileges.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
This guide matches the Postfix package installed on this machine, reported version 3.8.6. Check the active version and confirm you are looking at the right queue:
postconf mail_version
postqueue -p
The first command reports the installed Postfix version, the second lists queued mail. Read the listing before flushing anything: a flush starts delivery attempts, but it fixes nothing about DNS, authentication, routing, remote refusal, or whatever actually deferred the message.
Checkpoint
You know whether the queue is empty, which destinations are affected, and whether a retry is even worth it. Do not flush an undeliverable queue repeatedly; the Postfix documentation warns that frequent flushing can hurt delivery performance for other mail too.
1. Flush the whole queue
To ask the queue manager to attempt delivery of everything queued, run this as the Postfix administrator:
sudo postqueue -f
This only requests a queue scan. It does not guarantee delivery, remove messages, or bypass normal transport policy. A message that still cannot be delivered stays queued with a new reason recorded for later inspection.
Verify the queue again once you have allowed time for a delivery attempt:
postqueue -p
Compare this against the earlier listing. A message that vanished may have been delivered, bounced, or otherwise moved by normal Postfix processing; use the log and the message's queue ID when you need to tell those apart.
Checkpoint
A successful postqueue -f only means Postfix accepted the request to scan the queue. It is not a delivery receipt.
2. Retry one destination
When one remote site has recovered and you do not want to provoke retries for unrelated mail, use the fast-flush destination operation:
sudo postqueue -s example.net
Replace example.net with the destination you have actually verified. Postfix identifies a destination from the part after the right-most @, and this only helps destinations eligible for fast flush, which by default on this installation means $relay_domains, not every domain on the internet.
For an eligible destination, Postfix keeps deferred queue IDs in a per-destination logfile and uses it to request immediate delivery. That request can cover every message recorded for that destination, so check the queue first if scope matters to you.
If the command reports the site is not eligible, do not work around it by hand-editing /var/spool/postfix/flush. Use the whole-queue operation instead, or review the relay configuration with the mail administrator.
3. Retry one queue ID
For a single deferred message, get its queue ID from the listing first, then request immediate delivery:
postqueue -p
sudo postqueue -i QUEUE_ID
Replace QUEUE_ID with the exact ID from your listing. The -i operation has used the flush(8) service since Postfix 2.4, making it the focused alternative to a whole-queue flush.
Queue IDs are operational identifiers, not proof a message belongs to the recipient you expect. Confirm sender and recipients in the listing before retrying, and never guess an ID or paste one in from a different Postfix instance.
Checkpoint
One message, one eligible destination, or the whole queue are three different scopes. If a command hit the wrong one, stop issuing retries and go inspect the current queue and logs instead.
4. Check the fast-flush defaults
Read the active values instead of assuming the package defaults; these commands are read-only:
postconf fast_flush_domains fast_flush_refresh_time fast_flush_purge_time
postconf queue_directory
postconf -M flush
On this host, the reported values are:
| Setting | Value | Meaning |
|---|---|---|
fast_flush_domains | $relay_domains | Destinations that get per-destination fast-flush records |
fast_flush_refresh_time | 12h | Age after which a non-empty, unread record is refreshed |
fast_flush_purge_time | 7d | Age after which an empty record can be removed |
queue_directory | /var/spool/postfix | Top-level Postfix queue directory |
The final command should show a flush service managed by master. Do not start flush directly: the manpage says it expects to be run by the Postfix master process, and its requests use internal Postfix communication rather than a public network interface.
5. Let Postfix maintain the records
Fast-flush files are truncated after a send request, not necessarily when delivery actually completes, so they can hold old or redundant queue IDs. The service's own refresh operation deals with stale records and purges old empty files, and Postfix normally triggers that maintenance through the service configuration rather than you doing it by hand.
Check the configured wakeup value before changing anything:
postconf -M flush
postconf -h config_directory
If you need to change main.cf or master.cf, save a copy first, edit only the intended setting, validate the configuration, then reload:
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.before-flush-change
sudo postfix check
sudo postfix reload
Recovery
The copy above is your rollback point for this specific change. Restore it only after confirming no other administrator has made later edits:
sudo cp /etc/postfix/main.cf.before-flush-change /etc/postfix/main.cf
sudo postfix check
sudo postfix reload
Changing fast-flush eligibility only changes which destinations get these acceleration records. It does not make a remote server accept mail, and broadening the list can add to your queue-management work. Treat it as a configuration change, not a repair for one stuck message.
Common traps
- Running
flushas a standalone command. It is a Postfix daemon service, not the operator interface. Usepostqueueinstead. - Expecting the retry to fix delivery. A retry only repeats the delivery path. Check the recorded failure before you retry again.
- Assuming every domain is eligible. The default is
$relay_domains; confirmfast_flush_domainsbefore reaching forpostqueue -s. - Editing the spool directory. Files under
/var/spool/postfix/flushare internal state; manual deletion loses scheduling information and undoes nothing about a failed delivery. - Confusing acceptance with completion. An empty queue after a delay is useful evidence, but check the logs whenever delivery, bounce or filtering behaviour actually matters.
Done means
- Queue checked first, so you knew the exact retry scope before acting.
- Right command used.
postqueue -f,-s, or-i, with administrator privileges where required. - Queue verified again, and logs checked for anything still failing.
- Fast-flush settings confirmed rather than assumed from memory.
- flush service left under master control, with no hand-editing of its spool files.