Infrastructure8 min read

VPS email: six things to do so your mail actually reaches Inbox

A $5 VPS from Hetzner, DigitalOcean, OVH or Contabo can hit 90%+ Inbox if you do the setup right. Skip any one of these six steps and Gmail will treat you like a script kiddie.

The internet is full of "don't self-host email, just use SES" advice. For production transactional volume at scale, that's correct. For a personal domain, a small SaaS doing hundreds of mails/day, or a homelab you actually want to trust — self-hosting on a VPS works fine. You just have to do it right.

"Right" means six things: choose a clean IP, publish correct DNS, authenticate properly, warm up the IP, rate-limit yourself, and measure placement. Skip any one and you end up in Spam.

1. Pick a VPS provider with a clean mail IP

The cheap cloud providers have mixed reputations. Before you commit:

  • Do an IP reputation check on the IP you were assigned. Usedig +short -x <ip>, Spamhaus, UCEPROTECT, Barracuda, Microsoft SNDS.
  • Some providers block port 25 outbound by default: DigitalOcean, Google Cloud, AWS EC2, Azure. Port 25 block = you can't send direct to MX; you must relay.
  • Good providers for outbound mail: Hetzner, OVH, Contabo, Vultr (with request), Linode (with ticket). Port 25 open after a short verification.
# Quick IP reputation check before you start
$ dig +short -x 203.0.113.10
# Should return something. If empty, no PTR set - you'll need to set one.

# Check common blacklists (or use a web tool)
$ for zone in zen.spamhaus.org bl.spamcop.net b.barracudacentral.org; do
>   result=$(dig +short $(echo 203.0.113.10 | awk -F. '{print $4"."$3"."$2"."$1}').$zone)
>   [ -z "$result" ] && echo "$zone: clean" || echo "$zone: LISTED ($result)"
> done

# If listed on delivery: request removal via the list's form BEFORE
# sending any mail. Some lists require proof of ownership.

2. Hostname and PTR match

Your VPS must announce a HELO hostname that matches its forward and reverse DNS. This is FCrDNS and it's the single biggest signal Gmail uses to decide if you're a legitimate mail server.

# On the VPS
$ hostnamectl set-hostname mail.yourdomain.com
$ echo '127.0.1.1 mail.yourdomain.com mail' >> /etc/hosts
$ systemctl restart postfix

# Forward A record (at your DNS host)
mail.yourdomain.com.    IN A    203.0.113.10

# Reverse PTR (at your VPS provider's control panel)
# 203.0.113.10  ->  mail.yourdomain.com

# Verify both match (FCrDNS)
$ dig +short mail.yourdomain.com      # -> 203.0.113.10
$ dig +short -x 203.0.113.10          # -> mail.yourdomain.com.

If the reverse returns the VPS provider's generic hostname (static.203.0.113.10.ovh.net), change it at the provider's panel to your mail hostname. This is the #1 fixable issue on self-hosted VPS mail.

3. SPF, DKIM, DMARC

Authentication is non-negotiable. All three records, correctly aligned:

; SPF - lock to your server IP
yourdomain.com.        IN TXT "v=spf1 ip4:203.0.113.10 -all"

; DKIM - generate 2048-bit key
; $ opendkim-genkey -d yourdomain.com -s mail -b 2048
mail._domainkey.yourdomain.com. IN TXT
  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."

; DMARC - start p=none with reports
_dmarc.yourdomain.com. IN TXT
  "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s; pct=100"

; After 2 weeks of clean reports, tighten:
; "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com; ..."
; After another 2 weeks:
; "v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; ..."
The alignment test

Send a mail to check-auth@verifier.port25.com and read the reply. It shows you SPF, DKIM, DMARC and SpamAssassin from one place. If DMARC alignment shows fail, you've got a From domain that doesn't match your DKIM signing domain — fix that before sending anywhere else.

4. Minimal Postfix + OpenDKIM config

# /etc/postfix/main.cf  - minimal outbound-only config
myhostname = mail.yourdomain.com
mydomain = yourdomain.com
myorigin = $mydomain
inet_interfaces = all
inet_protocols = ipv4

mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8 [::1]/128

smtpd_tls_cert_file = /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem
smtpd_tls_key_file  = /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem
smtpd_tls_security_level = may
smtp_tls_security_level = may

# OpenDKIM milter
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891
milter_default_action = accept

# Sanity restrictions
smtpd_helo_required = yes
smtpd_helo_restrictions =
  permit_mynetworks
  permit_sasl_authenticated
  reject_invalid_helo_hostname

smtpd_relay_restrictions =
  permit_mynetworks
  permit_sasl_authenticated
  reject_unauth_destination

# Hide version
smtpd_banner = $myhostname ESMTP
disable_vrfy_command = yes

5. Warm up the IP

A brand-new IP has zero sending reputation. Gmail and Microsoft apply extra scrutiny for the first 2-4 weeks. Ramp volume gradually:

  1. Week 1: 50 mails/day total.
  2. Week 2: 200/day.
  3. Week 3: 500/day.
  4. Week 4: 1500/day.
  5. Week 5+: scale as engagement allows.

Prioritize engaged recipients: your team, existing customers who opened mail recently, double-opt-in subscribers. Do not blast 1000 cold addresses on day one — that's how you earn a Spamhaus listing before your coffee's cold.

Rate-limit outbound at Postfix

Add smtp_destination_rate_delay = 2s anddefault_destination_concurrency_limit = 5 tomain.cf. Gmail treats burst senders worse than steady ones. A 2-second pacer and 5 concurrent connections per destination domain looks human.

6. Feedback loops and postmaster tools

Once mail is flowing, instrument it:

  • Google Postmaster Tools: verify yourdomain.com, watch IP reputation, domain reputation, authentication pass rates, spam rate. Data appears after you cross a daily volume threshold (~500 mails/day).
  • Microsoft SNDS / JMRP: for Outlook/Hotmail, sign up atsendersupport.olc.protection.outlook.com. Get IP reputation + feedback loop for complaints.
  • DMARC aggregate reports: your rua address gets daily XML reports from every major provider. Parse with a tool like DMARC Analyzer or self-host Parsedmarc.
  • Seed-mailbox placement tests: weekly, against 20+ addresses across providers. Catches drift before real users complain.

The VPS mail checklist

  1. IP clean on Spamhaus, UCEPROTECT, Barracuda at provisioning time.
  2. Port 25 outbound open at the provider.
  3. Hostname, forward A, PTR all match: FCrDNS green.
  4. SPF, DKIM (2048-bit), DMARC published and aligned.
  5. Postfix + OpenDKIM wired with TLS and rate-limiting.
  6. IP warmup plan for 4 weeks.
  7. Postmaster Tools / SNDS / DMARC reporting on.
  8. Seed-placement test baseline and weekly monitoring.

When not to self-host on a VPS

Self-hosting is fine for low-to-mid volume (up to a few thousand mails/day) with stable, engaged audiences. It's a bad idea for:

  • Cold-outbound sales sequences — you'll burn the IP fast.
  • High-stakes transactional mail at scale — use Postmark, SES, Mailgun.
  • Teams without a sysadmin who can respond to a Spamhaus listing within 24 hours.
  • Compliance-heavy industries — providers have DPAs and certifications.

Frequently asked questions

Can I host mail on AWS Lightsail or EC2?

Outbound port 25 is blocked on AWS. You can send via SES, which is the recommended path. Trying to send direct-to-MX from EC2 requires a support ticket that is usually denied. Use Hetzner, OVH, Contabo, or Vultr instead for self-hosted direct-send.

What about IPv6?

Gmail and Microsoft prefer IPv6 when your host announces AAAA. You need PTR6 for that IPv6 address as well as PTR for IPv4. If your provider doesn't support PTR6, set inet_protocols = ipv4 in Postfix to force IPv4.

Should I use a mail-in-a-box distribution instead?

Mail-in-a-Box, Mailcow, Poste.io ship a full stack preconfigured. Great if you want a managed experience with config-as-documented. You still need to handle PTR, warmup, and placement testing — those can't be auto-configured because they live outside the VPS.

How often do I retest placement?

Weekly for production VPS mail. Daily during the first month of warmup. After any Postfix/OpenDKIM/DNS change. Before and after a DMARC policy tightening (none → quarantine → reject).
Related reading

Check your deliverability across 20+ providers

Gmail, Outlook, Yahoo, Mail.ru, Yandex, GMX, ProtonMail and more. Real inbox screenshots, SPF/DKIM/DMARC, spam engine verdicts. Free, no signup.

Run Free Test →

Unlimited tests · 20+ seed mailboxes · Live results · No account required