
Hours disappear fast when you “fix email” by flipping random toggles. A better approach is to trace one message end-to-end: headers → queue ID → logs → the remote server’s exact response. This email log troubleshooting tutorial shows a repeatable workflow you can run on a VPS or dedicated server. Use it to pinpoint bounces, delays, and authentication failures without destabilizing the rest of your stack.
What you’ll troubleshoot (and what you won’t)
This guide focuses on root-cause analysis using message headers and mail logs. The goal isn’t to “make the queue empty.”
The goal is to show, line-by-line, why a specific message failed and what to change next.
- You will do: extract queue IDs, follow a message across services, interpret SMTP status codes, confirm TLS/auth/hostname problems, and verify your fix with a clean retest.
- You won’t do: mass queue deletion, bulk retries, or risky “turn off checks” changes.
Prerequisites and a safe starting checklist
You’ll need SSH access with sudo/root. Examples target Ubuntu 24.04/24.10 and Debian 12/13. The same log-reading approach also works on AlmaLinux/Rocky.
- Know which MTA you run: Postfix (common on VPS) or Exim (common with cPanel/WHM).
- Have one failing email’s timestamp, sender, recipient, and ideally the full message headers.
- Don’t change anything yet. Capture evidence first: headers + logs.
Step 1: Identify your mail stack (Postfix vs Exim) and log locations
Confirm what actually handles outbound SMTP on the server. App “email settings” won’t matter if the wrong service is listening.
# What is listening on SMTP ports?
sudo ss -ltnp | egrep ':25|:465|:587'
# Check common MTAs
postconf -n 2>/dev/null | head -n 5
exim -bV 2>/dev/null | head -n 5
Log locations depend on your distro and MTA:
- Postfix on Ubuntu/Debian:
/var/log/mail.log(and rotated/var/log/mail.log.1,.gz) - Exim (often cPanel):
/var/log/exim_mainlog,/var/log/exim_rejectlog - systemd journal: some builds log to journald; you can still use
journalctl
# Journal-based view (works even if files exist)
sudo journalctl -u postfix --since "2 hours ago" --no-pager | tail -n 60
sudo journalctl -u exim4 --since "2 hours ago" --no-pager | tail -n 60
Step 2: Pull the queue ID from the email headers
Many providers include a queue ID in the Received: chain or in the bounce text. Ask the recipient for the full original headers. Get copy/paste text, not a screenshot.
Common patterns:
- Postfix queue ID:
ABC123DEF45(10–12 chars is common, can vary) - Exim message ID:
1sQv2A-0004nP-9x
If you only have a bounce message, search the bounce body for “queue id”, “message-id”, “X-Postfix-Queue-ID”, or the server’s “Reporting-MTA”.
Step 3: If you don’t have headers, find the queue ID from server logs
Sometimes all you get is “email didn’t send,” plus a sender, a recipient, and an approximate time window. That’s still enough to find the queue ID in logs.
Postfix example (Ubuntu/Debian):
# Search by recipient address
sudo grep -R "to=<user@example.com>" /var/log/mail.log* | tail -n 30
# Search by sender address
sudo grep -R "from=<sales@yourdomain.com>" /var/log/mail.log* | tail -n 30
Look for the line that contains the queue ID. It’s usually the first token on the line.
Exim example:
sudo grep -R "=> user@example.com" /var/log/exim_mainlog* | tail -n 30
sudo grep -R "<= sales@yourdomain.com" /var/log/exim_mainlog* | tail -n 30
Step 4: Trace one message end-to-end using the queue ID
Once you have the ID, stop searching by email address. Trace only by that ID. This prevents mixing multiple messages sent close together.
Postfix trace:
QID="ABC123DEF45"
sudo grep -R "$QID" /var/log/mail.log* | sed -e 's/\x1b\[[0-9;]*m//g'
Exim trace:
MID="1sQv2A-0004nP-9x"
sudo grep -R "$MID" /var/log/exim_mainlog* | tail -n 200
As you read the trace, label the stage:
- Accepted (submission happened)
- Queued (message stored)
- Routed (DNS lookup / transport selection)
- Delivered (remote accepted) or Deferred/Rejected (remote refused)
Step 5: Understand the remote server’s SMTP response codes
The most valuable log detail is the remote SMTP response. It’s the other server telling you exactly what it rejected.
- 2xx success (e.g.,
250 2.0.0 OK) - 4xx temporary failure (retryable), often rate limiting or greylisting
- 5xx permanent failure (bounce), often authentication, policy, or reputation
Examples you’ll commonly see in 2026:
450 4.7.1temporary rate limit; sending behavior or IP reputation issue451 4.4.2timeout; network/firewall/MTU/DNS issues550 5.7.1rejected; policy fail (SPF/DKIM/DMARC alignment, PTR/HELO mismatch, or blocked IP)554 5.7.1transaction failed; often content or reputation
Step 6: Diagnose DNS and hostname failures from logs (FQDN, HELO/EHLO, rDNS)
Many “mysterious” rejections are identity problems. Your server introduces itself with a name. The recipient then checks whether that identity matches DNS.
Verify your system hostname and FQDN:
hostname
hostname -f
cat /etc/hostname
getent hosts $(hostname -f)
Then verify reverse DNS (PTR) for your outbound IP:
# Replace with your server IP
IP="203.0.113.10"
dig +short -x "$IP"
# Check forward confirms the same host
PTR=$(dig +short -x "$IP" | head -n 1)
dig +short "$PTR"
If you need to fix a mismatched banner/HELO, follow your stack-specific guide. For VPS mail identity issues, this HostMyCode tutorial is a good companion: email server hostname setup tutorial.
For reverse DNS setup and verification, use: PTR record setup tutorial.
Step 7: Prove whether SPF/DKIM/DMARC alignment caused the failure
Logs may hint at authentication trouble. Stronger evidence comes from the bounce plus the recipient’s Authentication-Results header.
On your side, confirm the DNS records for the sending domain:
DOMAIN="yourdomain.com"
# SPF
dig +short TXT "$DOMAIN" | tr '"' ' ' | sed 's/ */ /g'
# DMARC
dig +short TXT "_dmarc.$DOMAIN" | tr '"' ' '
# DKIM (selector varies; common: default, mail, s1)
SELECTOR="default"
dig +short TXT "$SELECTOR._domainkey.$DOMAIN" | head -n 2
If you see rejections like 550 5.7.26 (common with strict auth enforcement) or wording that mentions “DMARC policy,” don’t guess.
Verify these three alignment points:
- The envelope-from domain matches your SPF-authorized sending IP.
- The DKIM d= domain matches the visible From domain (alignment).
- Your DMARC policy isn’t set to reject while DKIM signing is failing.
If you’re building your mail posture from scratch on a VPS, this walkthrough is practical and current: VPS email setup tutorial.
Step 8: Check for local submission problems (587/465 auth, SASL, app misconfig)
Sometimes the recipient is fine and the message never leaves your server. In that case, the failure is on submission.
Common causes are bad credentials, the wrong port, or TLS requirements the client can’t meet.
In Postfix logs, look for SASL failures and the client IP making the attempt:
# Postfix SASL auth failures (common patterns)
sudo grep -E "sasl_username=|SASL|authentication failed" /var/log/mail.log | tail -n 80
Fixes that come up constantly in hosting environments:
- Use port 587 with STARTTLS for authenticated submission.
- Use the correct SMTP hostname (and match your certificate name if you enforce TLS verification).
- Correct credentials and avoid “shared test accounts” used by multiple apps.
Step 9: Spot TLS negotiation issues from the log trace
TLS errors usually fall into a few buckets: protocol mismatch, certificate name mismatch, or an incomplete chain. Once you know the bucket, the fix is typically straightforward.
Quick outbound TLS test to a popular provider (replace domain):
openssl s_client -starttls smtp -connect gmail-smtp-in.l.google.com:25 -servername gmail-smtp-in.l.google.com -brief
For inbound submission (your users/app connecting to your server), test your hostname:
MAILHOST="mail.yourdomain.com"
openssl s_client -starttls smtp -connect "$MAILHOST":587 -servername "$MAILHOST" -brief
If you’re running Nginx/Apache for sites on the same VPS, keep your HTTPS posture modern too. This tutorial covers safe TLS hardening patterns that won’t break older clients: TLS hardening tutorial.
Step 10: Confirm basic network and firewall conditions (without locking yourself out)
Some “mail problems” are plain networking issues: blocked port 25, a broken resolver, or a firewall rule that silently stops SMTP. Check connectivity before you change mail configs.
# DNS resolution must work
resolvectl status 2>/dev/null | sed -n '1,120p'
dig +time=2 +tries=1 mx gmail.com
# Can you reach remote port 25?
# (nc is in netcat-openbsd on Debian/Ubuntu)
sudo apt-get update && sudo apt-get install -y netcat-openbsd
nc -vz -w 3 gmail-smtp-in.l.google.com 25
If you run CSF/LFD in WHM, review allowed outbound ports. Avoid accidental mail blocks.
If you need a hosting-safe baseline, see cPanel firewall setup guide.
Step 11: Reproduce the failure with a controlled test message
After you think you’ve found the cause, send a test message you can trace cleanly. Keep it boring: plain text, one recipient, and a subject you can grep.
Postfix/sendmail interface:
TO="you@external-test-domain.com"
SUBJ="trace-test-$(date +%F-%H%M)"
{
echo "Subject: $SUBJ"
echo "From: postmaster@yourdomain.com"
echo "To: $TO"
echo
echo "Test message for log tracing."
} | /usr/sbin/sendmail -t
Then immediately search logs for the subject or the new queue ID.
sudo grep -R "trace-test" /var/log/mail.log* | tail -n 50
Step 12: Fix patterns you’ll see repeatedly (quick playbook)
Use this as your “what to do next” map once you’ve found the remote response line.
- Remote says “PTR missing” / “HELO mismatch”: set correct FQDN + PTR. Validate forward-confirmed rDNS.
- Remote says SPF fail: update SPF TXT to authorize your VPS IP or relay. Re-test DNS propagation.
- Remote says DKIM fail: confirm selector TXT exists, key not truncated, and signing is enabled in the MTA/control panel.
- Remote says DMARC reject: temporarily soften policy while you fix alignment; then tighten again.
- Deferred 4xx for hours: look for rate limiting and sending patterns (bursts, too many recipients per hour). Consider a relay for transactional mail.
- Timeouts: confirm outbound port 25 is allowed, check firewall and resolver, and verify you’re not hitting upstream blocks.
If DNS changes are part of your fix, make them deliberately. Keep a rollback ready.
This is the checklist you want when you’re changing records under pressure: DNS cutover checklist.
Step 13: Keep logs readable on busy hosting nodes
On a multi-site VPS, mail logs grow quickly. Bots hammer submission ports, and apps retry aggressively.
Log volume can spike without warning.
- Verify log rotation runs daily.
- Keep enough history to investigate incidents (often 7–14 days), but don’t hoard months on the root disk.
If you’ve hit “No space left on device,” fix that first. Disk pressure causes strange mail symptoms.
You’ll see deferred queues, failed writes, and half-completed deliveries. Use this guide: VPS disk space troubleshooting.
Where hosting choices matter (VPS vs managed VPS)
Email troubleshooting is easier on a stable server. You want consistent reverse DNS, predictable firewall rules, and services that stay up.
If you run business email alongside websites, those basics decide whether you debug once a quarter or every week.
If you want root access but also want a second set of eyes on mail identity, firewall rules, and service health, consider managed VPS hosting. If you prefer full control of the stack, a HostMyCode VPS gives you a clean baseline and predictable mail logs.
Summary: your repeatable tracing workflow
- Get one message’s headers or bounce details.
- Extract the queue/message ID.
- Trace by ID through logs to the remote SMTP response.
- Classify the failure: identity (FQDN/PTR), auth (SPF/DKIM/DMARC), TLS, submission, or network.
- Apply one change, send one controlled test, and verify the new trace.
If you’re planning a move or rebuilding your stack, start with infrastructure that makes mail identity clean from day one. A HostMyCode VPS is a solid base, and managed VPS hosting helps when you want faster incident resolution without guesswork.
If you host mail and websites on the same server, small mistakes (hostname, rDNS, firewall rules) quickly turn into repeat support tickets. HostMyCode can help you keep mail delivery traceable and predictable on a HostMyCode VPS, or maintain it for you with managed VPS hosting so you spend less time chasing unclear bounces.
FAQ
What’s the fastest way to find the right log line for a single email?
Get the queue/message ID (from headers or a grep by sender/recipient). Then grep logs by that ID. This avoids false matches.
My logs show “deferred” but no clear error. What should I check?
Search earlier lines for the same queue ID. Look for a remote response there. If none exists, verify DNS resolution and outbound port 25 connectivity.
Do I need to enable verbose logging in Postfix/Exim?
Usually no. Standard logs already include queue IDs and remote SMTP responses. Increase verbosity only temporarily. If you do, rotate logs aggressively.
How do I tell if the problem is my server or the recipient’s server?
If the remote SMTP response is a clear policy reject (5xx) naming SPF/DKIM/DMARC/PTR, it’s on your side. If it’s 4xx rate limiting, it can be either side. You can still improve acceptance by tightening identity and adjusting sending patterns.
Should I run email on the same VPS as WordPress sites?
You can, but you need strong identity (FQDN/PTR), timely patches, and stable disk space. If email is business-critical, consider separating roles. Managed VPS support can also speed up troubleshooting.