Testing and Verifying a Mail Server
September 6, 2026
After building a mail server across multiple posts — Postfix, Dovecot, DKIM/SPF/DMARC, Rspamd, ClamAV, PostfixAdmin, and Roundcube — the components need end-to-end verification. This post covers testing every layer of the stack from TLS connectivity through to webmail access and email authentication.
Seeding Test Data
Before testing, create a test domain and mailbox through PostfixAdmin or the CLI:
php /var/www/mailadmin.example.com/htdocs/scripts/postfixadmin-cli.php domain add example.com
php /var/www/mailadmin.example.com/htdocs/scripts/postfixadmin-cli.php mailbox add mailtest@example.com \
--password "test-password" --password2 "test-password" --name "Mail Test" --active 1
Verify the mailbox exists in the database:
mysql mailserver -e "SELECT username, domain, maildir FROM mailbox WHERE username = 'mailtest@example.com';"
Testing SMTP Connectivity
Port 25 — Incoming Mail
telnet localhost 25
You should see the Postfix banner:
220 mail.example.com ESMTP
Type EHLO test and verify TLS and the milter are advertised:
250-STARTTLS
Type QUIT to disconnect.
Port 587 — Submission (Authenticated)
Test TLS on the submission port:
openssl s_client -connect localhost:587 -starttls smtp
After the TLS handshake, verify the certificate subject matches mail.example.com and the connection shows 250-AUTH PLAIN LOGIN.
Port 465 — SMTPS (Implicit TLS)
openssl s_client -connect mail.example.com:465
The TLS handshake should complete immediately (no STARTTLS needed). Verify the Postfix greeting appears after the encrypted connection is established.
Testing IMAP Connectivity
Port 993 — IMAPS
openssl s_client -connect localhost:993
After the TLS handshake, you should see the Dovecot banner:
* OK [CAPABILITY IMAP4rev1 ...] Dovecot ready.
Test Authentication
doveadm auth test mailtest@example.com test-password
This tests the full chain — Dovecot queries MySQL, verifies the password hash, and returns success or failure. A successful result looks like:
passdb: mailtest@example.com auth succeeded
extra fields:
user=mailtest@example.com
Test IMAP Login
openssl s_client -connect localhost:993 -quiet
After connecting, type:
a LOGIN mailtest@example.com test-password
a LIST "" "*"
a LOGOUT
This authenticates, lists all mailbox folders, and disconnects. A new mailbox should show at least INBOX.
Testing Mail Delivery
Local Delivery via LMTP
Send a test message through Postfix and verify Dovecot delivers it:
echo "Test message body" | mail -s "Delivery test" mailtest@example.com
Check the mail log for delivery confirmation:
tail -20 /var/log/mail.log
Look for status=sent with delivered to maildir. Verify the message arrived on disk:
ls /var/vmail/example.com/mailtest/Maildir/new/
A file should appear in the new/ directory.
Verify Sieve Filtering
Send a message that triggers the spam Sieve rule. If Rspamd is running, the easiest test is to send the GTUBE test string:
echo "XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X" | \
mail -s "Sieve test" mailtest@example.com
If the message was scored as spam and the Sieve script filed it:
ls /var/vmail/example.com/mailtest/Maildir/.Junk/new/
The GTUBE message should appear in the Junk folder.
Testing TLS Certificates
Verify Certificate for All Hostnames
Each hostname used in MX records, IMAP, and SMTP should be valid in the certificate's Subject Alternative Names:
openssl s_client -connect mail.example.com:993 -servername mail.example.com 2>/dev/null | \
openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
Verify all expected hostnames appear: mail.example.com, imap.example.com, smtp.example.com.
Check Certificate Expiry
openssl s_client -connect mail.example.com:993 -servername mail.example.com 2>/dev/null | \
openssl x509 -noout -dates
Verify notAfter is more than 30 days away. If using Let's Encrypt, the certificate renews automatically every 60-90 days.
Testing Email Authentication
Verify DKIM Signing
Send a message to an external address and inspect the headers. Or test locally:
opendkim-testkey -d example.com -s mail -vvv
This queries DNS for the DKIM public key and validates it matches the private key. A successful result shows key OK.
Verify SPF Record
dig +short TXT example.com | grep spf
The result should show your SPF record, e.g.:
"v=spf1 mx a:mail.example.com -all"
Verify DMARC Record
dig +short TXT _dmarc.example.com
Expected result:
"v=DMARC1; p=reject; rua=mailto:postmaster@example.com"
External Verification Services
Send a test message to check-auth@verifier.port25.com. The reply includes a report showing whether DKIM, SPF, and DMARC passed. Alternatively, send to a Gmail address and inspect the headers — Gmail shows authentication results in the Authentication-Results header.
Testing Spam Filtering
Verify Rspamd Is Processing Mail
curl -s http://127.0.0.1:11334/stat | python3 -m json.tool
This returns statistics including scanned (total messages processed), learned (Bayes training count), and actions (reject, greylist, add header).
Test GTUBE Detection
rspamc < /usr/share/rspamd/gtube.eml
The output should show a high spam score and GTUBE in the symbols list.
Verify Greylisting
Send a message from an unknown sender. Check the log for a greylisting entry:
grep "greylist" /var/log/mail.log | tail -5
The first attempt should be temporarily rejected. A second attempt after 5 minutes should succeed.
Testing Antivirus
Verify ClamAV Is Running
clamdscan /etc/hostname
Should return OK.
Test EICAR Detection via Rspamd
Rspamd's ClamAV integration only scans MIME attachments — not inline message body text (scan_text_mime = false by default). To trigger detection, send the EICAR string as a file attachment:
# Create the EICAR test file (exact 68-byte string)
python3 -c "print('X5O!P%@AP[4\\\\PZX54(P^)7CC)7}\$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!\$H+H*', end='')" > /tmp/eicar.txt
# Send as an attachment
echo "Virus attachment test" | mail -s "EICAR test" -a /tmp/eicar.txt mailtest@example.com
# Clean up
rm /tmp/eicar.txt
Check the Rspamd log for rejection:
grep "CLAM_VIRUS" /var/log/rspamd/rspamd.log | tail -5
The message should be rejected with the CLAM_VIRUS symbol. If you put the EICAR string in the message body instead of an attachment, ClamAV won't scan it — this is expected behavior, not a misconfiguration.
Testing Web Interfaces
Roundcube
Navigate to https://mail.example.com/ and verify:
- The login page loads
- Login succeeds with
mailtest@example.comcredentials - The inbox shows test messages sent earlier
- Composing and sending a test email works
- Settings > Filters shows the ManageSieve interface
curl -sk https://mail.example.com/ | grep -o 'rcmloginuser\|Roundcube Webmail'
This should return a match confirming the login page is served.
PostfixAdmin
Navigate to https://mailadmin.example.com/ and verify:
- The login page loads
- Superadmin login works
- The domain list shows
example.com - The mailbox list shows
mailtest@example.com
Rspamd Web Interface
Access the Rspamd dashboard (via SSH tunnel or reverse proxy):
curl -s http://127.0.0.1:11334/stat
The web interface at port 11334 provides real-time graphs of spam vs. ham ratios, top symbols, and throughput.
Service Health Checks
Verify all services are running and listening:
# Postfix
ss -lnt sport = :25
ss -lnt sport = :587
# Dovecot
ss -lnt sport = :993
ss -lnt sport = :4190
# Rspamd
ss -lnt sport = :11332
ss -lnt sport = :11334
# ClamAV
ls -la /var/run/clamav/clamd.sock
# MySQL
ss -lnt sport = :3306
# Redis
ss -lnt sport = :6379
# Apache
ss -lnt sport = :80
ss -lnt sport = :443
All should return results showing the respective services are bound.
Check Service Logs for Errors
tail -20 /var/log/mail.log
tail -20 /var/log/clamav/clamd.log
tail -20 /var/log/clamav/freshclam.log
tail -20 /var/log/roundcube/errors.log
No errors should appear during normal operation. Common post-setup issues include file permission problems, socket path mismatches, and database connection failures.
DNS Verification
Verify all DNS records resolve correctly from an external perspective:
# MX record
dig +short MX example.com
# A record for mail hostname
dig +short A mail.example.com
# PTR (reverse DNS) — must match the MX hostname
dig +short -x $(dig +short A mail.example.com)
# SPF
dig +short TXT example.com | grep spf
# DKIM
dig +short TXT mail._domainkey.example.com
# DMARC
dig +short TXT _dmarc.example.com
The PTR record is critical — many mail servers reject messages from IPs without a matching reverse DNS entry. Contact your hosting provider if the PTR record doesn't match your mail hostname.
End-to-End Test
The final verification is a round-trip test: send mail to an external provider and receive a reply.
- Log in to Roundcube at
https://mail.example.com/ - Send a test message to a Gmail, Outlook, or other external address
- Check the received message's headers for:
Authentication-Resultsshowingdkim=pass,spf=pass,dmarc=pass- No spam folder delivery
- Reply from the external address
- Verify the reply arrives in Roundcube's inbox
- Verify the reply is not in the Junk folder
If authentication passes and messages flow both directions, the mail server is fully operational.
Summary
A complete mail server verification covers:
- SMTP connectivity on ports 25, 587, and 465 with correct TLS
- IMAP access on port 993 with working authentication
- LMTP delivery from Postfix to Dovecot with Sieve filtering
- DKIM signing verified via DNS key lookup
- SPF and DMARC records correctly published
- Rspamd processing all incoming mail with spam scoring
- ClamAV scanning attachments with virus rejection
- Roundcube serving webmail with ManageSieve integration
- PostfixAdmin managing domains and mailboxes
- DNS records (MX, A, PTR, SPF, DKIM, DMARC) all resolving correctly
- End-to-end message flow to and from external providers
The mail server is now complete. For an overview of the entire build process, see Building a Complete Mail Server from Scratch.