You provision a new VM. You deploy your app. You trigger a password reset email. Nothing arrives.
Your logs show retries, timeouts, and a quiet little truth: outbound TCP/25 is blocked.
If you’re thinking “I’ll just open port 25,” welcome to the modern internet where abuse ruined the party.
The good news is you can still send mail reliably—without tunneling through something cursed, and without becoming your own deliverability department.
What port 25 is (and why it’s blocked)
Port 25 is the classic SMTP port used primarily for server-to-server email delivery:
your mail transfer agent (MTA) talks to the recipient domain’s MX server and hands off the message.
That “direct-to-MX” model still exists, but it’s not how you should run email from random compute in 2026.
Cloud providers and many ISPs block outbound 25 by default for one reason: spam.
New IP ranges get abused fast. Blocking port 25 reduces abuse, protects provider reputation, and keeps their IP space off blocklists.
You’re not being personally targeted; you’re living in a neighborhood where someone keeps setting couches on fire.
The modern pattern is: your server sends mail via an authenticated submission endpoint (usually port 587 with STARTTLS, or port 465 with implicit TLS),
or via a mail-sending API. The relay service then handles deliverability: IP reputation, rate limiting, feedback loops, DKIM signing, and so on.
One quote to tape above your monitor (paraphrased idea): Gene Kranz: “Be tough and competent.” Reliability work is mostly competence under pressure.
Fast diagnosis playbook
When email fails, people immediately argue about SPF vs DKIM vs “the provider is down.”
Stop. Diagnose like an SRE: narrow the blast radius, then locate the bottleneck.
First: confirm it’s actually a network block (not an app bug)
- From the host that sends mail, test TCP reachability to a known SMTP server on 25, 587, and 465.
- Check whether your application is trying direct-to-MX, or using a relay.
- Look for “connection timed out” (network block) vs “authentication failed” (credentials) vs “rejected” (policy/deliverability).
Second: identify who blocks it
- Is the cloud provider blocking outbound 25 at the hypervisor edge?
- Is your VPC/NACL/security group restricting egress?
- Is a local firewall (ufw/iptables/nftables) blocking?
Third: choose the clean alternative
- If you just need transactional email: use SMTP submission on 587/465 to a reputable relay.
- If you need high-volume or strong telemetry: use an email API (still often backed by SMTP under the hood).
- If you’re doing internal notifications only: route to an internal relay or collaboration tool, not the public internet.
Fourth: verify deliverability, not just “it connected”
- Check that your From domain is authenticated (SPF/DKIM/DMARC) and aligned with the sender.
- Confirm bounce handling. Silent failures are the expensive kind.
Joke #1: Port 25 is like the office microwave—if you leave it unsupervised, someone will put fish in it and ruin it for everyone.
What to do instead of port 25
Option A (recommended for most): authenticated SMTP relay on port 587 (STARTTLS)
This is the “boring and correct” setup. Your host sends mail to a relay using credentials.
The relay speaks SMTP to the wider world, manages reputation, and handles the ugly bits.
Use port 587 with STARTTLS unless the provider explicitly wants port 465. Port 587 is the standards-based submission port,
and it plays nicely with modern security policies.
Option B: SMTP relay on port 465 (implicit TLS)
Port 465 is widely supported and, in practice, fine. It’s “SMTPS” where TLS is established immediately.
If you’re in a locked-down environment that blocks STARTTLS or mangles it, 465 can be simpler.
Option C: provider email API (HTTP)
If your environment blocks outbound SMTP entirely, or you want cleaner instrumentation and idempotency,
an HTTPS API is often easier. It also avoids SMTP’s famously “friendly but vague” error semantics.
The catch: you’re coupling app logic to a provider API. That’s okay if you treat it like any other external dependency:
timeouts, retries with backoff, circuit breakers, and queues.
Option D: internal relay + outbound gateway
In corporate environments, the right answer is frequently “don’t send mail from app servers at all.”
Send to an internal relay (even on port 25 inside the network), and let a hardened gateway handle external delivery.
Option E: ask for port 25 unblocking (rarely the best first move)
Some cloud providers will unblock outbound 25 after you prove you’re not a spam cannon.
This is occasionally justified for legacy systems or for running a real MTA. It is not a shortcut to deliverability.
New IP reputation is brutal, and “we opened port 25” is not a mail strategy.
Practical tasks and commands (with decisions)
Here are operational tasks you can run today. Each one includes: command, what output means, and the decision you make.
These are written for Linux hosts running Postfix, but the diagnostics apply broadly.
Task 1: Prove TCP/25 is blocked (timeout vs refuse matters)
cr0x@server:~$ nc -vz -w 3 gmail-smtp-in.l.google.com 25
nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progress
Meaning: A timeout usually indicates a network policy block (provider edge, firewall, or egress filtering).
A “refused” would mean you reached the host but the port is closed (rare for public MX).
Decision: Don’t waste time tweaking Postfix. Move to 587/465 relay or fix egress policy.
Task 2: Check if 587 works (your escape hatch)
cr0x@server:~$ nc -vz -w 3 smtp.relay.example 587
Connection to smtp.relay.example 587 port [tcp/submission] succeeded!
Meaning: Network path exists to submission port.
Decision: Configure your MTA/app to authenticate to this relay on 587 with TLS.
Task 3: Validate STARTTLS negotiation (don’t assume encryption)
cr0x@server:~$ openssl s_client -starttls smtp -crlf -connect smtp.relay.example:587 -servername smtp.relay.example < /dev/null
CONNECTED(00000003)
---
250-STARTTLS
---
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Meaning: Server advertises STARTTLS and you negotiated modern TLS.
Decision: If STARTTLS is missing or TLS fails, switch to port 465 or fix TLS interception/proxy issues.
Task 4: Check if your application is trying direct-to-MX (bad default)
cr0x@server:~$ grep -R "smtp" -n /etc | head
/etc/postfix/main.cf:relayhost =
/etc/postfix/main.cf:inet_interfaces = loopback-only
Meaning: Empty relayhost means Postfix will try direct delivery to MX records.
Decision: Set relayhost to your authenticated smart host unless you intentionally operate an outbound MTA.
Task 5: Inspect Postfix queue (is mail stuck?)
cr0x@server:~$ mailq
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
3F1A12C0A9 1542 Fri Jan 3 10:41:12 alerts@yourdomain.example
(connect to gmail-smtp-in.l.google.com[142.250.0.27]:25: Connection timed out)
user@gmail.com
-- 1 Kbytes in 1 Request.
Meaning: Postfix is attempting direct-to-MX on 25 and timing out.
Decision: Stop the bleeding: configure relayhost and flush the queue after confirming credentials.
Task 6: Identify who is blocking egress (local firewall check)
cr0x@server:~$ sudo nft list ruleset | sed -n '1,120p'
table inet filter {
chain output {
type filter hook output priority 0;
policy accept;
}
}
Meaning: Output chain policy is accept; local firewall likely not blocking.
Decision: If you see explicit drops for tcp dport 25/587/465, fix those rules or coordinate with security.
Task 7: Check cloud egress rules (security group / NACL equivalent via instance tooling)
cr0x@server:~$ ip route
default via 10.0.0.1 dev eth0
10.0.0.0/16 dev eth0 proto kernel scope link src 10.0.12.34
Meaning: You’re in a private network; egress likely traverses NAT or firewall appliances.
Decision: Validate NAT/security policies allow 587/465. Don’t assume “default route” means “internet works for SMTP.”
Task 8: Configure Postfix relayhost (core fix)
cr0x@server:~$ sudo postconf -e 'relayhost = [smtp.relay.example]:587'
cr0x@server:~$ sudo postconf -e 'smtp_tls_security_level = encrypt'
cr0x@server:~$ sudo postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
Meaning: Brackets force Postfix to use the host as-is (no MX lookup). Port 587 specified explicitly. TLS required.
Decision: Proceed only if you can authenticate (next task). Otherwise you’ll trade timeouts for auth failures.
Task 9: Enable SASL authentication for the relay
cr0x@server:~$ sudo postconf -e 'smtp_sasl_auth_enable = yes'
cr0x@server:~$ sudo postconf -e 'smtp_sasl_security_options = noanonymous'
cr0x@server:~$ sudo postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
Meaning: Postfix will authenticate using credentials stored in /etc/postfix/sasl_passwd.
Decision: Lock down the password file permissions. If you can’t protect it, don’t do SMTP creds on that box.
Task 10: Create credentials map and verify it compiles
cr0x@server:~$ sudo bash -c 'printf "[smtp.relay.example]:587 username:app-specific-password\n" > /etc/postfix/sasl_passwd'
cr0x@server:~$ sudo chmod 0600 /etc/postfix/sasl_passwd
cr0x@server:~$ sudo postmap /etc/postfix/sasl_passwd
cr0x@server:~$ ls -l /etc/postfix/sasl_passwd*
-rw------- 1 root root 63 Jan 3 10:52 /etc/postfix/sasl_passwd
-rw------- 1 root root 122 Jan 3 10:52 /etc/postfix/sasl_passwd.db
Meaning: The .db file is the compiled map Postfix uses. Permissions are tight.
Decision: If postmap errors, fix format or hashing toolchain before restarting Postfix.
Task 11: Reload Postfix and watch logs live during a test send
cr0x@server:~$ sudo systemctl reload postfix
cr0x@server:~$ sudo tail -f /var/log/mail.log
Jan 3 10:54:11 server postfix/smtp[22119]: 3F1A12C0A9: to=<user@gmail.com>, relay=smtp.relay.example[203.0.113.10]:587, delay=12, delays=0.1/0.02/2.1/9.8, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as ABC123)
Meaning: Mail is now relayed through the smart host on 587 and accepted (DSN 2.0.0).
Decision: Flush the queue if there’s backlog; then monitor bounce rates and provider dashboards.
Task 12: Flush the queue (only after you fix the root issue)
cr0x@server:~$ sudo postfix flush
cr0x@server:~$ mailq | tail -n 3
Mail queue is empty
Meaning: Backlogged messages are attempted immediately; queue is now empty.
Decision: If messages requeue with auth errors, stop and fix credentials. Don’t hammer the relay and trip rate limits.
Task 13: Confirm you’re not accidentally an open relay (yes, people still do this)
cr0x@server:~$ sudo postconf mynetworks smtpd_recipient_restrictions | sed -n '1,3p'
mynetworks = 127.0.0.0/8 [::1]/128
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination
Meaning: Only localhost is trusted; unauthenticated outbound relaying is rejected. Good.
Decision: If you see permissive rules (like broad RFC1918 ranges) on an internet-facing host, fix it immediately.
Task 14: Check DNS for SPF (basic but mandatory)
cr0x@server:~$ dig +short TXT yourdomain.example
"v=spf1 include:spf.relay.example -all"
Meaning: SPF exists and delegates to your relay provider.
Decision: If SPF is missing or too permissive, fix it before scaling volume. Deliverability debt compounds fast.
Task 15: Check DMARC policy (your domain’s safety rails)
cr0x@server:~$ dig +short TXT _dmarc.yourdomain.example
"v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.example; adkim=s; aspf=s"
Meaning: DMARC exists and enforces alignment (strict in this example).
Decision: If you don’t have DMARC, start with p=none to observe, then move toward enforcement once aligned.
Task 16: Confirm the server isn’t trying IPv6 and failing (surprisingly common)
cr0x@server:~$ postconf inet_protocols
inet_protocols = all
Meaning: Postfix will use IPv4 and IPv6. If IPv6 egress is broken, you might see intermittent SMTP failures.
Decision: If logs show IPv6 timeouts, either fix IPv6 routing/DNS or temporarily set inet_protocols = ipv4.
Postfix reference setup: relay through a smart host
If you’re running Postfix on the server (or your application hands mail off to localhost), a smart host relay configuration is the cleanest solution.
The goal is simple: your box should not be negotiating with random MX servers on the internet. It should talk to one relay you trust.
A sane minimal main.cf shape
These are the knobs that matter for this problem. Keep it small. Keep it understandable. Complexity is where outages breed.
cr0x@server:~$ sudo postconf -n | egrep '^(relayhost|smtp_tls|smtp_sasl|inet_interfaces|myhostname|myorigin)'
myhostname = server.yourdomain.example
myorigin = /etc/mailname
inet_interfaces = loopback-only
relayhost = [smtp.relay.example]:587
smtp_tls_security_level = encrypt
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
A few opinions, because you’re here for decisions:
- Set
inet_interfaces = loopback-onlyon app servers. You are not offering inbound SMTP to the world. Don’t cosplay as an MTA. - Use
smtp_tls_security_level = encryptso you don’t silently downgrade to plaintext. - Bracket the relayhost so Postfix doesn’t MX-resolve it. You want one explicit destination.
- Store credentials securely. If this host is ephemeral, use secret management and render the map at boot.
Don’t leak credentials into logs or shell history
Use app-specific passwords or API keys scoped to sending only. Rotate them. Treat SMTP creds like database creds: stolen means abuse, abuse means blocklists.
Application-level sending: SMTP vs API
You can send email from your application in two mainstream ways:
SMTP (to a relay) or an HTTP API (to the same class of providers). Neither is morally pure.
Pick based on your failure modes and your operational maturity.
SMTP from apps: straightforward, but watch timeouts and pooling
SMTP libraries vary wildly in how they handle STARTTLS, connection reuse, and partial failures.
You want:
- Explicit host/port (587 or 465), never “send to MX” logic.
- Reasonable connect and read timeouts (not infinite).
- Retries with backoff for transient 4xx responses.
- Queueing (even a simple local queue) if mail is mission-critical.
Email API: better observability, fewer protocol edge cases
With an API you typically get message IDs, structured error responses, and webhooks for bounces/complaints.
It’s easier to do idempotency (“don’t send duplicate password reset emails”) and easier to instrument.
The gotcha: if you treat the API as “always available,” your app will melt when the provider has a bad day.
Use a queue, set a budget for retries, and degrade gracefully.
Deliverability basics you can’t ignore
Port 25 being blocked is a transport problem. But once you fix transport, deliverability becomes your next outage.
This is where teams get surprised: messages are “sent” (accepted by the relay) but never seen by humans.
Use a real envelope sender and handle bounces
Configure a bounce domain/address that you monitor. If you ignore bounces, you’ll keep sending to dead addresses,
your complaint rate will climb, and your provider will throttle or suspend you.
SPF, DKIM, DMARC: alignment beats superstition
- SPF says which servers may send for your domain.
- DKIM signs messages so recipients can verify they weren’t altered.
- DMARC ties it together with alignment rules and reporting.
The operational rule: the domain in the From header should align with SPF and/or DKIM, and DMARC should express an intentional policy.
If your relay signs DKIM for you, great—publish the selector records it gives you and verify they propagate.
Rate limits exist; they’re not personal
Even good relays enforce per-account and per-domain limits. Treat SMTP/API responses as part of the contract.
If you blast the relay with a stuck retry loop, you will discover the limit the hard way.
Joke #2: SMTP error codes are the only place you’ll see a server politely say “I can’t do that” while actively judging you.
Corporate mini-stories from the trenches
Mini-story 1: The incident caused by a wrong assumption
A mid-sized SaaS company migrated workloads from a colo to a major cloud. The app sent signup and password reset emails through a local Postfix instance.
In colo, they had a static IP range with decent reputation and outbound 25 was open. In cloud, the team assumed the same would be true.
Launch week arrived. The app was healthy, the database was healthy, traffic was up—and the support queue caught fire.
Users couldn’t verify accounts, couldn’t reset passwords, and churn started within hours.
The on-call engineer saw Postfix queue growth and assumed “recipient MX is slow.”
They scaled the Postfix box. Twice. Queue grew faster, because every new instance was also blocked on 25.
The wrong assumption wasn’t just “port 25 is open.” It was “email is part of app logic, not infrastructure.”
The fix was boring: configure relayhost to a reputable authenticated relay on 587, publish SPF/DKIM, flush the queue, and add an integration test that verifies SMTP submission connectivity on deploy.
The postmortem recommendation was even more boring: treat email as an external dependency with explicit SLOs and alerting on queue depth.
Mini-story 2: The optimization that backfired
An enterprise team wanted to reduce latency for transactional emails. Someone proposed keeping a persistent SMTP connection open from each app pod to the relay provider,
reusing it for hundreds of messages to save TCP and TLS handshakes. It looked elegant in a diagram.
In practice, it created a new failure mode: when the relay rotated certificates or enforced idle timeouts, pods kept writing to dead sockets.
Some SMTP libraries reported success until the final flush; others threw opaque exceptions; a few just hung.
Latency improved right up until it didn’t, and then deliveries became non-deterministic.
The team also discovered that their “optimization” amplified rate-limit bursts. When a batch job kicked off, every pod dumped messages as fast as it could over a warm connection.
The relay responded with transient 4xx deferrals, which their retry logic interpreted as “try again immediately,” producing a neat little self-DDOS.
They rolled back to a controlled queue-based sender service with bounded concurrency, explicit timeouts, and exponential backoff.
The final architecture was less clever and far more predictable. Predictability is the point.
Mini-story 3: The boring but correct practice that saved the day
A financial services platform ran weekly disaster recovery tests. Part of the checklist: confirm outbound email via the relay works from the DR region, including DNS-based authentication.
It was not glamorous work. It was also the first thing people tried to skip when the schedule got tight.
One quarter, the DR test failed: emails were accepted by the relay, but messages arrived without DKIM signatures.
The team traced it to a mis-scoped relay account in the DR environment: it could submit mail, but the signing configuration wasn’t attached to that credential set.
They fixed the configuration before it ever mattered, and later that year a real regional incident forced traffic to DR.
When customer notifications went out, they delivered cleanly. No scrambling. No “why are we in spam.”
The lesson: the most reliable systems are built from small, verifiable boring steps. If your email path isn’t in your DR plan, you don’t have a DR plan—you have a wish.
Common mistakes: symptom → root cause → fix
1) Symptom: mail queue grows; logs show timeouts to recipient MX on port 25
Root cause: Outbound 25 blocked; Postfix trying direct-to-MX delivery.
Fix: Configure relayhost to an authenticated smart host on 587/465; require TLS; flush queue after.
2) Symptom: “SASL authentication failed” or “535 Authentication credentials invalid”
Root cause: Wrong username/password, wrong port, or provider requires app-specific password.
Fix: Verify credentials format in /etc/postfix/sasl_passwd, re-run postmap, confirm you’re using the provider’s submission endpoint and TLS requirements.
3) Symptom: messages accepted by relay, but land in spam (or never seen)
Root cause: Missing/incorrect SPF or DKIM, misaligned From domain, or sending from a domain without reputation.
Fix: Publish SPF including the relay; enable DKIM signing; add DMARC; ensure From domain aligns with signing domain.
4) Symptom: intermittent timeouts; logs show IPv6 addresses failing
Root cause: IPv6 egress/routing broken, but system prefers AAAA records sometimes.
Fix: Fix IPv6 path or set Postfix to IPv4 only as a mitigation (inet_protocols = ipv4).
5) Symptom: TLS handshake failures with STARTTLS
Root cause: TLS inspection proxy, missing CA bundle, or provider requiring SNI/modern TLS.
Fix: Validate with openssl s_client; update CA certs; use port 465 if STARTTLS is interfered with; ensure -servername works.
6) Symptom: “Relay access denied” or “unauthorized sender”
Root cause: Provider requires the envelope sender or From domain to be verified, or you’re sending from an unapproved domain.
Fix: Verify sending domain in provider; align From; configure myorigin/myhostname sensibly; avoid random From addresses.
7) Symptom: you can send from the host, but the application can’t
Root cause: App uses its own SMTP settings (direct-to-MX, wrong port), or container network policies block egress.
Fix: Standardize: either app sends to localhost Postfix, or app sends directly to relay on 587/465. Don’t mix silently.
8) Symptom: high latency, occasional duplicates
Root cause: Retry logic without idempotency; SMTP connection reuse gone wrong; lack of queue.
Fix: Put mail sending behind a queue; use message IDs and dedupe keys; bound concurrency and retries.
Checklists / step-by-step plan
Step-by-step plan: from “blocked” to “reliably sending”
- Confirm the block: test TCP to 25/587/465 from the sending host.
- Pick a sending model: SMTP submission (587/465) to a relay, or API over HTTPS.
- Stop direct-to-MX delivery: configure Postfix
relayhostor app SMTP host. - Require TLS: STARTTLS on 587 or implicit TLS on 465.
- Enable auth: SASL credentials or API key.
- Lock down: loopback-only listener for Postfix; permissions on credential files; least-privileged accounts.
- Send a test: watch logs for status=sent and a provider queue ID.
- Fix authentication DNS: SPF, DKIM, DMARC aligned to the From domain.
- Implement bounce/complaint handling: mailbox or webhook sink; alert on spikes.
- Operationalize: monitor queue depth, error codes, provider deferrals, and delivery latency.
On-call checklist: when emails stop again
- Is the relay reachable on 587/465 from affected hosts?
- Are credentials still valid (rotated? expired?)
- Is the provider returning 4xx deferrals (rate limit) or 5xx rejections (policy)?
- Is DNS resolution healthy for relay endpoint and your domain records?
- Did anything change: firewall, egress proxy, CA bundle, time sync?
Security checklist: avoid turning email into an incident
- Store SMTP creds in a secret manager; render at runtime; rotate regularly.
- Do not allow inbound SMTP from the internet unless you are intentionally running an MTA.
- Use dedicated subdomains for different mail streams (transactional vs marketing) when possible.
- Set sensible alerting: sudden spike in sent volume may indicate compromised credentials.
Interesting facts and historical context
- SMTP predates the commercial internet: it was standardized in the early 1980s, when trust between hosts was assumed more than verified.
- Port 587 exists for a reason: it’s the submission port meant for clients to submit mail to an MSA (Mail Submission Agent), usually with authentication.
- Port 465 had a weird history: it was used early for implicit TLS, later deprecated, then effectively “came back” because it works and clients support it.
- Port 25 blocking became normal with broadband: as consumer machines got infected and spam volumes exploded, ISPs started blocking outbound 25 from residential networks.
- Cloud providers block by default to protect IP reputation: a single abused subnet can poison deliverability for thousands of legitimate tenants.
- SPF was born from operational pain: it emerged in the early 2000s to reduce forged sender domains, but it only authenticates the envelope path, not message integrity.
- DKIM’s core idea is cryptographic accountability: a domain signs mail so recipients can verify it, shifting trust from IP reputation alone.
- DMARC added policy and reporting: it tells receivers what to do when authentication fails and provides feedback loops via aggregate reports.
- Email is still one of the last global federated protocols: it’s an open system where anyone can run a server—great for resilience, rough for abuse.
FAQ
1) Why is port 25 blocked on my VM?
Usually because your provider blocks outbound SMTP by default to prevent spam and protect their IP range reputation.
Sometimes it’s your own firewall or egress policy, but in cloud it’s commonly enforced upstream.
2) Can I just request port 25 to be unblocked?
Sometimes, yes. But you still inherit deliverability work: IP warmup, reverse DNS, complaint handling, blocklist monitoring, abuse prevention.
If you only need transactional mail, using a relay on 587/465 is almost always the better business decision.
3) What’s the difference between port 25 and port 587?
Port 25 is traditionally MTA-to-MTA delivery. Port 587 is for authenticated mail submission from clients/apps to a relay (MSA),
typically with STARTTLS and credentials.
4) Should I use port 465 or 587?
Default to 587 with STARTTLS. Use 465 if your environment breaks STARTTLS or your provider recommends 465.
The key is: use TLS and authentication either way.
5) My relay accepts mail, but Gmail puts it in spam. Is port 25 still the issue?
No. That’s deliverability: missing or misaligned SPF/DKIM/DMARC, new sender domain reputation, content issues, or high bounce/complaint rates.
Port 25 is transport; spam placement is reputation and authentication.
6) Is it safe to store SMTP credentials on the server?
It can be, if you treat them like any other secret: least privilege, strict permissions, no logging, and regular rotation.
Prefer secret managers and short-lived credentials where available.
7) Do I need Postfix at all? Can my app send directly to the relay?
You can do either. Postfix is useful as a local buffer and policy point (queueing, retries, standardized logging).
Direct app-to-relay can be fine for simpler systems—just make sure you implement sensible retry behavior and timeouts.
8) What if outbound SMTP is blocked on all ports?
Use an email API over HTTPS, or route mail through an internal gateway that has allowed egress.
Also check whether your network requires an explicit proxy for outbound internet traffic.
9) How do I know if the problem is DNS?
If you see “host not found” errors, intermittent failures across different destinations, or your SPF/DKIM/DMARC lookups fail,
you likely have DNS resolution issues. Test with dig, verify resolver configuration, and check for split-horizon surprises.
10) Can I run my own mail server and avoid relays entirely?
You can, and people still do, but it’s an operations commitment. You need stable IP reputation, correct reverse DNS, abuse handling,
monitoring, and constant tuning. If email is not your product, don’t make it your hobby.
Conclusion: next steps that won’t wake you up at 3 a.m.
When port 25 is blocked, the right move is not to get clever. The right move is to stop doing direct-to-MX delivery from application servers.
Use authenticated submission on 587/465, or an email API, and let a proper relay handle the messy public-internet side of SMTP.
Practical next steps:
- Run the connectivity tests to confirm where the block is (25 vs 587/465).
- Pick a relay or API approach and standardize it across environments.
- Configure Postfix (or your app) to send only to that relay with TLS + auth.
- Publish and verify SPF/DKIM/DMARC alignment for the From domain.
- Add monitoring: mail queue depth, relay deferrals, bounce/complaint rates.
Email is a reliability problem wearing a communication costume. Treat it like production infrastructure, and it behaves.