2026年11月14日
2026年11月14日
Postfix Queue Management: postqueue, postcat and postsuper Explained
This article is a localized edition of the Japanese original. Read the Japanese original
When Postfix cannot deliver a message right away, it puts it in a queue and retries later. When mail does not arrive, sending is delayed, or a server is sending a flood of mail, checking the queue first usually points you toward the cause.
This article covers the three commands used to check and manage the queue.
| Command | Main use | Run as |
|---|---|---|
postqueue |
Listing the queue, requesting delivery | Listing works as a regular user |
postcat |
Showing the contents of one queued message | root |
postsuper |
Delete, hold, release, requeue | root |
The commands and output were checked with Postfix 3.10 on Debian 13 as of October 2026.
Queue types
The Postfix queues are directories under /var/spool/postfix/. Do not delete or move the files directly; use the commands in this article.
| Queue | Contains |
|---|---|
maildrop |
Mail just submitted with the local sendmail command |
incoming |
Mail that was accepted and is being prepared for delivery |
active |
Mail currently being delivered |
deferred |
Mail that could not be delivered because of a temporary error and is waiting to be retried |
hold |
Mail put on hold by an administrator. Not delivered until released |
corrupt |
Damaged, unreadable queue files |
When people say "mail is piling up in the queue", they almost always mean deferred mail.
List the queue
postqueue -p
mailq gives the same result. The output looks like this.
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
949F63B5C1 285 Thu Oct 29 17:36:33 sender@mail.example.test
(connect to 127.0.0.1[127.0.0.1]:2525: Connection refused)
alice@example.net
9AF003B5C2 285 Thu Oct 29 17:36:33 sender@mail.example.test
(connect to 127.0.0.1[127.0.0.1]:2525: Connection refused)
bob@example.net
-- 0 Kbytes in 2 Requests.
- First line: queue ID, size (bytes), arrival time, sender
- The line in
( ): why delivery failed (the delay reason). This is the most useful part when looking for the cause - Last line: recipient
A symbol may follow the queue ID directly.
| Symbol | Meaning |
|---|---|
* |
In the active queue, being delivered |
! |
In the hold queue, on hold |
# |
Forced to expire with postsuper -e or similar (returned to the sender at the next delivery attempt) |
| none | Waiting for a retry in deferred or similar |
The final line, such as -- 0 Kbytes in 2 Requests., shows the number of messages. When the queue is empty, it shows Mail queue is empty.
List the queue as JSON
postqueue -j prints one line of JSON per message. This is easier to handle in scripts.
postqueue -j
{"queue_name": "deferred", "queue_id": "949F63B5C1", "arrival_time": 1793295393, "message_size": 285, "forced_expire": false, "sender": "sender@mail.example.test", "recipients": [{"address": "alice@example.net", "delay_reason": "connect to 127.0.0.1[127.0.0.1]:2525: Connection refused"}]}
With jq you can count messages per queue or per delay reason.
# Count per queue
postqueue -j | jq -r .queue_name | sort | uniq -c
# Count per delay reason (most frequent first)
postqueue -j | jq -r '.recipients[].delay_reason' | sort | uniq -c | sort -rn | head
Count by recipient domain
qshape tallies queued mail by recipient domain and by age. Use it to see whether only mail to a particular domain is piling up.
sudo qshape deferred
T 5 10 20 40 80 160 320 640 1280 1280+
TOTAL 3 3 0 0 0 0 0 0 0 0 0
example.net 2 2 0 0 0 0 0 0 0 0 0
example.org 1 1 0 0 0 0 0 0 0 0 0
T is the total, and the numbers to the right are counts by age (minutes). On Debian / Ubuntu, qshape is included in the postfix package.
View one message
sudo postcat -q 949F63B5C1
This shows the envelope (sender and recipients), headers and body together. To see only part of it, put one of these options before -q.
| Command | Shows |
|---|---|
sudo postcat -hq QUEUE_ID |
Headers only |
sudo postcat -bq QUEUE_ID |
Body only |
sudo postcat -eq QUEUE_ID |
Envelope only |
When checking whether the server is being abused to send spam, look at the Received: and From: headers. Mail submitted by a program on the server with the sendmail command records the submitting user's ID, as in Received: by mail.example.test (Postfix, from userid 0). If it is the web server's user (www-data on Debian / Ubuntu is usually 33), you can guess the mail came from PHP, such as WordPress.
Message contents include personal information. Look only at what you need for the investigation.
Operate on the queue
Retry now
# Attempt delivery of all queued mail now
sudo postqueue -f
# Attempt delivery of one message now
sudo postqueue -i 949F63B5C1
deferred mail is normally retried automatically after a while. Use these when you have fixed the cause (DNS, a remote server outage, a firewall and so on) and do not want to wait. Repeating postqueue -f while a lot of undeliverable mail is queued slows down delivery of other mail too, so fix the cause first.
Hold and release
# Put on hold (move to the hold queue)
sudo postsuper -h 949F63B5C1
# Release from hold
sudo postsuper -H 949F63B5C1
Held mail is neither delivered nor expired until you release or delete it. This is useful when you want to stop suspected spam before deciding whether to delete it.
Delete
# Delete one message
sudo postsuper -d 949F63B5C1
# Delete all mail in the deferred queue
sudo postsuper -d ALL deferred
# Delete all queued mail
sudo postsuper -d ALL
Deleted mail cannot be recovered, and no error message is returned to the sender. Before using ALL, check with postqueue -p what will be deleted.
Delete in bulk by recipient or sender
postsuper -d - reads queue IDs from standard input and deletes them. Filter the targets with postqueue -j and jq and pipe them in to delete only mail for a specific recipient or sender.
# Delete mail addressed to bob@example.net
postqueue -j \
| jq -r 'select(any(.recipients[]; .address == "bob@example.net")) | .queue_id' \
| sudo postsuper -d -
# Delete mail from spam@example.com
postqueue -j | jq -r 'select(.sender == "spam@example.com") | .queue_id' | sudo postsuper -d -
To be safe, run it first without | sudo postsuper -d -, confirm that the printed queue IDs are the ones you intend, then delete.
Expire and return to the sender
sudo postsuper -e 949F63B5C1
-e marks a message as expired. At the next delivery attempt (which you can trigger with postqueue -i and the like), Postfix gives up and returns an error message to the sender. Unlike deletion, the sender learns that the mail was not delivered.
Requeue
sudo postsuper -r 949F63B5C1
sudo postsuper -r ALL
-r puts mail back into the queue so that address rewriting and next-hop decisions are redone. Use it after changing settings such as relayhost or transport_maps when you want queued mail to use the new settings.
Retry intervals and lifetime
How often deferred mail is retried and when Postfix gives up are set by the following parameters (values are Postfix 3.10 defaults).
| Parameter | Default | Meaning |
|---|---|---|
queue_run_delay |
300s |
How often the deferred queue is scanned |
minimal_backoff_time |
300s |
Shortest wait before a retry |
maximal_backoff_time |
4000s |
Longest wait before a retry |
maximal_queue_lifetime |
5d |
Mail still undeliverable after this period is returned to the sender |
bounce_queue_lifetime |
5d |
How long error messages (bounces) themselves stay in the queue |
Check the current values with postconf.
postconf maximal_queue_lifetime bounce_queue_lifetime queue_run_delay minimal_backoff_time maximal_backoff_time
Check regularly that the queue is not piling up
A simple script that logs a message when the queue exceeds a set number of messages. Save it as /usr/local/bin/postfix-queue-check.sh.
#!/bin/bash
# If the Postfix queue holds more than LIMIT messages, log it and exit with code 1
LIMIT=50
COUNT=$(/usr/sbin/postqueue -j | wc -l)
if [ "$COUNT" -gt "$LIMIT" ]; then
/usr/bin/logger -t postfix-queue-check "queue has $COUNT messages (limit $LIMIT)"
exit 1
fi
sudo chmod 755 /usr/local/bin/postfix-queue-check.sh
Register it with sudo crontab -e to run every 10 minutes.
*/10 * * * * /usr/local/bin/postfix-queue-check.sh
You can read the entries with journalctl -t postfix-queue-check. Since mail sending itself may be what has stopped, use something other than email (a chat webhook or an external monitoring service) if you need notifications.
Common delay reasons
Example reason in postqueue -p |
Likely cause |
|---|---|
connect to ...:25: Connection timed out |
Outbound connections to port 25 are blocked (OP25B at the cloud or ISP), or the remote server does not respond |
connect to ...: Connection refused |
The remote server or relay is not listening on that port |
Host or domain name not found |
The MX / A record for the recipient domain cannot be looked up (misspelled domain, DNS failure) |
451 4.7.1 ... try again later and similar |
The remote server is temporarily refusing (greylisting, rate limits) |
After fixing the cause, retry with sudo postqueue -f and confirm with postqueue -p that the count goes down. You can follow delivery progress by searching the mail log for the queue ID (/var/log/mail.log on Debian / Ubuntu, /var/log/maillog on the RHEL family, journalctl where rsyslog is not installed).
Summary
- Use
postqueue -p(mailq) to see the list and delay reasons. In scripts, usepostqueue -jwithjq - Use
qshape deferredto see which domains mail is piling up for - Use
postcat -qto view one message (-hheaders,-bbody) - Use
postsuperto delete (-d), hold (-h/-H), expire (-e) and requeue (-r).-d -deletes only the filtered messages in bulk - Fix the cause before retrying with
postqueue -f
Related articles:
- How to test email sending (telnet, curl, swaks)
- How to investigate mail problems with Postfix logs
- How to rate-limit sending in Postfix
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)