Date: 2026-06-20 Public version note: this post keeps the setup order, config shape, validation path, and operational decisions for a self-hosted SMTP relay server. Environment-specific identifiers have been replaced with example values. The redacted code and config examples are in this GitHub Gist: [Self-hosted SMTP HTTP relay config example (English)](https://gist.github.com/leconio/0a3f48b860f15616cf7a3ca019e6b07e#file-01-self-hosted-smtp-relay-server-en-md). The `server.js` in that Gist was exported from the server path `/opt/coolmeow-mail-relay/server.js`, then sanitized. The Gist also includes systemd, Postfix, OpenDKIM, Cloudflare Tunnel, environment file, and DNS examples, so each config file has a clear server-side location. The Chinese notes are in `00-self-hosted-smtp-relay-server.zh-CN.md` in the same Gist. The article itself uses `example.com`, `203.0.113.10`, and `example-mail-relay` so the steps stay reusable. If you want to see where my server actually keeps the code and config, use the Gist. If you build your own version, replace those names with your own domain, IP, service directory, and sender addresses. This is written in the order I would actually build it. The goal is not to run a public SMTP submission service. The goal is a small outbound-only transaction mail relay on a VPS: ```text Business service -> HTTP/HTTPS /send -> Node relay -> local queue -> sendmail -> Postfix -> OpenDKIM -> recipient MX ``` The public entry layer is optional. It can be Nginx, a VPN, mTLS, Cloudflare Tunnel, or nothing at all if only local services call `127.0.0.1`. The non-negotiable parts are clear MTA identity, correct DNS authentication, a Postfix setup that can deliver mail reliably, and a relay interface that cannot become an open mail sender. This setup sends mail only. It does not receive mail, does not take over the domain's MX, and does not affect the mailbox service that already handles replies and bounces. User replies, delivery failures, and DMARC aggregate reports still follow the domain's MX records. The dependency order matters. First decide whether the VPS and its IP are worth using for outbound mail. Then fix the sending domain, MTA hostname, and HTTP relay entry. Once those names are stable, set A, PTR/rDNS, SPF, DMARC, and MX in DNS and the provider panel. After the DNS identity is clear, install Postfix on the VPS and keep it loopback-only. Then attach OpenDKIM so outbound messages get signed. Only after that does it make sense to add a local HTTP relay that turns business requests into email and hands them to sendmail. systemd, external access, inbox header validation, and old DNS cleanup come after the local chain works. ## Step 0: check whether this VPS and IP can send mail This step does not touch Postfix and does not edit `/etc/postfix`. Work in three places: your VPS provider panel, reputation lookup sites, and the VPS shell. On the server you only need small diagnostic tools such as `dig` and `nc`. The actual setting you may need to edit is the provider's rDNS/PTR entry. Until IP reputation, outbound TCP 25, and PTR/rDNS are acceptable, it is too early to pick the final MTA hostname. Not every VPS or public IP is suitable for self-hosted SMTP. Many cloud providers block outbound TCP 25. Some IP ranges have a long history of spam, proxy abuse, mining, or scanners. Some providers do not let you set PTR/rDNS at all. If any of those are true, a careful Postfix, DKIM, and DMARC setup still may not reach the inbox consistently. Check IP reputation first. Good starting points: ```text Spamhaus IP and Domain Reputation Checker https://check.spamhaus.org/ Cisco Talos IP and Domain Reputation Center https://www.talosintelligence.com/reputation_center AbuseIPDB https://www.abuseipdb.com/ Scamalytics IP Fraud Check https://scamalytics.com/ip ``` Do not stop at "is it on a blocklist?". Also look for proxy/VPN flags, hosting-abuse scores, many abuse reports on nearby addresses, abnormal Talos email reputation, and Spamhaus SBL/CSS/PBL hits. If a newly rented IP is already dirty, replacing the IP or provider is usually faster than trying to explain your way out of that history. Then check outbound TCP 25 from the VPS: ```sh dig +short MX gmail.com nc -vz gmail-smtp-in.l.google.com 25 ``` For direct-to-MX delivery, Postfix must connect from your VPS to recipient MX servers on TCP 25. This is outbound TCP 25. It is not the same thing as opening inbound SMTP submission. If the connection times out, returns unreachable, or is blocked by policy, look for an SMTP unlock or port 25 request in the provider panel. If the provider does not allow it, do not keep building a direct-to-MX relay on that machine. Use a different provider or switch to an upstream `relayhost`. Finally, confirm that the provider lets you set PTR/rDNS: ```text 203.0.113.10 -> mail.example.com ``` And make the forward lookup point back to the same IP: ```text mail.example.com -> 203.0.113.10 ``` Verify both directions: ```sh dig +short -x 203.0.113.10 dig +short mail.example.com A ``` If PTR stays on a provider default name such as `vps-203-0-113-10.provider.example`, the server may still send mail, but it looks more like a throwaway host. For production transaction mail, use a VPS where PTR can match your MTA hostname. ## Step 1: decide the sending identity before installing software This step is still planning, not package installation. Write these values in your deployment notes or DNS draft: ```text Sending domain: example.com Default sender: [email protected] MTA hostname: mail.example.com HTTP relay entry: mail-relay.example.com/send VPS sending IP: 203.0.113.10 ``` These values will show up again in DNS records, `/etc/postfix/main.cf`, OpenDKIM tables, and the relay environment file. If they are vague here, DNS, EHLO, DKIM, and DMARC will drift apart later. The easy mistake is to mix up `mail.example.com` and `mail-relay.example.com`. ```text mail.example.com: SMTP-side MTA hostname used by Postfix HELO/EHLO mail-relay.example.com: HTTP-side relay entry used by business services ``` They can be different names. `mail-relay.example.com` is only for `/send`. `mail.example.com` is what recipient servers see during SMTP, PTR/rDNS checks, and reputation evaluation. ## Step 2: plan DNS and provider-side records Now you work in the DNS console and the VPS provider's rDNS/PTR panel. You do not need server software yet. A, SPF, DMARC, and MX live in your DNS zone. PTR/rDNS lives with the IP owner, usually the VPS provider. DKIM needs one extra loop: generate the key on the VPS in Step 4, then publish the public record in DNS. The Postfix values in Step 3, especially `myhostname` and `smtp_helo_name`, must match the A/PTR plan here. For a self-hosted sender, inbox placement depends more on MTA identity, DNS authentication, and reputation than on the Node relay. I treat these as production basics: ```text A / AAAA: MTA hostname resolves to the sending IP PTR / rDNS: sending IP resolves back to the MTA hostname HELO / EHLO: Postfix announces a resolvable hostname that matches the plan SPF: authorizes the sending IP for the envelope sender domain DKIM: signs message content and important headers DMARC: checks visible From alignment through SPF or DKIM MX: keeps replies, bounces, and DMARC reports deliverable TLS: lets SMTP transport encrypt when the peer supports it Low complaint rate: authentication does not cancel user spam reports ``` These are not edited in one place. A, SPF, DMARC, and MX are DNS records. PTR/rDNS is in the provider panel. HELO/EHLO and TLS are Postfix settings in `/etc/postfix/main.cf`. DKIM starts on the VPS and ends as a DNS TXT record. ### A / AAAA If Postfix uses: ```text myhostname = mail.example.com smtp_helo_name = mail.example.com ``` Then DNS needs at least: ```text mail.example.com A 203.0.113.10 ``` Add AAAA only if the server really sends over IPv6 and the IPv6 side has matching routing, firewall, PTR, and reputation work: ```text mail.example.com AAAA 2001:db8::10 ``` Do not publish AAAA just because the provider gives you one. A half-configured IPv6 path can create random-looking mail failures. ### PTR / rDNS PTR usually cannot be added in your normal DNS zone. Set it in the VPS provider panel: ```text 203.0.113.10 -> mail.example.com mail.example.com -> 203.0.113.10 ``` That is forward-confirmed reverse DNS. Google and Yahoo sender guidance both treat valid forward and reverse DNS as a baseline requirement. ### HELO / EHLO During SMTP delivery, Postfix announces: ```text EHLO mail.example.com ``` That value comes from `myhostname` or `smtp_helo_name`. It does not have to be the HTTP relay name, but it must resolve and should match PTR/rDNS. This matters during cleanup. If `mail.example.com` is still the Postfix HELO/EHLO name, do not delete its A record. Either keep the record or first move Postfix to a new MTA hostname that is also resolvable and PTR-aligned. ### SPF SPF checks the SMTP envelope sender domain, commonly seen later as `Return-Path`. It answers: ```text Is this sending IP allowed to send mail for example.com? ``` Example: ```text example.com TXT "v=spf1 ip4:203.0.113.10 include:_spf.mx.cloudflare.net ~all" ``` Meaning: ```text v=spf1: SPF record ip4:203.0.113.10: this VPS may send include:_spf.mx.cloudflare.net: Cloudflare mail-related sender is also allowed if you use it ~all: other sources soft-fail ``` If only this VPS should send mail for the domain, tighten it later: ```text example.com TXT "v=spf1 ip4:203.0.113.10 -all" ``` Do that only after confirming there are no other legitimate senders. ### DKIM DKIM signs outbound messages. Postfix passes mail through OpenDKIM, and recipient servers verify the signature against a public DNS record. Example: ```text mail202606._domainkey.example.com TXT "v=DKIM1; h=sha256; k=rsa; p=<public-key>" ``` `mail202606` is the selector. Naming selectors by year and month makes rotation easier. DKIM covers the case SPF cannot: if `d=example.com` in the DKIM signature aligns with the visible `From: [email protected]`, DMARC can pass through DKIM even when forwarding breaks SPF. ### DMARC DMARC looks at the visible `From:` domain and requires SPF or DKIM to align with it. Example: ```text _dmarc.example.com TXT "v=DMARC1; p=quarantine; pct=100; rua=mailto:[email protected]; adkim=s; aspf=s" ``` Meaning: ```text v=DMARC1: DMARC record p=quarantine: ask receivers to isolate mail that fails authentication pct=100: apply the policy to all messages rua=mailto:[email protected]: aggregate reports go here adkim=s: strict DKIM alignment aspf=s: strict SPF alignment ``` Use `p=none` only while observing. For a production sending domain, move to `p=quarantine` once legitimate mail is passing, and consider `p=reject` later. ### MX A send-only relay does not technically need to receive mail, but the domain should still have working MX. Reasons: ```text Users reply to [email protected] Bounces need somewhere to go DMARC aggregate reports need [email protected] Some receivers distrust a From domain that cannot receive mail at all ``` Example: ```text example.com MX 10 route1.mx.cloudflare.net. example.com MX 20 route2.mx.cloudflare.net. example.com MX 30 route3.mx.cloudflare.net. ``` Do not point MX at this VPS just because it sends mail. Keep using Google Workspace, Cloudflare Email Routing, Fastmail, or whatever currently receives mail for the domain unless you are intentionally building a receiving server too. ### TLS and inbox placement TLS is not a DNS record, but it is part of a normal SMTP setup: ```text smtp_tls_security_level = may smtpd_tls_security_level = may ``` `may` means "encrypt when the peer supports it." MTA-STS, TLS-RPT, and DANE are later improvements. Basic opportunistic TLS is enough for the first version. Passing SPF, DKIM, DMARC, and PTR does not guarantee inbox placement. It only gets you into the game. Receivers still look at IP history, domain history, sudden volume spikes, spam rate, body quality, broken links, HTML-only mail, invalid recipients, and Postfix retry behavior. This setup is best for transaction mail: account security, ticket updates, system alerts. Do not use a small VPS as a bulk marketing sender. ## Step 3: configure Postfix as a local outbound MTA Now work in the VPS shell. Install Postfix, and optionally `mailutils` or `bsd-mailx` for local testing. The main file is `/etc/postfix/main.cf`. Do not enable public `submission` or `smtps` in `/etc/postfix/master.cf` for this relay. The goal is a local MTA that listens on `127.0.0.1:25` and provides `/usr/sbin/sendmail` for local delivery into Postfix. The shape is: ```text 127.0.0.1:25 Postfix smtpd 127.0.0.1:2525 Node HTTP relay 127.0.0.1:8891 OpenDKIM milter ``` And deliberately not: ```text 0.0.0.0:25 0.0.0.0:465 0.0.0.0:587 ``` Only local processes can submit mail to Postfix. Postfix then connects outbound to recipient MX servers. Key `/etc/postfix/main.cf` settings: ```text inet_interfaces = loopback-only inet_protocols = ipv4 myhostname = mail.example.com myorigin = $mydomain mynetworks = 127.0.0.0/8 smtpd_relay_restrictions = permit_mynetworks, reject_unauth_destination smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination smtp_tls_security_level = may smtpd_tls_security_level = may ``` After OpenDKIM is ready, Postfix also gets: ```text milter_protocol = 6 milter_default_action = accept smtpd_milters = inet:127.0.0.1:8891 non_smtpd_milters = inet:127.0.0.1:8891 ``` Keep `relayhost =` empty if this VPS delivers directly to recipient MX: ```text relayhost = ``` The relay code will hand mail to Postfix through: ```sh /usr/sbin/sendmail -t -oi -f [email protected] ``` The flags matter: - `-t` reads recipients from the message headers. - `-oi` prevents a line containing only `.` from ending the message. - `-f` sets the envelope sender for SPF and DMARC alignment. ## Step 4: connect OpenDKIM Once Postfix can submit mail locally, install `opendkim` and `opendkim-tools` on the VPS. This step spans two places. The signing key and OpenDKIM config stay on the VPS. The public DKIM record goes back to DNS. Common files: ```text /etc/opendkim.conf /etc/opendkim/SigningTable /etc/opendkim/KeyTable /etc/opendkim/TrustedHosts /etc/opendkim/keys/example.com/mail202606.private ``` OpenDKIM listens only on loopback: ```text Socket inet:[email protected] ``` Postfix connects to it as a milter: ```text milter_protocol = 6 milter_default_action = accept smtpd_milters = inet:127.0.0.1:8891 non_smtpd_milters = inet:127.0.0.1:8891 ``` OpenDKIM tables look like this: ```text SigningTable: *@example.com -> mail202606._domainkey.example.com KeyTable: mail202606._domainkey.example.com -> example.com:mail202606:/etc/opendkim/keys/example.com/mail202606.private ``` Then publish the public key in DNS: ```text mail202606._domainkey.example.com TXT "v=DKIM1; h=sha256; k=rsa; p=<public-key>" ``` Validation is simple: outbound mail should have a `DKIM-Signature` header, and the recipient's original headers should show: ```text dkim=pass [email protected] ``` ## Step 5: write the local HTTP relay Only now does the application relay matter. It runs on the VPS and needs a Node.js runtime. The code can live in `/opt/example-mail-relay/server.js` or your own service directory. The listening address should still be local: ```text 127.0.0.1:2525 ``` The relay has a small job: ```text GET /health POST /send ``` `/health` returns `ok` for local checks and proxy health checks. `/send` accepts JSON: ```json { "to": "[email protected]", "from": { "email": "[email protected]", "name": "Example Support" }, "replyTo": "[email protected]", "subject": "Your request has been received", "text": "Plain text body...", "html": "<!doctype html>..." } ``` The relay itself only needs: ```text Authorization: Bearer <relay-token> Content-Type: application/json ``` If Cloudflare Access, mTLS, VPN, or another gateway sits in front, those headers or client certs belong to the entry layer. The Node relay should not depend on the gateway's internal details unless it has to. ## Step 6: keep the relay small and strict This step edits the relay code, not Postfix. The main file is still `/opt/example-mail-relay/server.js`. Runtime values live on the VPS in `/etc/example-mail-relay.env`, not in the repository. The relay should reject anything that could turn it into a mail-forging endpoint: ```text body limit: 512 KiB token: at least 32 bytes rate limit: for example 60 requests per source IP per minute to: valid email address from: must be in an allowlist replyTo: valid email address when present subject: capped length, for example 200 characters text/html: capped by the body limit ``` Sender allowlist: ```js const allowedFrom = new Set([ "[email protected]", "[email protected]", "[email protected]" ]); ``` This is not cosmetic. If any internal service with the relay token can choose any `From`, your domain reputation and DMARC alignment will eventually get messy. Clean mail headers before building the MIME message. Fields such as `Subject`, display name, and `Reply-To` must not contain raw newlines. Header injection is one of those bugs that looks theoretical until a service boundary changes. ## Step 7: write to disk before calling Postfix This still belongs to the relay project. No extra system package is required. Create real queue directories on the VPS: ```text /var/spool/example-mail-relay/pending /var/spool/example-mail-relay/processing /var/spool/example-mail-relay/failed ``` Do not make the HTTP request wait on the full SMTP delivery path. The better flow is: ```mermaid flowchart TD req["POST /send"] validate["Validate token, sender, body"] write["Atomically write to pending"] move["Move to processing"] send["Call sendmail"] ok["Delivered<br/>delete job"] retry{"Can retry?"} pending["Back to pending<br/>set nextAttemptAt"] failed["Move to failed"] req --> validate validate --> write write --> move move --> send send -->|success| ok send -->|failure| retry retry -->|yes| pending retry -->|no| failed ``` Write queue files through a temporary file and `rename`, so a half-written JSON file is never picked up by the worker. For a small transaction relay, serial draining is easier to reason about than parallel throughput. Exponential backoff is enough: ```text first failure: retry after about 30 seconds second failure: retry after about 60 seconds later failures: keep doubling maximum backoff: 1 hour maximum attempts: 8 ``` The queue is not meant for high-volume mail. It exists so a brief Postfix or network problem does not drop transaction messages. ## Step 8: run it under systemd with its own user After the queue is in place, turn the relay into a system service. This is still done in the VPS shell. systemd is usually already present. Node.js, the relay code, and queue directories should exist first. Create a separate user: ```text User=mailrelay Group=mailrelay ``` Put runtime values on the VPS: ```text /etc/example-mail-relay.env ``` Required: ```text SMTP_RELAY_TOKEN=<long-random-token> ``` Optional: ```text QUEUE_ROOT=/var/spool/example-mail-relay QUEUE_MAX_ATTEMPTS=8 SENDMAIL_TIMEOUT_MS=30000 ``` The systemd unit goes in: ```text /etc/systemd/system/example-mail-relay.service ``` Service shape: ```ini [Service] User=mailrelay Group=mailrelay WorkingDirectory=/opt/example-mail-relay EnvironmentFile=/etc/example-mail-relay.env ExecStart=/usr/bin/node /opt/example-mail-relay/server.js Restart=always PrivateTmp=true PrivateDevices=true ProtectSystem=strict ProtectHome=true NoNewPrivileges=true ReadOnlyPaths=/opt/example-mail-relay /etc/example-mail-relay.env ReadWritePaths=/var/spool/postfix /var/lib/postfix /var/mail /var/spool/example-mail-relay /run /tmp ``` The deployment script can live in a repository. The environment file should stay on the server. Reload and start: ```sh systemctl daemon-reload systemctl enable --now example-mail-relay.service journalctl -u example-mail-relay.service -n 100 --no-pager ``` ## Step 9: choose an entry layer only if you need one If only services on the same VPS call the relay, Step 8 is enough. Use: ```text http://127.0.0.1:2525/send ``` If another backend needs to call it, add a controlled entry layer. This is not a Postfix change. It is a gateway change: ```text Nginx + HTTPS + allowlist VPN / Tailscale / WireGuard mTLS reverse proxy Cloudflare Tunnel + Access ``` With Cloudflare Tunnel, the shape is: ```text https://mail-relay.example.com/send -> http://127.0.0.1:2525/send ``` If Cloudflare Access is enabled, the caller also sends: ```text CF-Access-Client-Id CF-Access-Client-Secret ``` Cloudflare is not required. The point is to avoid exposing a naked public sending endpoint. The gateway controls who can reach the relay. The Bearer token controls what the relay accepts. Keep those two layers separate. ## Step 10: validate locally first Do not start with the public endpoint. First validate the local chain on the VPS: ```text 127.0.0.1:2525 -> sendmail -> Postfix -> OpenDKIM ``` Check health: ```sh curl -i http://127.0.0.1:2525/health ``` Expected: ```text HTTP/1.1 200 OK ok ``` Check the Postfix queue: ```sh postqueue -p ``` A clean idle queue says: ```text Mail queue is empty ``` Check relay logs: ```sh journalctl -u example-mail-relay.service -n 100 --no-pager ``` For a normal message, the relay should log an enqueue event and then a delivered event. If local health, sendmail, queue movement, or DKIM signing fails here, fix Steps 3 through 8 before touching Nginx, Cloudflare, or caller-side values. ## Step 11: validate the external entry External validation should be done from the caller's point of view: your laptop, CI, or another backend server. The settings you may need to edit are caller environment variables, the gateway allowlist, Cloudflare Access client values, or the reverse proxy. Postfix is not the first place to look at this stage. Test failure paths before success: ```text No gateway auth: blocked by the gateway Gateway auth but no Bearer token: relay returns 401 Wrong Bearer token: relay returns 401 from not in allowlist: relay returns 400 ``` Then send a complete test: ```sh curl -i https://mail-relay.example.com/send \ -H 'content-type: application/json' \ -H 'Authorization: Bearer <relay-token>' \ --data '{ "to": "[email protected]", "from": {"email": "[email protected]", "name": "Example Support"}, "replyTo": "[email protected]", "subject": "SMTP relay test", "text": "This is a test message." }' ``` If the gateway has its own auth layer, add those headers too. Successful relay response: ```json {"ok":true,"queued":true,"id":"<job-id>"} ``` That only means the mail was queued. Still check the relay log for delivery and `postqueue -p` for backlog. ## Step 12: read the original headers, not just the inbox This step happens in the mailbox, not on the server. Use Gmail "Show original", the raw-message view in your mail provider, or a mail client's source view. Look for: ```text spf=pass [email protected] dkim=pass [email protected] dmarc=pass header.from=example.com ``` Also confirm: ```text The message lands in Inbox, not Spam From is [email protected] Reply-To is [email protected] Return-Path and SPF domain match the plan DKIM selector is the current selector Both HTML and plain text versions are present when you send HTML Link domains are not suspiciously unrelated to the From domain ``` If SPF fails, go back to Step 2 and check the sending IP and SPF record. If DKIM fails, check Step 4 and the DNS public record. If HELO or PTR looks wrong, check Step 2 and Step 3. If the visible mail content or sender is wrong, check Steps 5 through 7. ## Step 13: clean old DNS only after validation Cleanup happens in the DNS console, and sometimes in the provider rDNS/PTR panel. It does not require installing anything on the server. Delete only records that are no longer used by Postfix, DKIM, MX, or business sending. Common cleanup targets: ```text old SPF includes old DKIM selectors old _dmarc.mail.example.com records old sending subdomains ``` After cleanup, send another sample message and repeat Step 12. Do not delete `mail.example.com` if Postfix still uses it as `myhostname` or `smtp_helo_name`. Either keep it, or first move Postfix to a new MTA hostname and update PTR/rDNS. ## Step 14: BIMI and sender avatar come last BIMI is a brand-display item. It is not part of the relay delivery path and does not require software on the relay VPS. The things you edit are DNS and a public BIMI SVG file: ```text default._bimi.example.com TXT "v=BIMI1;l=https://app.example.com/.well-known/bimi/logo.svg" ``` The domain needs DMARC at `p=quarantine` or `p=reject`. Gmail also requires a VMC or CMC certificate for reliable brand-logo display. Without that certificate, you can prepare the SVG, but do not treat the avatar as finished. Use the real brand logo for BIMI. A hand-drawn approximation may pass a quick glance, but it looks wrong once enlarged. ## Mistakes that are easy to make These are not random gotchas. Each one maps back to a layer above. ### Testing only the API 200 A 202 or `{"queued":true}` response means the relay accepted the job. It does not mean the recipient server accepted the mail. Check relay logs, Postfix logs, Postfix queue, and original message headers. ### Letting callers choose any From Accepting arbitrary `From` values breaks sender reputation and DMARC alignment. Keep a short allowlist such as `support`, `noreply`, and `tickets`. ### Passing the body through shell arguments Email bodies can contain HTML, quotes, newlines, and long content. Build a MIME message and write it to sendmail stdin. Do not assemble a shell command with the body inside it. ### Losing jobs in processing When the service restarts, jobs may be left in `processing`. On startup, move those files back to `pending`. Otherwise a crash can strand mail in the middle state. ### Treating gateway auth and Bearer token as one thing The gateway decides who can reach the relay. The Bearer token decides who the relay accepts. Use both when the relay is reachable from outside the VPS. ## Rollback Rollback should follow the dependency order in reverse: stop the external entry, stop the relay, inspect the queue, then touch Postfix, DKIM, and DNS only if needed. Do not start by deleting DNS or removing the queue. If the relay misbehaves: ```sh systemctl stop example-mail-relay.service ``` To temporarily block external callers: ```text revoke the gateway client value disable the reverse proxy or tunnel route remove the caller from the firewall, VPN, or allowlist ``` If Postfix delivery is broken: ```text keep the pending queue fix Postfix / DKIM / DNS restart the relay so the queue can drain ``` If a batch should not send: ```text stop the relay inspect pending / processing move or delete the matching jobs start the relay again ``` Do not delete the whole queue directory in a hurry. Inspect recipients and subjects first, then decide whether to retry, move to `failed`, or delete. ## References - Postfix documentation: local MTA behavior, queues, and the sendmail-compatible interface. - Spamhaus IP and Domain Reputation Checker: `https://check.spamhaus.org/` - Cisco Talos IP and Domain Reputation Center: `https://www.talosintelligence.com/reputation_center` - AbuseIPDB: `https://www.abuseipdb.com/` - Scamalytics IP Fraud Check: `https://scamalytics.com/ip` - Gmail Email sender guidelines: SPF/DKIM, PTR/rDNS, TLS, DMARC, and spam-rate requirements, `https://support.google.com/mail/answer/81126` - Yahoo Sender Best Practices: authentication, reverse DNS, complaints, and message format, `https://senders.yahooinc.com/best-practices/` - DMARC RFC 7489: alignment and `p=quarantine` / `p=reject`, `https://datatracker.ietf.org/doc/html/rfc7489` - SPF RFC 7208: envelope sender authorization, `https://www.rfc-editor.org/rfc/rfc7208` - Cloudflare Tunnel documentation: optional HTTPS entry for a local HTTP relay. - Cloudflare Access service-token documentation: optional machine-to-machine access control. - RFC 5322: message headers such as From, To, Subject, and Message-ID.
Comments