When Port 25 Is Blocked: Configuring Postfix to Relay Through a Smarthost

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Many hosting networks and residential ISPs drop outbound traffic on TCP port 25, and the block is silent: Postfix connects, waits for a greeting that never arrives, and the message sits in maillog until the queue manager retries and eventually bounces it after five days. If you see connect to mx.example.com[203.0.113.5]:25: Connection timed out repeated for every destination, the port is blocked upstream and no amount of Postfix tuning will change the outcome.

The fix is to relay everything through a smarthost that listens on a submission port you can reach — usually 587 with STARTTLS, sometimes 465 with implicit TLS. This walkthrough configures relayhost and SASL credentials, keeps sender rewriting honest, and shows how to drain the existing queue without losing messages. The machine only needs to reach one host on one port, which any reasonable KVM virtual server with unrestricted outbound 587 can do.

Confirm the block before changing config

Test the outbound path from the same host Postfix runs on, with the same source address. A working telnet or nc to an MX on 587 tells you the network allows submission; a timeout on 25 while 587 opens proves the filter. Also check whether 25 is blocked only outbound or in both directions, because inbound mail requires a listening daemon that the provider may also restrict.

nc -zv -w5 smtp.relay.example.net 587
nc -zv -w5 smtp.relay.example.net 465
nc -zv -w5 alt4.gmail-smtp-in.l.google.com 25

postconf -n | grep -E 'relayhost|smtp_sasl|smtp_tls'
mailq | tail -1

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

relayhost syntax and the port that matters

Set relayhost with an explicit port. Without one Postfix assumes 25 and you end up right back in the black hole. Bracket the hostname so Postfix does not attempt MX lookups — [smtp.relay.example.net]:587 means “this exact host, this exact port”. Bracket syntax is the difference between a working relay and a DNS recursion loop on a host with no outbound resolver path.

For submission on 587 you also need smtp_use_tls = yes and STARTTLS enabled on the relay. If the provider offers 465 instead, use smtp_tls_wrappermode = yes with port 465; that is not STARTTLS, it is TLS from the first byte, and mixing the two settings produces a handshake that never completes. Ask the provider which one they support rather than guessing.

postconf -e 'relayhost = [smtp.relay.example.net]:587'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtp_tls_security_level = encrypt'
postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options = noanonymous'
postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Storing credentials without leaving them world-readable

The password map is a plain text file plus a Berkeley DB hash. Create it with mode 600, run postmap, and delete nothing until postfix reload succeeds, because Postfix reads the .db at startup and an empty or stale map produces “SASL authentication failed; cannot authenticate to server” with no further detail. The relay host key must match the bracket form you used in relayhost.

printf '%s\n' '[smtp.relay.example.net]:587 [email protected]:APPPASSWORD' \
  > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd.db
postfix check && postfix reload

Use an application-specific password, not the mailbox password. If the provider supports IP allowlists for relay use, add your server’s current address; that limits the blast radius of a leaked credential to one host instead of the whole account. Note the address only changes when you rebuild the server, which is another reason to keep its network configuration simple.

Sender rewriting so the relay accepts your mail

Most relays reject envelope senders that do not belong to the authenticated account with a 553 or 550. If your application sends as noreply@yourdomain, either the relay must allow it or you must rewrite it. Set sender_canonical_maps for targeted rewrites and smtp_generic_maps for a blanket envelope rewrite; keep the header From intact where the relay permits it.

MapRewritesUse when
sender_canonical_mapsEnvelope senderRelay requires your account address
smtp_generic_mapsEnvelope + headerRelay rejects mismatched From
canonical_mapsAll addressesAvoid unless certain

Draining the stuck queue

Messages already deferred are still addressed to their original MX on port 25, so Postfix will keep retrying them on the old path even after the relay is configured — unless relayhost applies, which it does for all outbound mail by default. Flush with postqueue -f and watch the log for a burst of relay=smtp.relay.example.net lines. Anything that does not move is usually a sender the relay rejects.

For a specific message, postcat -q QUEUEID shows the headers and the recipient, and postsuper -r QUEUEID requeues it with a fresh timestamp so it jumps to the front. Do not delete deferred mail before reading the queue reason; a 550 from the relay is permanent and retrying will never help, while a 451 is transient and worth one more attempt.

mailq
postqueue -f
grep 'relay=' /var/log/mail.log | tail -20
postqueue -p | grep -c '^(connect to'

Verifying the path end to end

Send a test through a local submission port rather than straight to Postfix’s sendmail binary, so you exercise the same path your application uses. Then check that the Received chain shows the relay, that SPF for your domain passes at the destination, and that the message is not marked as spam. A message that leaves cleanly but lands in a spam folder points at authentication records, not at the relay.

Keep the local Postfix listening only on loopback unless you genuinely accept inbound mail. inet_interfaces = loopback-only plus a firewall that drops 25 inbound is the default posture for a relay-only host, and it eliminates a whole class of open-relay incidents. Confirm with ss -lntp | grep :25 that nothing is bound to a public address.

When the relay itself is throttled

Relay providers impose rate limits, often 100 to 500 messages per hour on shared plans. Exceeding them yields 421 or 450 responses, and Postfix will keep deferring. Tune smtp_destination_rate_delay and default_destination_recipient_limit to stay under the limit rather than letting the provider apply backpressure for you.

Monitor the deferred count rather than individual bounces; a steady rise over ten minutes means you are being throttled or the credential expired. Setting maximal_queue_lifetime = 1d for transactional mail avoids sending stale password resets five days later. With the relay configured and the queue monitored, a port-25-blocked host behaves like any other mail sender on a standard KVM VPS.

Leave a Reply