| |

LFCE 65 ๐Ÿง Mail Server โ€” Sending and Receiving

โฑ๏ธ Estimated read: 13 min | |

The previous two chapters configured Postfix to move mail and Dovecot to serve it. This chapter covers the end-to-end flow: what happens when a local user sends a message, how Postfix determines whether to deliver it locally or relay it to a remote server, the difference between the SMTP submission port and the SMTP transport port, and the diagnostic tools that show where a message is stuck. The configuration from the previous chapters is assumed to be in place; this chapter exercises it.

A mail system has two directions. Inbound mail arrives from remote servers on port 25, is accepted by Postfix, and is delivered to a local mailbox or forwarded to a Dovecot LMTP socket. Outbound mail is submitted by a local user or an authenticated client, is queued by Postfix, and is relayed to the destination server using DNS MX lookups. The two directions use different ports, different authentication requirements, and different Postfix components.

Key point: Port 25 is the SMTP transport port. It accepts mail from other mail servers, and it must be reachable from the internet for inbound mail to work. Port 587 is the SMTP submission port. It requires authentication and is used by mail clients to send mail through the server. Postfix does not enable port 587 by default; it must be configured with a master.cf entry and an authentication mechanism. The two ports look similar but serve different purposes, and conflating them is the most common source of “cannot send mail” problems.


Why inbound and outbound mail require different configurations

The port-separation problem.

Port 25 is for server-to-server transport. Remote mail servers connect to it to deliver mail for the domains in mydestination. The connection is not authenticated; the server is identified by its IP address and hostname, and the trust is established by DNS and, increasingly, by SPF, DKIM, and DMARC. Port 587 is for client submission. The client authenticates with a username and password, and Postfix accepts the mail for relay to any destination. A client that connects to port 25 and tries to relay to an external address receives a “Relay access denied” error because port 25 is not an authenticated submission port .

The DNS MX problem.

When Postfix has a message for user@remote.com, it queries DNS for the MX record of remote.com. The MX record lists the mail servers that accept mail for the domain, ordered by priority. Postfix connects to the highest-priority server and delivers the message. If the MX lookup fails or the servers are unreachable, the message is deferred and retried according to the maximal_queue_lifetime setting, which defaults to five days . A message that cannot be delivered within that window is bounced to the sender.

The local-delivery problem.

When Postfix has a message for a domain in mydestination, it does not query DNS. It delivers the message to the local mailbox using the local delivery agent or forwards it to Dovecot using the lmtp transport. The mailbox_transport parameter in main.cf selects the delivery method. Without it, Postfix writes to /var/mail/$USER in mbox format, which is not what Dovecot is configured to read if the mailbox format is Maildir .


a. Sending mail from the command line

The mail or mailx command submits a message to the local Postfix instance. Postfix queues it and delivers it according to the recipient’s domain.

# Send a simple message to a local user
echo "Test body" | mail -s "Test subject" root

# Send to a remote address
echo "Test body" | mail -s "Test subject" user@remote.com

# Send with an attachment (mailx)
echo "Body" | mailx -s "Subject" -a /path/to/file.pdf user@remote.com

# Check the queue
mailq

# Check the log
sudo tail -f /var/log/mail.log    # Debian
sudo tail -f /var/log/maillog     # RHEL

The mail command reads the body from stdin and the subject from the -s flag. The message is passed to the local Postfix instance over the sendmail compatibility interface, which is a Unix socket at /var/spool/postfix/public/sendmail. Postfix queues the message and returns immediately; the delivery happens asynchronously . The mailq command shows the queue, and the log shows the delivery status.

The log entry for a successful delivery includes status=sent and the relay. For a deferred message, it includes status=deferred and the reason, such as Connection timed out or Host or domain name not found.

# Find the log entries for a specific recipient
grep "user@remote.com" /var/log/mail.log

b. Configuring the submission port

Port 587 requires a master.cf entry and an authentication mechanism. The submission service is defined in master.cf with the smtpd command and the submission profile.

# /etc/postfix/master.cf
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject

The submission service listens on port 587. The smtpd_tls_security_level=encrypt requires TLS on the connection, which protects the credentials. The smtpd_sasl_auth_enable=yes enables SASL authentication. The smtpd_client_restrictions and smtpd_recipient_restrictions deny clients that are not authenticated . The permit_sasl_authenticated directive is the key: it allows authenticated clients to relay to any destination, while rejecting unauthenticated clients.

The SASL authentication is provided by Dovecot. Postfix delegates the authentication to Dovecot’s SASL socket, which is configured in main.cf.

# /etc/postfix/main.cf
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/postfix/ssl/fullchain.pem
smtpd_tls_key_file = /etc/postfix/ssl/privkey.pem

The smtpd_sasl_type = dovecot tells Postfix to use Dovecot’s SASL implementation. The smtpd_sasl_path = private/auth is the relative path to the socket under the Postfix queue directory. The smtpd_tls_security_level = may allows TLS but does not require it on port 25; the submission port overrides this with encrypt . Dovecot must be configured to provide the SASL socket, which is covered in the next section.


c. The Dovecot SASL socket and LMTP delivery

Dovecot provides two services to Postfix: the SASL authentication socket for submission, and the LMTP socket for local delivery. Both are configured in Dovecot’s 10-master.conf.

# /etc/dovecot/conf.d/10-master.conf
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}

The auth service creates a Unix socket at /var/spool/postfix/private/auth, which is the path that smtpd_sasl_path = private/auth refers to. The socket is owned by the postfix user so that Postfix can connect to it . The lmtp service creates a second socket at /var/spool/postfix/private/dovecot-lmtp, which Postfix uses to deliver mail to Dovecot.

The Postfix side of the LMTP delivery is configured in main.cf with the mailbox_transport parameter.

# /etc/postfix/main.cf
mailbox_transport = lmtp:unix:private/dovecot-lmtp
virtual_transport = lmtp:unix:private/dovecot-lmtp

The mailbox_transport parameter applies to the domains in mydestination, and the virtual_transport applies to the domains in virtual_mailbox_domains. Both point to the same LMTP socket. When Postfix has a message for a local user, it hands the message to Dovecot over LMTP, and Dovecot writes it to the user’s Maildir . This is the integration that makes the mail readable over IMAP.


Complete Example Session

# ============================================
# PART 1: CONFIGURE THE SUBMISSION PORT
# ============================================
sudo tee -a /etc/postfix/master.cf > /dev/null <<'EOF'
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
EOF

# ============================================
# PART 2: CONFIGURE SASL IN POSTFIX
# ============================================
sudo tee -a /etc/postfix/main.cf > /dev/null <<'EOF'
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_tls_security_level = may
EOF

# ============================================
# PART 3: CONFIGURE DOVECOT SASL AND LMTP
# ============================================
sudo tee -a /etc/dovecot/conf.d/10-master.conf > /dev/null <<'EOF'
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}
EOF

# ============================================
# PART 4: CONFIGURE LMTP DELIVERY IN POSTFIX
# ============================================
sudo tee -a /etc/postfix/main.cf > /dev/null <<'EOF'
mailbox_transport = lmtp:unix:private/dovecot-lmtp
virtual_transport = lmtp:unix:private/dovecot-lmtp
EOF

# ============================================
# PART 5: RESTART BOTH SERVICES
# ============================================
sudo systemctl restart dovecot
sudo systemctl restart postfix

# ============================================
# PART 6: VERIFY THE PORTS
# ============================================
sudo ss -tlnp | grep -E ":25|:587|:143"
# Output: 25 (SMTP), 587 (submission), 143 (IMAP)

# ============================================
# PART 7: TEST LOCAL DELIVERY
# ============================================
echo "Local test" | mail -s "Local test" test
# The message is delivered to /home/vmail/test/Maildir/new/

# ============================================
# PART 8: CHECK THE MAILBOX
# ============================================
ls -la /home/vmail/test/Maildir/new/
# Output shows the new message file

# ============================================
# PART 9: TEST SUBMISSION AUTHENTICATION
# ============================================
# Using swaks or openssl s_client to test the submission port
openssl s_client -connect localhost:587 -starttls smtp
# EHLO localhost
# AUTH PLAIN <base64-credentials>
# MAIL FROM:<test@example.com>
# RCPT TO:<user@remote.com>
# DATA
# Subject: Test
#
# Body
# .
# QUIT

# ============================================
# PART 10: CHECK THE LOG
# ============================================
sudo tail -10 /var/log/mail.log
# Output shows the delivery status for the local message

The ten parts demonstrate the complete sending and receiving flow: the submission port configuration, the SASL settings, the Dovecot SASL and LMTP sockets, the LMTP delivery, the service restarts, the port verification, the local delivery test, the mailbox inspection, the submission authentication test, and the log check. The critical verification is Parts 7 and 8: a local message is delivered to the Maildir, which confirms that the LMTP integration is working.


Quick Reference

SMTP Ports

PortPurposeAuthentication
25Server-to-server transportNo (uses DNS and reputation)
587Client submissionYes (SASL)
465Implicit TLS submissionYes (SASL)
143IMAPYes (login)
993IMAPSYes (login over TLS)

Postfix Submission Settings

SettingPurpose
smtpd_sasl_auth_enable = yesEnable SASL
smtpd_sasl_type = dovecotUse Dovecot’s SASL
smtpd_sasl_path = private/authSocket path
smtpd_tls_security_level = encryptRequire TLS on submission
permit_sasl_authenticated,rejectAllow authenticated, reject others

Delivery Transports

SettingPurpose
mailbox_transportDelivery for mydestination
virtual_transportDelivery for virtual domains
lmtp:unix:private/dovecot-lmtpDovecot LMTP socket

Diagnostic Commands

CommandPurpose
mailqShow the queue
postqueue -fFlush the queue
tail -f /var/log/mail.logFollow the mail log
grep <recipient> /var/log/mail.logFind a message’s status
openssl s_client -connect :587 -starttls smtpTest submission

Best Practices

โœ… Do This:

# Enable the submission port with SASL and TLS
submission inet n - y - - smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes                              # โœ…

# Use Dovecot for SASL authentication
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth                               # โœ…

# Deliver local mail via LMTP to Dovecot
mailbox_transport = lmtp:unix:private/dovecot-lmtp           # โœ…

# Check the log after sending a test message
tail -f /var/log/mail.log                                    # โœ…

# Flush the queue after fixing a relay issue
postqueue -f                                                 # โœ…

โŒ Don’t Do This:

# Allow unauthenticated relay on port 25
smtpd_recipient_restrictions = permit_mynetworks, permit     # โŒ

# Use port 25 for client submission
# Clients receive "Relay access denied"                     # โŒ

# Skip TLS on the submission port
smtpd_tls_security_level = none    # Credentials in cleartext # โŒ

# Forget to restart Dovecot after changing its sockets
# The SASL socket is not created                               # โŒ

# Ignore deferred messages
# The queue grows and disk fills                               # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Relay access deniedClient not authenticated on port 25Use port 587 with SASL
Cannot connect to submission portmaster.cf entry missingAdd the submission service
Authentication failsDovecot SASL socket not createdRestart Dovecot; check permissions
Local mail not in MaildirNo mailbox_transportSet it to the LMTP socket
Messages deferredDNS or destination server issueCheck the log; postqueue -f
Port 587 not listeningPostfix not reloadedpostfix reload

Real-World Examples

1. Send a local message

echo "Body" | mail -s "Subject" root

2. Send a remote message

echo "Body" | mail -s "Subject" user@remote.com

3. Check the queue

mailq

4. Follow the log

tail -f /var/log/mail.log

5. Find a message’s status

grep "user@remote.com" /var/log/mail.log

6. Enable submission

submission inet n - y - - smtpd

7. SASL configuration

smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth

8. LMTP delivery

mailbox_transport = lmtp:unix:private/dovecot-lmtp

9. Test submission

openssl s_client -connect localhost:587 -starttls smtp

10. Flush the queue

postqueue -f

Visual

Inbound and Outbound Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TWO DIRECTIONS, TWO PORTS                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  INBOUND (port 25):                                          โ”‚
โ”‚  Remote server โ”€โ”€โ–ถ Postfix:25 โ”€โ”€โ–ถ LMTP โ”€โ”€โ–ถ Dovecot โ”€โ”€โ–ถ Maildirโ”‚
โ”‚                                                              โ”‚
โ”‚  OUTBOUND (port 587):                                        โ”‚
โ”‚  Mail client โ”€โ”€โ–ถ Postfix:587 (SASL) โ”€โ”€โ–ถ Queue โ”€โ”€โ–ถ MX lookup  โ”‚
โ”‚       โ””โ”€โ”€โ–ถ Remote server:25                                  โ”‚
โ”‚                                                              โ”‚
โ”‚  LOCAL DELIVERY:                                             โ”‚
โ”‚  Local user โ”€โ”€โ–ถ Postfix (sendmail) โ”€โ”€โ–ถ LMTP โ”€โ”€โ–ถ Dovecot      โ”‚
โ”‚                                                              โ”‚
โ”‚  Port 25 is unauthenticated transport.                       โ”‚
โ”‚  Port 587 is authenticated submission.                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Postfix-Dovecot Integration

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TWO SOCKETS CONNECT POSTFIX AND DOVECOT                     โ”‚
โ”‚                                                              โ”‚
โ”‚  /var/spool/postfix/private/auth                             โ”‚
โ”‚  โ”œโ”€โ”€ Created by Dovecot's `service auth`                     โ”‚
โ”‚  โ”œโ”€โ”€ Used by Postfix for SASL authentication                 โ”‚
โ”‚  โ””โ”€โ”€ Referenced by `smtpd_sasl_path = private/auth`          โ”‚
โ”‚                                                              โ”‚
โ”‚  /var/spool/postfix/private/dovecot-lmtp                     โ”‚
โ”‚  โ”œโ”€โ”€ Created by Dovecot's `service lmtp`                     โ”‚
โ”‚  โ”œโ”€โ”€ Used by Postfix for local delivery                      โ”‚
โ”‚  โ””โ”€โ”€ Referenced by `mailbox_transport`                       โ”‚
โ”‚                                                              โ”‚
โ”‚  Both sockets are owned by the postfix user.                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Message Lifecycle

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  FROM SUBMISSION TO DELIVERY                                 โ”‚
โ”‚                                                              โ”‚
โ”‚  1. Client submits to port 587 with SASL                     โ”‚
โ”‚  2. Postfix authenticates via Dovecot SASL socket            โ”‚
โ”‚  3. Postfix queues the message                               โ”‚
โ”‚  4. Postfix looks up MX for the recipient's domain           โ”‚
โ”‚  5. Postfix connects to the destination on port 25           โ”‚
โ”‚  6. Destination server accepts or rejects                    โ”‚
โ”‚     โ”œโ”€โ”€ Accept โ†’ status=sent                                 โ”‚
โ”‚     โ””โ”€โ”€ Reject โ†’ status=deferred or bounced                  โ”‚
โ”‚                                                              โ”‚
โ”‚  Local delivery bypasses steps 4-6 and uses LMTP.            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Inbound port25 (SMTP transport)
Submission port587 (authenticated)
Implicit TLS port465 (submissions)
SASL providerDovecot
SASL socket/var/spool/postfix/private/auth
LMTP socket/var/spool/postfix/private/dovecot-lmtp
Local transportmailbox_transport
Virtual transportvirtual_transport
Queue viewmailq
Log (Debian)/var/log/mail.log
Log (RHEL)/var/log/maillog
Flush queuepostqueue -f

Key takeaways:

  • Port 25 and port 587 serve different purposes. Port 25 is for server-to-server transport and is not authenticated. Port 587 is for client submission and requires SASL authentication. A client that connects to port 25 and tries to relay receives “Relay access denied” .
  • Postfix delegates authentication and delivery to Dovecot. The SASL socket at private/auth handles authentication, and the LMTP socket at private/dovecot-lmtp handles local delivery. Both sockets are created by Dovecot and owned by the postfix user .
  • The mailbox_transport parameter routes local mail to Dovecot. Without it, Postfix writes to /var/mail/$USER in mbox format, which Dovecot is not configured to read if the mailbox format is Maildir.
  • The MX lookup determines the outbound destination. Postfix queries DNS for the recipient’s MX record and connects to the highest-priority server. A failed lookup or unreachable server causes the message to be deferred and retried according to maximal_queue_lifetime .
  • The log is the diagnostic center. A successful delivery shows status=sent, a temporary failure shows status=deferred with a reason, and a permanent failure shows status=bounced. The queue and the log together show where a message is stuck.
  • postqueue -f retries all deferred messages. After fixing a relay or DNS issue, the flush command retries the queued messages immediately instead of waiting for the next retry interval.

Remember: Sending and receiving are two flows through the same Postfix instance. Inbound mail arrives on port 25 and is delivered to Dovecot over LMTP. Outbound mail is submitted on port 587 with SASL authentication and relayed to the destination’s MX. Local mail takes the same LMTP path as inbound. The SASL and LMTP sockets are the integration points, and the log is the tool that shows what happened. Configure the submission port, enable SASL, set the LMTP transport, restart both services, send a test message, and check the log.


Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!