A mail server can look healthy on the surface and still fail in ways users only notice later: messages stuck in the queue, mail rejected by Gmail, clients that cannot log in. In this tutorial you will test a Postfix and Dovecot server layer by layer, from DNS to SMTP, TLS, authentication, delivery and IMAP, so you can pinpoint exactly where a problem lives. The commands target Ubuntu 24.04 but work the same on Debian 12.
Prerequisites
To follow this guide you need:
- A mail server running Postfix and Dovecot on Ubuntu 24.04, for example a CubePath VPS, with a non-root user with
sudoprivileges. - A domain whose mail you host. This guide uses
example.comfor the domain,mail.example.comfor the server hostname and203.0.113.10for its public IP. Replace them with your own values. - A mailbox you can test with (
[email protected]) and an external mailbox at another provider, such as Gmail. - A second machine, like your workstation, to run tests from outside the server.
Step 1 - Installing the testing tools
Most checks rely on dig, swaks (a scriptable SMTP client), openssl and nc. Install them on the server, and ideally on your workstation too:
sudo apt update
sudo apt install dnsutils swaks openssl netcat-openbsd
Confirm that swaks is available:
swaks --version
swaks version 20240103.0
Step 2 - Checking DNS records
Delivery problems very often start in DNS. Check each record from a public resolver so you see what the rest of the internet sees, not a local cache.
Check the MX record, which tells other servers where to deliver mail for your domain:
dig +short MX example.com @1.1.1.1
10 mail.example.com.
The MX target must be a hostname with an A (and optionally AAAA) record, never an IP address or a CNAME:
dig +short A mail.example.com @1.1.1.1
203.0.113.10
Check the reverse DNS (PTR) record of the server IP. Large providers reject or penalize mail from IPs whose PTR does not resolve back to the sending hostname:
dig +short -x 203.0.113.10
mail.example.com.
The PTR value should match Postfix's myhostname. Compare them:
postconf myhostname
myhostname = mail.example.com
Next, check the SPF, DMARC and DKIM TXT records. Replace default with the DKIM selector you configured:
dig +short TXT example.com @1.1.1.1
dig +short TXT _dmarc.example.com @1.1.1.1
dig +short TXT default._domainkey.example.com @1.1.1.1
"v=spf1 mx -all"
"v=DMARC1; p=quarantine; rua=mailto:[email protected]"
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
A domain must have exactly one SPF record. Two v=spf1 records make SPF fail with a permerror. If you use OpenDKIM, it can also check that the published key matches the private key on disk (the tool is in the opendkim-tools package):
sudo opendkim-testkey -d example.com -s default -vvv
opendkim-testkey: key OK
key not secure in that output only means the zone is not DNSSEC-signed and is not an error.
Step 3 - Testing SMTP connectivity
Confirm that Postfix and Dovecot listen on the expected ports:
sudo ss -tlnp | grep -E ':(25|465|587|143|993)\s'
LISTEN 0 100 0.0.0.0:25 0.0.0.0:* users:(("master",pid=1234,fd=13))
LISTEN 0 100 0.0.0.0:587 0.0.0.0:* users:(("master",pid=1234,fd=17))
LISTEN 0 100 0.0.0.0:465 0.0.0.0:* users:(("master",pid=1234,fd=21))
LISTEN 0 100 0.0.0.0:993 0.0.0.0:* users:(("dovecot",pid=987,fd=38))
Port 25 receives mail from other servers, 587 (STARTTLS) and 465 (implicit TLS) are for your users' mail clients, and 993 is IMAP over TLS. If a port is missing, check /etc/postfix/master.cf for the submission and submissions services, and the protocols setting in Dovecot.
Listening locally is not enough: the firewall must allow the traffic too. With UFW:
sudo ufw status
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
25,465,587/tcp ALLOW Anywhere
993/tcp ALLOW Anywhere
Now test from your workstation, outside the server. A successful connection proves that both the firewall and the network path are open:
nc -zv mail.example.com 25
nc -zv mail.example.com 587
Connection to mail.example.com (203.0.113.10) 25 port [tcp/smtp] succeeded!
Connection to mail.example.com (203.0.113.10) 587 port [tcp/submission] succeeded!
NoteMany residential ISPs block outbound port 25, so the port 25 test can fail from a home connection even if the server is fine. Use an online checker such as MXToolbox or run the test from another server.
Finally, check that the server itself can reach other mail servers on port 25. Without this, outgoing mail stays in the queue:
nc -zv -w 5 gmail-smtp-in.l.google.com 25
If this times out, outbound SMTP is blocked by your provider or by an egress firewall rule. Many hosting providers block port 25 on new accounts; open a support request with your provider to have it enabled.
Step 4 - Checking TLS certificates
Mail clients refuse to connect, and other servers may fall back to plain text, when the certificate is expired or does not match the hostname. Retrieve the certificate Postfix offers on the submission port and print its subject and validity dates:
openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
subject=CN = mail.example.com
issuer=C = US, O = Let's Encrypt, CN = R12
notBefore=Aug 30 08:14:02 2026 GMT
notAfter=Nov 28 08:14:01 2026 GMT
Repeat the check for the implicit TLS ports, which do not need -starttls:
openssl s_client -connect mail.example.com:465 -servername mail.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
openssl s_client -connect mail.example.com:993 -servername mail.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
To confirm the full chain validates, look for the verification result:
openssl s_client -connect mail.example.com:993 -servername mail.example.com </dev/null 2>/dev/null | grep 'Verify return code'
Verify return code: 0 (ok)
Any other code, such as 21 (unable to verify the first certificate), usually means the configuration points to the leaf certificate instead of the full chain. With Let's Encrypt, use fullchain.pem in both smtpd_tls_cert_file (Postfix) and ssl_cert (Dovecot).
Step 5 - Testing SMTP authentication
Postfix delegates authentication to Dovecot through a socket. Test the layers from the bottom up. First, ask Dovecot directly whether the credentials are valid:
sudo doveadm auth test [email protected]
Enter the password when prompted:
passdb: [email protected] auth succeeded
extra fields:
[email protected]
If this fails, the problem is in Dovecot's user database, not in Postfix. If it succeeds, check that the socket Postfix uses exists:
sudo ls -l /var/spool/postfix/private/auth
srw-rw---- 1 postfix postfix 0 Sep 25 09:00 /var/spool/postfix/private/auth
Then test an authenticated submission exactly like a mail client would, over STARTTLS on port 587. Run this from your workstation, with a real recipient at an external provider:
swaks --server mail.example.com --port 587 --tls \
--auth LOGIN --auth-user [email protected] \
--from [email protected] --to [email protected] \
--header "Subject: swaks submission test"
swaks prompts for the password and prints the whole SMTP conversation. The lines that matter are:
<~ 235 2.7.0 Authentication successful
~> MAIL FROM:<[email protected]>
<~ 250 2.1.0 Ok
~> RCPT TO:<[email protected]>
<~ 250 2.1.5 Ok
<~ 250 2.0.0 Ok: queued as 4C7D21A0F3
A 535 5.7.8 Authentication failed here, with doveadm auth test succeeding, points to the Postfix side: confirm that the submission service enables SASL:
sudo postconf -P 'submission/inet/smtpd_sasl_auth_enable' 'submission/inet/smtpd_tls_security_level'
submission/inet/smtpd_sasl_auth_enable = yes
submission/inet/smtpd_tls_security_level = encrypt
If no output appears, those options are not set on the submission service in /etc/postfix/master.cf, and the main settings in main.cf apply instead.
Step 6 - Testing inbound delivery
Send a message to your own domain from outside, without authentication, the way another mail server would. Run this from your workstation or a different server:
swaks --server mail.example.com --port 25 \
--from [email protected] --to [email protected] \
--header "Subject: swaks inbound test"
<~ 250 2.0.0 Ok: queued as 7E2B41A0F9
Also confirm the server is not an open relay. An unauthenticated attempt to send to an external domain must be refused:
swaks --server mail.example.com --port 25 \
--from [email protected] --to [email protected]
<** 554 5.7.1 <[email protected]>: Relay access denied
If this message is accepted, fix smtpd_relay_restrictions immediately; spammers find open relays within hours.
Step 7 - Inspecting and managing the mail queue
Messages that cannot be delivered right away wait in the queue. List it:
sudo postqueue -p
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
9A1F2B3C4D* 1523 Fri Sep 25 09:41:12 [email protected]
[email protected]
-- 1 Kbytes in 1 Request.
An asterisk after the ID means the message is being delivered now; an exclamation mark means it is on hold. For deferred messages, Postfix shows the last error in parentheses under the sender, which is usually enough to identify the problem. To see a message's headers and content:
sudo postcat -q 9A1F2B3C4D | less
Once you have fixed the cause, ask Postfix to retry all deferred messages now instead of waiting for the next retry interval:
sudo postqueue -f
To delete a single message, or everything in the deferred queue (for example backscatter after a spam incident):
sudo postsuper -d 9A1F2B3C4D
sudo postsuper -d ALL deferred
Warning
postsuper -d ALLwithout a queue name deletes every queued message, including legitimate mail waiting to be delivered.
Step 8 - Testing IMAP access
Connect to Dovecot over TLS and log in manually. This isolates server problems from mail client configuration:
openssl s_client -connect mail.example.com:993 -servername mail.example.com -quiet
After the * OK [CAPABILITY ...] Dovecot ready. greeting, type these commands one at a time. Each starts with an arbitrary tag:
a1 LOGIN [email protected] your_password
a2 SELECT INBOX
a3 LOGOUT
a1 OK [CAPABILITY IMAP4rev1 ...] Logged in
* 3 EXISTS
a2 OK [READ-WRITE] Select completed (0.001 + 0.000 secs).
* BYE Logging out
* 3 EXISTS shows how many messages the inbox holds; it should include the test message from Step 6. The password stays in your terminal scrollback, so use a test mailbox or clear the terminal afterwards.
Step 9 - Checking deliverability and authentication results
Send a message from your server to an external mailbox you control, then open the raw headers. In Gmail, open the message and choose Show original. You should see:
SPF: PASS with IP 203.0.113.10
DKIM: 'PASS' with domain example.com
DMARC: 'PASS'
For a scored report, send a message to the address shown on mail-tester.com and open the result page. It checks SPF, DKIM, DMARC, reverse DNS, blocklists and content in one go.
Step 10 - Reading the logs
Every failure in the previous steps leaves a trace in the logs. On Ubuntu, Postfix and Dovecot log to /var/log/mail.log. Follow it while you repeat a failing test:
sudo tail -f /var/log/mail.log
To see everything Postfix logged about one message, search for its queue ID:
sudo grep 9A1F2B3C4D /var/log/mail.log
2026-09-25T09:41:12.318201+00:00 mail postfix/submission/smtpd[20114]: 9A1F2B3C4D: client=unknown[198.51.100.7], sasl_method=LOGIN, [email protected]
2026-09-25T09:41:12.402117+00:00 mail postfix/qmgr[1402]: 9A1F2B3C4D: from=<[email protected]>, size=1523, nrcpt=1 (queue active)
2026-09-25T09:41:13.771954+00:00 mail postfix/smtp[20120]: 9A1F2B3C4D: to=<[email protected]>, relay=gmail-smtp-in.l.google.com[142.250.27.26]:25, delay=1.5, delays=0.08/0.01/0.62/0.78, dsn=2.0.0, status=sent (250 2.0.0 OK)
2026-09-25T09:41:13.772512+00:00 mail postfix/qmgr[1402]: 9A1F2B3C4D: removed
status=sent means the receiving server accepted the message. For a full walkthrough of log formats and useful search patterns, see the email logs analysis guide.
Troubleshooting
Outgoing mail stays in the queue with Connection timed out: outbound port 25 is blocked. Confirm with the nc test in Step 3 and ask your provider to enable outbound SMTP.
Gmail or Outlook rejects mail with 550 5.7.1 or a message about PTR or authentication: check the PTR record, SPF and DKIM from Step 2. The PTR must resolve to your hostname and that hostname must resolve back to the same IP.
Incoming mail never arrives, but the queue is empty: the sending server cannot reach you. Check the MX record with dig, confirm port 25 is open from outside, and look in /var/log/mail.log for NOQUEUE: reject lines showing why a message was refused.
Clients get Authentication failed but doveadm auth test works: the client is connecting to port 25 instead of 587 or 465, or sends a different username format (for example user instead of [email protected]) than the one Dovecot expects.
Mail client warns about the certificate: the hostname in the client must match the certificate subject (for example mail.example.com, not example.com), and the server must present the full chain. Recheck Step 4.
Postfix refuses to start after a change: run sudo postfix check to validate the configuration and sudo journalctl -u postfix@- -n 50 to read the startup error.
Conclusion
You have tested every layer of the mail server: DNS, network reachability, TLS, SASL authentication, inbound and outbound delivery, the queue and IMAP access. Working through the layers in this order lets you narrow down a fault quickly instead of guessing.
As next steps, you can:
- Learn to read and search the mail logs in depth to track individual messages and spot abuse.
- Set up DMARC aggregate reports (
rua) to see who is sending mail on behalf of your domain. - Add Fail2ban jails for Postfix SASL and Dovecot to block password guessing.
