Route Postfix Mail Safely with smtp and LMTP

Mail stuck in the queue with a vague "connection failed" usually means the route is wrong, and smtp is where that route actually lives. You will finish with a checked Postfix delivery route for either a remote SMTP destination or a local LMTP service. The installed package here is Postfix 3.8.6-1ubuntu0.1, and the smtp(8postfix) and lmtp(8postfix) entries describe one SMTP+LMTP client. Allow 20 to 30 minutes for a read-only inspection, or longer if you need to edit a transport and wait for a test message.

This guide assumes Postfix is already installed and that you can read its configuration. Ordinary inspection commands do not need elevated privileges. Editing main.cf or master.cf, reloading Postfix and reading system mail logs usually require root or suitable group access. The examples do not send mail or change service state until a step says so.

1. Confirm the installed client and its defaults

Start by checking the package and the active configuration directory:

$ dpkg-query -W -f='${Package} ${Version}\n' postfix
postfix 3.8.6-1ubuntu0.1
$ postconf config_directory
/etc/postfix

The exact package revision can differ between machines. The important distinction is that this client is normally started by Postfix's master(8) process for queued delivery; it is not a general-purpose command-line SMTP sender. Do not try to feed a message to smtp directly.

Checkpoint: Inspect the values that control the route before changing anything:

$ postconf smtp_host_lookup smtp_tcp_port lmtp_tcp_port smtp_tls_security_level smtp_sasl_auth_enable
smtp_host_lookup = dns
smtp_tcp_port = smtp
lmtp_tcp_port = 24
smtp_tls_security_level =
smtp_sasl_auth_enable = no

Blank TLS security level means the local default is not forcing a client TLS policy. It does not mean that every remote server is unsafe, and it does not make an SMTP connection encrypted by itself. Treat remote TLS requirements as a separate policy decision.

2. Choose the destination syntax

For SMTP, a bare domain uses its MX records. A host in square brackets skips MX lookup and resolves that host directly. A port can follow the domain or bracketed host:

example.net
example.net:2525
[mail.example.net]
[mail.example.net]:587
[203.0.113.25]:25
[ipv6:2001:db8::25]:25

These values are normally the next-hop destination in a transport map or service definition, not arguments for an interactive smtp invocation. Postfix 3.5 and later accept multiple SMTP destinations separated by commas or whitespace. The installed 3.8 client therefore supports that form, but keep a production route easy to read and test.

For LMTP, use a UNIX socket or an explicit TCP form:

unix:/run/dovecot/lmtp
inet:127.0.0.1
inet:127.0.0.1:24
inet:[ipv6:2001:db8::24]:24

With inet: and no port, Postfix first uses the lmtp service from services(5), then falls back to lmtp_tcp_port, whose default is 24. A UNIX socket path is interpreted relative to the Postfix queue directory when the client is chrooted. That detail is a common cause of a route that looks correct but cannot connect.

3. Inspect the transport that will use the client

Find the relevant transport and its destination before editing it. These commands are read-only:

$ postconf -M smtp
smtp       inet  n       -       y       -       -       smtpd
$ postconf -M lmtp
lmtp       unix  -       -       y       -       -       lmtp

The output is site-specific. The first column is the master.cf service name and the final field selects the delivery client. A custom transport may still use the same smtp program with a service name such as relay; its per-service setting is then named from that service. Do not infer the name from a queue ID.

When a delivery service must add Delivered-To: or X-Original-To:, it must deliver one recipient per request. In main.cf, the setting is named after the transport service:

relay_destination_recipient_limit = 1

This is a configuration change. Before applying it, check that the service is really called relay and that the receiving system expects those headers. To undo this example, remove the line or restore its previous value, then reload Postfix.

4. Set security policy before enabling delivery

SMTP authentication and TLS are independent controls. SASL authentication is disabled by default in this client. If a relay requires it, the relevant settings include smtp_sasl_auth_enable and smtp_sasl_password_maps. Credentials belong in a protected map file, not in a public transport map or shell history.

For TLS, choose the security level deliberately. An opportunistic policy may use STARTTLS when offered, while a mandatory policy refuses delivery if encryption cannot be established. The manpage identifies smtp_tls_security_level and smtp_tls_policy_maps as the client controls. Review the installed TLS documentation and the receiving service's certificate name before enabling a mandatory policy.

Warning: Changing TLS or SASL settings can stop queued mail, or expose credentials if the policy is wrong. Make a backup of the specific file and keep the old value available:

# cp -p /etc/postfix/main.cf /etc/postfix/main.cf.before-smtp-route
# postconf -n | grep -E '^(smtp|relay|lmtp)_(tls|sasl)'

The backup is local recovery material. Do not paste its contents into a ticket or commit it to a repository.

5. Apply and reload a deliberate change

After editing the required Postfix configuration as an administrator, ask Postfix to validate and reload it:

# postfix check
# postfix reload
postfix/postfix-script: refreshing the Postfix mail system

postfix check checks the installation and permissions before the reload. The reload affects new client processes; the smtp manpage says configuration changes are picked up automatically because these processes have limited lifetimes, and that reload speeds up the change. It does not rewrite or resend every queued message.

Checkpoint: Confirm the effective value after the reload:

# postconf smtp_tls_security_level smtp_sasl_auth_enable
smtp_tls_security_level = may
smtp_sasl_auth_enable = yes

Your output will reflect your policy. If the value is not what you intended, stop before sending mail. Restore the saved file or edit only the incorrect line, run postfix check again, and reload.

6. Verify delivery from the queue and logs

Use the normal Postfix queue workflow to test a route. Send one harmless test message through the application or local submission path that normally creates the mail. Do not use an invented direct invocation of smtp. Then inspect the queue:

$ postqueue -p
-Queue ID-  --Size-- ----Arrival Time---- -Sender/Recipient-------
<queue output is site-specific>

A queue entry can remain while delivery is deferred. That is not proof of permanent failure. The smtp client records transactions and problems through syslogd(8) or postlogd(8). On a system using systemd journal, an administrator can inspect recent Postfix records with:

# journalctl -u postfix --since '10 minutes ago'

Look for the queue ID, destination, response code and whether the result is delivered, deferred or bounced. A DNS failure, connection timeout, rejected TLS policy or refused authentication needs a different fix. Do not repeatedly flush a queue while the log still shows a deterministic configuration error.

7. Recover without losing queued mail

For a temporary network or remote-server problem, leave the message queued and correct the cause. The smtp client is designed to try alternate MX hosts for unreachable or recoverable failures, and Postfix records deferred delivery for a later attempt. After correcting configuration, a controlled queue run is usually enough:

# postqueue -f

Recovery: This requests a queue run; it does not repair DNS, create a TLS certificate or bypass a recipient server's policy. If the message must not be delivered, stop and use the normal Postfix queue-management procedure for your local policy. Avoid deleting queue files by hand: that is destructive and can remove the only queued copy.

For a route that points at the wrong socket or host, restore the previous transport or destination from your backup, run postfix check, reload, and confirm with postconf. Keep the failed queue ID and its log lines until you know whether the message was later delivered, deferred again or returned.

Done means