Back to tutorials
Tutorial

Email Deliverability Troubleshooting Tutorial (2026): Fix SPF, DKIM, rDNS, and SMTP Errors on a Hosting VPS

Email deliverability troubleshooting tutorial for VPS: fix bounces, SPF/DKIM, rDNS, TLS, and queue issues with practical commands.

By Anurag Singh
Updated on Sep 01, 2026
Category: Tutorial
Share article
Email Deliverability Troubleshooting Tutorial (2026): Fix SPF, DKIM, rDNS, and SMTP Errors on a Hosting VPS

Email can “work” and still fail in the only way that matters: messages never reach the inbox. This email deliverability troubleshooting tutorial gives you a repeatable process. You’ll track down bounces, missing authentication, rDNS mismatches, TLS issues, and mail queue backlogs on a hosting VPS.

These steps assume Ubuntu 24.04/25.04-class servers running Postfix. That’s common on VPS setups and many control panel stacks. DNS is assumed to be hosted at your registrar or a DNS provider.

If you don’t want to manage mail reputation on the same box as your websites, don’t. Start with clean IP space and predictable networking at the hosting layer.

HostMyCode VPS plans are a solid fit for running web plus transactional mail in a controlled setup.

What you’ll need before you touch anything

  • Server access: root or sudo on the VPS.
  • DNS access: ability to edit A/AAAA, MX, TXT (SPF, DKIM, DMARC), and ideally PTR/rDNS via your provider.
  • One test recipient: a mailbox on Gmail/Microsoft/Yahoo plus a business mailbox (your own domain).
  • Mail logs: Postfix logs (usually /var/log/mail.log on Ubuntu).

Email deliverability troubleshooting tutorial: follow this order (it saves hours)

Deliverability issues stack. If you fix them out of order, you often chase the wrong symptom.

Use this sequence to converge quickly:

  1. Identify the failure type (bounce vs. spam folder vs. delayed).
  2. Verify basic DNS: A/AAAA, MX, and rDNS/PTR alignment.
  3. Check SPF and DKIM are actually passing for the exact sending host.
  4. Confirm TLS + hostname/HELO sanity.
  5. Inspect the mail queue and rate/deferral patterns.
  6. Only then look at reputation and blocklists.

Step 1: Classify the problem (bounce, delay, or spam placement)

Start with evidence, not guesses. Bounces include a reason from the remote server. Delays show up in your queue. Spam placement requires headers from a delivered message.

Grab a bounce reason from Postfix logs

sudo grep -E "status=(bounced|deferred|sent)" /var/log/mail.log | tail -n 80

Look for patterns like:

  • 550 5.7.1 (policy reject, auth failure, or reputation)
  • 450 4.7.1 (temporary deferral; often reputation/rate)
  • 535 5.7.8 (SMTP auth issues when using a relay)
  • 451 4.4.2 (connection problems/timeouts)

Dump message headers when mail lands in spam

Send a test message to Gmail and open “Show original”. Focus on these signals:

  • SPF: PASS/FAIL and which IP was evaluated.
  • DKIM: PASS/FAIL and selector/domain used.
  • DMARC: PASS/FAIL plus alignment notes.
  • Received: hostname, HELO, and sending IP.

If your problem is “mail stuck”, jump to the queue section later. Still, don’t skip DNS.

Bad rDNS or a broken hostname often causes deferrals that look like “random delays”.

Step 2: Confirm forward DNS and reverse DNS match your sending identity

Receivers expect your sending IP to reverse-resolve (PTR) to a hostname. That hostname should forward-resolve back to the same IP.

In 2026, you won’t get consistent inbox placement without this.

Pick the hostname you will send as

Example: mail.example.com. Use it consistently for:

  • Server hostname (hostnamectl)
  • Postfix myhostname and often smtpd_banner
  • PTR/rDNS for the server IP

Verify A record and PTR/rDNS alignment

# Replace with your real values
HOST=mail.example.com
IP=203.0.113.10

# Forward lookup should return your sending IP
dig +short A $HOST

# Reverse lookup should return your hostname
dig +short -x $IP

# Optional: confirm forward-confirmed reverse DNS
python3 - <<'PY'
import socket
ip='203.0.113.10'
host=socket.gethostbyaddr(ip)[0]
ips=socket.gethostbyname_ex(host)[2]
print('PTR host:', host)
print('Forward IPs:', ips)
PY

If your PTR is wrong or blank, fix rDNS at the provider level. HostMyCode customers can set rDNS on supported plans; follow: rDNS setup tutorial.

Set the server hostname correctly

sudo hostnamectl set-hostname mail.example.com
hostnamectl

Then confirm /etc/hosts has a sensible mapping for mail. Avoid pointing your hostname at 127.0.1.1.

sudo nano /etc/hosts
# Example
127.0.0.1 localhost
203.0.113.10 mail.example.com mail

Step 3: SPF — fix “softfail” and “none” without breaking legitimate senders

SPF answers one narrow question: “Is this IP allowed to send for this domain?” It doesn’t encrypt mail. It also doesn’t stop spoofing by itself.

But missing SPF, or SPF that doesn’t reflect reality, gets you penalized fast.

Find your current SPF record

dig +short TXT example.com | tr '"' ' ' | sed 's/  */ /g'

Build an SPF record for a single VPS sender

If your domain only sends from one VPS IP:

v=spf1 ip4:203.0.113.10 -all

If you also send through a provider (newsletters, helpdesk, Google/Microsoft), you need to authorize them too. Example pattern:

v=spf1 ip4:203.0.113.10 include:_spf.google.com include:spf.protection.outlook.com -all

Pitfall: Don’t pile on includes without checking the count. SPF has a DNS-lookup limit (10).

Hit it and you’ll get permerror. That is usually worse than softfail.

Validate SPF from your workstation

DNS checks help, but the real test is receiver evaluation. Send a test email and confirm SPF PASS in the headers.

If you send through a relay, SPF must authorize the relay’s outbound IPs. Authorizing only your VPS IP won’t help.

If WordPress is your main sender, using a relay is often easier to operate. HostMyCode’s workflow is here: SMTP relay setup guide.

Step 4: DKIM — confirm signing works and selectors match DNS

DKIM fails often because it’s easy to “set up” but still not sign mail. Selector drift is another common cause.

The selector in DNS must match what your milter or control panel uses.

Check if outbound mail is being DKIM-signed

In a delivered message, look for a header like:

DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=default; ...

If there’s no DKIM-Signature at all, your DKIM service may be stopped. It may also be miswired into Postfix. Some setups only sign certain domains.

On a Postfix + OpenDKIM VPS: quick service checks

sudo systemctl status opendkim --no-pager
sudo ss -lntp | grep -E "opendkim|8891" || true
sudo grep -R "^Socket" /etc/opendkim.conf /etc/opendkim/opendkim.conf 2>/dev/null

If you need a full setup path for OpenDKIM integration, use: DKIM setup tutorial. This article stays in troubleshooting mode.

Verify the DKIM public key exists in DNS

Replace selector and domain with what appears in your DKIM-Signature header:

SEL=default
DOMAIN=example.com
dig +short TXT ${SEL}._domainkey.${DOMAIN}

Common failure modes:

  • TXT record split incorrectly across strings (some DNS UIs do this). Receivers can still read it, but formatting mistakes can break parsing.
  • Wrong selector: DNS has mail but mail signs with default.
  • Using a subdomain in d= (like d=mail.example.com) but publishing the key under example.com.

Step 5: DMARC — use it as a diagnostic tool first (not a hammer)

You were explicit that you don’t want a DMARC setup tutorial. Fair.

For troubleshooting, publish a low-risk policy first. That lets you see failures before you enforce anything.

For initial diagnostics, publish a DMARC record with reporting and a “do nothing” policy:

_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; ruf=mailto:dmarc-fail@example.com; fo=1; adkim=s; aspf=s"

Why strict alignment? It exposes quiet misalignment early.

Example: your mail is signed as d=example.com, but the visible From is billing.example.net. That may pass SPF, yet still fail DMARC alignment.

If you run many client domains (reseller/shared hosting), align DKIM and SPF per domain. Avoid a single shared From domain.

That pattern creates DMARC failures that feel “random” to users.

Step 6: HELO/EHLO, banners, and TLS — eliminate easy trust breakers

Receivers score the shape of your SMTP session. A HELO like localhost is a red flag. So is a TLS hostname mismatch.

These issues don’t always hard-bounce. More often, they push mail toward spam or trigger repeated deferrals.

Check your Postfix identity settings

sudo postconf -n | grep -E "^(myhostname|mydomain|myorigin|smtpd_banner|smtp_helo_name|mydestination)"

Recommended baseline:

  • myhostname = mail.example.com
  • smtpd_banner should not advertise unnecessary details
  • smtp_helo_name should be your hostname for outbound

Confirm TLS cert matches your mail hostname

If Postfix terminates TLS, the certificate CN/SAN should include mail.example.com. Quick checks:

sudo postconf -n | grep -E "smtpd_tls_cert_file|smtpd_tls_key_file"

# Test STARTTLS from outside (run from your laptop or another server)
openssl s_client -starttls smtp -connect mail.example.com:587 -servername mail.example.com -showcerts

If you see certificate mismatches or renewals failing, fix ACME first. Otherwise you may blame reputation for a broken TLS identity.

This guide focuses on diagnosis and repair: SSL renewal troubleshooting tutorial.

Step 7: Mail queue triage — find what’s stuck and why

A growing queue is usually a symptom. Common causes include bad DNS, blocked port 25, receiver throttling, or content that triggers filtering.

Fix the underlying issue first. The queue often drains on its own.

Inspect the queue and spot repeats

mailq
sudo postqueue -p

For a clearer view (group by recipient domain):

sudo postqueue -p | awk '
/^[A-F0-9]/ {id=$1}
/^[ ]+[^ ]+@/ {print $1, id}
' | sed 's/[<>]//g' | awk -F@ '{print $2}' | sort | uniq -c | sort -nr | head

Read the exact deferral reason for a queued message

Copy a queue ID and run:

sudo postcat -q QUEUE_ID | sed -n '1,120p'

Typical messages and fixes:

  • “Host not found”: broken MX or DNS outage.
  • “Connection timed out”: egress blocked (often port 25), firewall, or remote greylisting.
  • “450 4.7.1 Try again later”: rate limit/reputation; slow down and fix authentication/rDNS.

If you need a deeper, Postfix-specific workflow, this companion guide stays operational and website-safe: Mail Queue Troubleshooting Tutorial.

Step 8: Check that your firewall and provider allow SMTP ports (and that you didn’t block yourself)

Many “deliverability” investigations end with a simple cause. Outbound 25 is blocked, or inbound 587/465 isn’t allowed for submission.

Verify both sides.

Local firewall quick checks (UFW)

sudo ufw status verbose

Common required inbound ports (depending on your setup):

  • 25/tcp (SMTP) — if you accept server-to-server mail
  • 587/tcp (Submission) — for authenticated users/apps
  • 465/tcp (SMTPS) — optional legacy submission

If mail broke right after “hardening”, start by auditing rules. This guide walks the common mistakes: UFW firewall troubleshooting tutorial.

Provider-level blocks

Some networks restrict outbound port 25 by default to reduce abuse. If you can’t send direct SMTP to the internet, use an authenticated relay on 587.

Or request unblocking if your host supports it.

Step 9: Reputation and blocklists — confirm, then take the fastest safe action

Once DNS, SPF, DKIM, rDNS, and TLS are correct, reputation is easier to interpret. Don’t start here.

If you start with blocklists, you’ll usually get noisy results.

Signals that point to reputation issues

  • Deferrals like 4.7.1 across multiple large providers
  • Immediate rejections mentioning “policy”, “spam”, or “listed”
  • High bounce rate from invalid recipients (old lists)

Fast remediation checklist

  • Stop sending to unknown/old lists. Clean the list first.
  • Separate mail streams: use a relay for bulk and keep your VPS for transactional.
  • Fix the source: compromised WordPress forms, weak SMTP auth, or open relay misconfig.

If you suspect compromise, treat it like an incident. Scan, quarantine, rotate credentials, then re-enable mail.

On cPanel servers, use WHM tooling rather than guessing. This tutorial is directly relevant: cPanel malware scan tutorial.

Step 10: WordPress-specific causes (the ones that look like “email is broken”)

WordPress issues often show up as “the contact form isn’t sending” or “WooCommerce order emails are missing”. Sometimes that’s SMTP, but not always.

  • Wrong From address: Using no-reply@gmail.com as From guarantees DMARC trouble. Use no-reply@yourdomain.
  • PHP mail() surprises: It can bypass authenticated submission and produce inconsistent headers.
  • Plugin conflicts: multiple SMTP plugins fighting.

Preferred operational pattern in 2026: configure WordPress to send via SMTP submission (587).

Point it at your local Postfix (authenticated) or a trusted relay. You get consistent logs, predictable auth results, and fewer “silent” failures.

Quick diagnostics: copy/paste checklist (10 minutes)

  • DNS: dig A mail.example.com and dig -x IP match.
  • Hostname: hostnamectl shows mail.example.com.
  • SPF: one valid TXT starting with v=spf1, no permerror.
  • DKIM: outbound mail contains DKIM-Signature; selector TXT exists.
  • TLS: STARTTLS presents a cert for mail.example.com.
  • Queue: postqueue -p small and shrinking.
  • Logs: bounces show clear remote reason, not timeouts.

Common “fixed it” changes that actually make deliverability worse

  • Setting SPF to +all to “stop failures”. That invites spoofing and damages reputation.
  • Using a mismatched PTR (PTR points to a generic host, not your mail hostname).
  • Sending bulk newsletters from your web server IP. One bad campaign can poison the IP for transactional mail.
  • Rotating DKIM keys weekly without a plan. Rotate deliberately, not constantly.

Summary: a stable deliverability baseline for a hosting VPS

Get the identity chain correct end-to-end: forward DNS, PTR/rDNS, hostname/HELO, SPF, DKIM, and a valid TLS certificate.

Then watch your logs and keep the queue small. After that, reputation work becomes measurable instead of mysterious.

If you’re building a fresh server for websites plus reliable mail sending, pick infrastructure with clean networking. You also need enough control to keep identity consistent.

For hands-on control, start with a HostMyCode VPS. If you want fewer moving parts and guided upkeep, managed VPS hosting is often the calmer option for business mail and web workloads.

Running mail on the same box as production sites can work well, but only if your DNS, rDNS, and server identity are correct from day one. HostMyCode can provision a dependable VPS footprint for this workflow, and you can move into managed VPS hosting if you want help staying current on updates and core services. If you want full control, start with a HostMyCode VPS and use the checklists above to keep deliverability steady.

FAQ

Why does SPF pass but my emails still go to spam?

SPF only validates the sending IP. If DKIM is missing, rDNS is wrong, the HELO is generic, or the content triggers filters, messages can still land in spam. Check headers for DKIM and DMARC results next.

Do I need port 25 open to send mail from a VPS?

To deliver server-to-server directly, yes. If outbound 25 is restricted, you can still send through a relay using authenticated submission on port 587.

What’s the fastest way to confirm rDNS is hurting me?

If multiple recipients defer with policy-style messages and your dig -x PTR doesn’t match your mail hostname, fix rDNS first. Many providers treat missing PTR as an automatic trust penalty.

How can I tell if my server is signing DKIM at all?

Check a delivered message’s headers for DKIM-Signature. If it’s not present, your DKIM service isn’t integrated or it’s not configured for the sending domain.

Should I send WooCommerce emails using PHP mail()?

Avoid it. Use SMTP submission (587) to a local Postfix instance or a relay so you get consistent authentication, logging, and fewer “silent” failures.

Email Deliverability Troubleshooting Tutorial (2026): Fix SPF, DKIM, rDNS, and SMTP Errors on a Hosting VPS | HostMyCode