--- title: "How to Troubleshoot Postfix Email Delivery with Mail Logs" description: "Find out why email is not sending or arriving on a Postfix server on Ubuntu 24.04 by reading mail.log, the mail queue, SMTP error codes and DNS records." category: "Troubleshooting Guide" language: en source: https://cubepath.com/docs/troubleshooting-guide/email-not-sending-receiving-log-analysis updated: 2026-09-25 --- # How to Troubleshoot Postfix Email Delivery with Mail Logs When a mail server stops delivering, the answer is almost always written in its log: every message Postfix handles leaves a trail with a queue ID, the remote server it talked to, and the exact SMTP reply it got back. In this tutorial you will read those logs on Ubuntu 24.04 to find out why outgoing mail is deferred or bounced, why incoming mail never arrives, and how to fix the most common causes. ## Prerequisites To follow this tutorial you need: - A server running Ubuntu 24.04 LTS with Postfix installed and configured, for example a CubePath VPS. - A non-root user with `sudo` privileges. - A domain whose MX record points to the server. This guide uses `your_domain`, the hostname `mail.your_domain` and the IP `203.0.113.10`; replace them with your own values. - An external mailbox (Gmail, Outlook or similar) to send test messages to and from. The commands target Postfix, the default MTA on Ubuntu and Debian. Exim and Sendmail log similar information in different formats. ## Step 1 - Checking that Postfix is running and listening Start by ruling out the obvious. On Ubuntu, Postfix runs as the `postfix@-` systemd instance: ```bash sudo systemctl status postfix@- ``` ```output ● postfix@-.service - Postfix Mail Transport Agent (instance -) Loaded: loaded (/usr/lib/systemd/system/postfix@.service; enabled-runtime; preset: enabled) Active: active (running) since Fri 2026-09-25 09:58:12 UTC; 2h ago ``` Run Postfix's own consistency check. It prints nothing when the configuration and file permissions are correct: ```bash sudo postfix check ``` Then confirm that Postfix listens on port 25 on all interfaces, not only on localhost: ```bash sudo ss -ltnp | grep ':25 ' ``` ```output LISTEN 0 100 0.0.0.0:25 0.0.0.0:* users:(("master",pid=1234,fd=13)) LISTEN 0 100 [::]:25 [::]:* users:(("master",pid=1234,fd=14)) ``` If you only see `127.0.0.1:25`, the server cannot receive mail from the Internet. Check `inet_interfaces`, which should be `all` on a server that receives mail: ```bash postconf inet_interfaces ``` ```output inet_interfaces = all ``` ## Step 2 - Finding the mail logs On Ubuntu 24.04, rsyslog writes all mail activity to `/var/log/mail.log`. Follow it live while you reproduce the problem: ```bash sudo tail -f /var/log/mail.log ``` The same entries are also in the systemd journal, which is useful if rsyslog is not installed: ```bash sudo journalctl -u postfix@- --since "1 hour ago" ``` On RHEL-family systems the file is `/var/log/maillog`. ## Step 3 - Following one message through the log Postfix assigns every message a queue ID and prints it on each line that concerns that message. Once you have the ID, you can reconstruct the whole delivery. Send a test message from the server to your external mailbox: ```bash printf 'Subject: Postfix test\n\nTest message from mail.your_domain\n' | sendmail -f admin@your_domain you@example.net ``` Find the most recent delivery attempt for that recipient: ```bash sudo grep 'to=' /var/log/mail.log | tail -n 1 ``` ```output 2026-09-25T10:12:02.418305+00:00 mail postfix/smtp[23145]: 4B7F21A0C3: to=, relay=mx.example.net[198.51.100.20]:25, delay=1.2, delays=0.05/0.01/0.62/0.52, dsn=2.0.0, status=sent (250 2.0.0 OK 1727259122 queued) ``` Then pull every line for that queue ID: ```bash sudo grep 4B7F21A0C3 /var/log/mail.log ``` ```output ... postfix/pickup[22901]: 4B7F21A0C3: uid=1000 from= ... postfix/cleanup[23140]: 4B7F21A0C3: message-id=<20260925101201.4B7F21A0C3@mail.your_domain> ... postfix/qmgr[1240]: 4B7F21A0C3: from=, size=412, nrcpt=1 (queue active) ... postfix/smtp[23145]: 4B7F21A0C3: to=, relay=mx.example.net[198.51.100.20]:25, ... status=sent (250 2.0.0 OK) ... postfix/qmgr[1240]: 4B7F21A0C3: removed ``` The fields on the delivery line tell you what happened: - **relay**: the server Postfix delivered to. `relay=none` means it never managed to connect. - **status**: `sent` (accepted by the next server), `deferred` (temporary failure, Postfix will retry), or `bounced` (permanent failure, a bounce was sent to the sender). - **dsn**: the enhanced status code. `2.x.x` is success, `4.x.x` temporary, `5.x.x` permanent. - **delays**: four numbers in seconds: time before the queue manager, time in the queue manager, connection setup (DNS, TCP, TLS, HELO), and message transmission. A large third value points to network or DNS trouble, not to Postfix. - **The text in parentheses**: the remote server's reply or Postfix's own error. This is the most important part. For incoming mail the same technique works: grep for `to=` and look for `postfix/lmtp`, `postfix/local` or `postfix/virtual` lines with `status=sent`. ## Step 4 - Getting an overview of failures When many messages are affected, count the outcomes to see the scale of the problem: ```bash sudo grep -oP 'status=\K\w+' /var/log/mail.log | sort | uniq -c ``` ```output 812 bounced 1903 deferred 4521 sent ``` Group the deferral reasons to find the dominant one: ```bash sudo grep 'status=deferred' /var/log/mail.log | grep -oP 'status=deferred \(\K.*(?=\)$)' | sort | uniq -c | sort -rn | head ``` ```output 1650 connect to mx.example.net[198.51.100.20]:25: Connection timed out 190 host mx.example.org[192.0.2.44] said: 451 4.7.1 Greylisted, please try again later (in reply to RCPT TO command) ``` Messages that were rejected before they even got a queue ID are logged with `NOQUEUE`. These are usually incoming connections that Postfix refused: ```bash sudo grep 'NOQUEUE: reject' /var/log/mail.log | tail -n 5 ``` For daily summaries, install `pflogsumm`, which parses the log into totals, top senders and recipients, and lists of deferrals and bounces: ```bash sudo apt install pflogsumm sudo pflogsumm -d today /var/log/mail.log | less ``` ## Step 5 - Working with the mail queue Deferred messages wait in the queue and Postfix retries them automatically with increasing intervals. List the queue with the reason for each deferral: ```bash sudo postqueue -p ``` ```output -Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient------- 9C2D41A0F7 1204 Fri Sep 25 10:40:11 admin@your_domain (connect to mx.example.net[198.51.100.20]:25: Connection timed out) you@example.net -- 1 Kbytes in 1 Request. ``` Inspect a queued message, including headers, with `postcat`: ```bash sudo postcat -vq 9C2D41A0F7 | less ``` After you fix the cause, ask Postfix to retry everything immediately instead of waiting for the next scheduled attempt: ```bash sudo postqueue -f ``` To retry a single message, use `sudo postqueue -i 9C2D41A0F7`. To delete one message, use `sudo postsuper -d 9C2D41A0F7`. If the queue is full of spam from a compromised account, delete the whole deferred queue only after you have found and stopped the source: ```bash sudo postsuper -d ALL deferred ``` ## Step 6 - Diagnosing outgoing mail failures Match the reason in the log with these common causes. ### Connection timed out to port 25 ```text connect to mx.example.net[198.51.100.20]:25: Connection timed out ``` If every destination times out, outbound port 25 is blocked, either by a local firewall or by your provider. Many cloud providers block outbound SMTP on new accounts to prevent spam. Test a known mail server directly: ```bash nc -vz -w 5 gmail-smtp-in.l.google.com 25 ``` ```output Connection to gmail-smtp-in.l.google.com (142.250.27.26) 25 port [tcp/smtp] succeeded! ``` If this times out, contact your provider to have outbound port 25 enabled, or send through an authenticated relay (smarthost) on port 587 by setting `relayhost` in `/etc/postfix/main.cf`. ### Rejected for authentication or reputation Large providers explain their rejections in the reply text. Typical examples: ```text 550-5.7.26 This mail has been blocked because the sender is unauthenticated. 550-5.7.25 The IP address sending this message does not have a PTR record setup ``` Check your SPF, DKIM and DMARC records and the server's reverse DNS: ```bash dig your_domain TXT +short | grep spf1 dig _dmarc.your_domain TXT +short dig -x 203.0.113.10 +short ``` The PTR should return `mail.your_domain.`, and that name should resolve back to `203.0.113.10`. The hostname Postfix announces in HELO should match it: ```bash postconf myhostname ``` ```output myhostname = mail.your_domain ``` If your IP is on a blocklist, the rejection usually names it (for example `blocked using zen.spamhaus.org`). Follow the delisting link in the message after fixing the cause, such as an open relay or a compromised account. ### Relay access denied ```text NOQUEUE: reject: RCPT from unknown[198.51.100.50]: 554 5.7.1 : Relay access denied ``` A client tried to send to an external domain without authenticating, and its IP is not in `mynetworks`. This is Postfix working correctly. If the client is a legitimate application or mail client, configure it to authenticate with SMTP AUTH on the submission port (587) instead of adding its IP to `mynetworks`. ### Authentication failures ```text warning: unknown[198.51.100.50]: SASL LOGIN authentication failed: UGFzc3dvcmQ6 ``` Occasional failures from random IPs are password-guessing bots. A burst for one of your users means a wrong password in their client. If failures continue from the same IPs, a tool such as Fail2ban with its `postfix-sasl` jail can block them. ## Step 7 - Diagnosing incoming mail failures If external senders get bounces or their messages never show up in the log at all, the connection never reached Postfix. Check the MX record first: ```bash dig your_domain MX +short dig mail.your_domain A +short ``` ```output 10 mail.your_domain. 203.0.113.10 ``` The MX must point to a hostname (never directly to an IP) that resolves to your server. Next, make sure the firewall allows SMTP. Ubuntu's Postfix package ships a UFW application profile: ```bash sudo ufw allow Postfix sudo ufw status ``` From a different machine, confirm that port 25 answers with your server's banner: ```bash nc -v mail.your_domain 25 ``` ```output Connection to mail.your_domain (203.0.113.10) 25 port [tcp/smtp] succeeded! 220 mail.your_domain ESMTP Postfix (Ubuntu) ``` Type `QUIT` to close the session. If you have `swaks` installed on that machine, it runs a full SMTP transaction and shows each step: ```bash swaks --to user@your_domain --server mail.your_domain ``` If the connection works but the message is rejected, look for `NOQUEUE: reject` lines with the sender's address. Two frequent causes: - `550 5.1.1 : Recipient address rejected: User unknown` means the mailbox does not exist in the local or virtual mailbox maps. - `mail for your_domain loops back to myself` means the domain is missing from `mydestination` (for local users) or from your virtual domains, so Postfix tries to relay the mail to itself. ## Step 8 - Checking resources A full disk stops mail flow completely. Postfix then rejects new mail with a temporary error: ```text NOQUEUE: reject: MAIL from unknown[198.51.100.50]: 452 4.3.1 Insufficient system storage ``` Check free space and inodes on the partition that holds `/var/spool/postfix`, and the size of the queue itself: ```bash df -h /var/spool/postfix df -i /var/spool/postfix sudo du -sh /var/spool/postfix/deferred ``` A deferred queue with thousands of messages is usually outgoing spam. Find the top senders in the queue before deleting anything: ```bash sudo postqueue -j | grep -oP '"sender": *"\K[^"]*' | sort | uniq -c | sort -rn | head ``` ## Troubleshooting - **`fatal: open database /etc/postfix/sasl_passwd.db: No such file or directory`**: a lookup table was edited but not compiled. Run `sudo postmap /etc/postfix/sasl_passwd` (use the path from the error) and `sudo systemctl reload postfix`. - **Changes to `main.cf` have no effect**: run `sudo postfix check`, then `sudo systemctl reload postfix`, and confirm the value with `postconf parameter_name`. Use `postconf -n` to list only the settings you changed from the defaults. - **You need more detail for one remote host**: add `debug_peer_list = example.net` to `/etc/postfix/main.cf` and reload. Postfix logs the full SMTP conversation for that host. Remove the setting when you are done, because it is very verbose. ## Conclusion You traced messages by queue ID, grouped failures by their SMTP reply, managed the queue, and checked the network, DNS and resource issues behind most delivery problems. The reply text in parentheses on each `status=` line is almost always the key to the fix. As next steps, set up SPF, DKIM and DMARC if any are missing, and schedule a daily `pflogsumm` report so you notice rising deferrals before users do.