Email·8 min read·

Why Transactional Email From a VPS IP Dies

Transactional mail from a new VPS IP often never reaches the inbox. This post covers blocklists, missing PTR, port 25, and when an ESP is the only path.

NB

Netbay Engineering

Netbay Engineering

On this page

Password resets, invoice PDFs, and magic links are not marketing. They are the mail your product has to send. Teams still point nodemailer or Django at 127.0.0.1 port 25 on the same VPS that runs the app, then watch Gmail junk the message or return 550 5.7.1. The framework did not fail. Large receivers treat unknown datacenter IPs as untrusted, and a freshly issued VPS address has no sending history at all.

What receivers actually check

A receiving MX does not read your product copy. It scores the TCP session before it scores the body.

  • Reverse DNS for the connecting IP, and whether that PTR matches the HELO name and a matching A record (forward-confirmed reverse DNS).
  • Whether the IP sits on Spamhaus, Barracuda, SORBS, or a dozen smaller lists. Cloud ranges appear on policy lists even when you have never sent spam.
  • SPF on the envelope sender, DKIM on the signed headers, and DMARC alignment for the visible From domain.
  • Volume, complaint rate, and unknown-user rate if you already have any history on that IP or domain.

Fail two of those and the message is junk or rejected. Fail IP reputation and the authentication records never get a fair reading.

Datacenter IPs start in the hole

Consumer broadband IPs are not automatically trusted, but they are at least familiar to inbox providers. Datacenter ranges are the opposite. They host scanners, botnets, and throwaway bulk senders. Gmail, Outlook, and Yahoo apply a higher bar before they accept mail from those ranges. A Lucknow DC01 address on Intel Xeon Platinum hardware with High-Speed SSD storage and L3/L4 DDoS filtering is a solid application node. It is not, by itself, a mail node those inboxes have seen before.

Reused IPs are worse. The address assigned this morning may have been listed last month. That debt travels with the number, not with your customer account. Always check the IP the day you get it, before you send a single reset mail.

Why VPS transactional mail dies app VPS nodemailer:25 local MTA 250 queued remote MX IP + auth score junk or 550 local 250 is not inbox delivery PTR, HELO, SPF, DKIM, DMARC, and IP lists decide a new datacenter IP starts with no reputation

Port 25 is the other silent killer

Outbound TCP 25 is how SMTP reaches a public MX. Many networks block it to cut abuse. If this VPS cannot open 25 to a remote mail exchanger, local Postfix queues forever and your app still thinks sendMail succeeded. Test the path the day you provision the box, before you design around direct delivery.

bash
nc -vz gmail-smtp-in.l.google.com 25
dig -x 203.0.113.40 +short
host 203.0.113.40
IP=203.0.113.40
REV=$(python3 -c "print('.'.join(reversed('$IP'.split('.'))))")
dig +short $REV.zen.spamhaus.org A
dig +short $REV.bl.spamcop.net A

An empty dig answer is the only result you want from those list zones. Any A record means the IP is listed and Gmail will not give you the benefit of the doubt.

The application code is not the bottleneck

This is the pattern that dies quietly:

javascript
const nodemailer = require("nodemailer");
const transport = nodemailer.createTransport({
  host: "127.0.0.1",
  port: 25,
  secure: false,
  name: "app.example.com"
});
function sendReset(to, url) {
  return transport.sendMail({
    from: "noreply@example.com",
    to: to,
    subject: "Reset your password",
    text: "Open this link: " + url
  });
}

The SMTP conversation succeeds locally. The remote MX then rejects, greylists, or silently junks. Your logs show 250 because the local MTA accepted the message, not because a human read it. Until you watch bounce logs and a real inbox, you do not have delivery.

What actually works

Pick one of three honest designs.

  1. Send transactional mail through an ESP (Amazon SES, Mailgun, Postmark, or similar) over authenticated submission on port 587. Their IPs are warmed and monitored.
  2. Relay the VPS through that ESP as a smarthost so cron and existing app mail still work without teaching every process a new SDK.
  3. Send directly only if you control PTR, SPF, DKIM, DMARC, a dedicated IP, and a weeks-long warmup, and you accept that Gmail may still throttle you.

Direct send from a new VPS IP is the hardest of the three. Netbay does not sell a managed mailbox or outbound filter. You get a static IP in Lucknow and a Linux or Windows VPS you control. That is enough to run an MTA. It is not enough to skip reputation work.

Warmup is not folklore. Start at a few dozen messages a day to addresses that will open and not complain. Double slowly. A burst of password resets to unengaged users on day one looks like a harvested list. Inbox providers do not distinguish "our app launched" from "this IP just joined a botnet."

Takeaway

Transactional mail dies on VPS IPs because receivers score the IP first. Fix identity (PTR, HELO, SPF, DKIM, DMARC) and still prefer an ESP for anything a user must actually read. You can spin up an Ubuntu 24.04 instance on Netbay in under 60 seconds and run the port-25 and blocklist checks above — netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance