| |

LFCE 63 ๐Ÿง Mail Server โ€” Postfix Basics

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

Postfix is the default Mail Transfer Agent (MTA) on most Linux distributions. Its job is to move mail between servers using SMTP, accepting messages from local users or remote clients and delivering them to the correct destinationโ€”either a local mailbox or another mail server. The previous chapters covered web servers and load balancers; this chapter covers the mail server that handles the SMTP protocol, the configuration file that controls it, and the diagnostic tools that show whether mail is flowing or stuck.

Postfix was designed by Wietse Venema as a secure replacement for Sendmail. It uses a modular architecture: a master daemon launches small, unprivileged processes that each perform a specific task, limiting the damage a compromised process can cause . The configuration lives in /etc/postfix/, and the primary file is main.cf, which contains the global parameters . A working Postfix installation requires only a few parameters to be set correctly: the domain it serves, the hosts it accepts mail for, and the networks it relays for.

Key point: Postfix is an MTA, not a complete mail system. It handles SMTP transport and local delivery, but reading mail requires an MDA like Dovecot for IMAP/POP3. The mydestination parameter defines the domains Postfix accepts mail for locally. The mynetworks parameter defines the clients allowed to relay through it. Without these two settings, Postfix either rejects mail for its own domain or becomes an open relay.


Why Postfix requires careful configuration

The open-relay problem.

An MTA that relays mail for any client is an open relay, and spammers will find it within hours. The mynetworks parameter controls which clients are authorized to relay. By default, Postfix authorizes the IP subnets the machine is attached to . If the server has a public interface and mynetworks includes it, the server becomes an open relay. The parameter must be set to the local network only, or to 127.0.0.0/8 for a server that only accepts mail from local processes.

The local-delivery problem.

Postfix accepts mail for the domains listed in mydestination. If the server’s hostname is mail.example.com and mydestination contains only $myhostname, then mail addressed to user@example.com is rejected because example.com is not in the list. A domain-wide mail server must list $mydomain in mydestination . The distinction between $myhostname (the server’s name) and $mydomain (the parent domain) is the most common source of “Relay access denied” or “User unknown” errors.

The alias problem.

The postmaster address must exist and must point to a human. The alias_maps parameter points to the alias file, typically /etc/aliases or /etc/postfix/aliases. After editing the file, the postalias command must be run to rebuild the lookup table . Without it, the new aliases are not active, and mail to the alias address bounces.


a. The main.cf parameters

The main.cf file contains the global configuration. The parameters that must be set for a basic server are myhostname, mydomain, myorigin, mydestination, mynetworks, and inet_interfaces.

# /etc/postfix/main.cf
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = $myhostname, localhost.$mydomain, $mydomain
mynetworks = 127.0.0.0/8, 192.168.1.0/24
inet_interfaces = all

The myhostname is the server’s fully qualified name. The mydomain is the parent domain. The myorigin is the domain that appears in mail sent from this machine; setting it to $mydomain makes outgoing mail appear to come from example.com rather than mail.example.com . The mydestination lists the domains that Postfix delivers locally. The mynetworks lists the clients allowed to relay. The inet_interfaces controls which interfaces the SMTP server listens on.

After editing main.cf, the service must be reloaded.

sudo postfix reload
# Or: sudo systemctl reload postfix

The postfix reload command re-reads the configuration without dropping the connections that are in progress . A full restart is not necessary for parameter changes.


b. Aliases and local delivery

The alias file maps one address to another or to a list of recipients. The postmaster alias is required, and the root alias is commonly set to forward to a real user.

# /etc/aliases (or /etc/postfix/aliases)
postmaster: root
root: admin@example.com
sysadmin: alice, bob

The postmaster: root line forwards mail for postmaster to the root account. The root: admin@example.com line forwards root’s mail to an external address. The sysadmin: alice, bob line delivers to both local users . After editing, the postalias command rebuilds the table.

sudo postalias /etc/aliases
# Or: sudo newaliases

The postalias command is the Postfix-specific tool. The newaliases command is the Sendmail-compatible alias . Both produce the same result: a database file that Postfix reads when delivering mail.


c. The mail queue and diagnostics

Postfix places every message in a queue before delivery. The queue has five states: incoming, active, deferred, hold, and corrupt . The mailq command shows the messages in the queue, and the postqueue command provides the same information with more options.

# Show the queue
mailq
# Or: postqueue -p

# Force a retry of all deferred messages
sudo postqueue -f

# Delete a specific message
sudo postsuper -d <QUEUE_ID>

# Put a message on hold
sudo postsuper -h <QUEUE_ID>

# Release a held message
sudo postsuper -H <QUEUE_ID>

The mailq output shows the queue ID, the size, the arrival time, the sender, and the recipient. A message that is deferred shows the reason in parentheses, such as “Connection timed out” or “Host not found” . The postqueue -f command flushes the deferred queue and retries every message immediately.

The log file is the primary diagnostic tool. On Debian and Ubuntu, the mail log is /var/log/mail.log; on RHEL and its derivatives, it is /var/log/maillog . The log shows the SMTP conversation, the delivery status, and the reason for any failure.

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

# Find the log entries for a specific queue ID
grep <QUEUE_ID> /var/log/mail.log

The log entries include status=sent, status=deferred, or status=bounced, along with the recipient and the relay. A deferred message that repeatedly shows “Connection refused” indicates that the destination server is unreachable or the port is blocked.


Complete Example Session

# ============================================
# PART 1: INSTALL POSTFIX
# ============================================
# Debian / Ubuntu: the installer prompts for the mail configuration type
sudo apt install postfix
# Select "Internet Site" and enter the system mail name (e.g., example.com)

# RHEL / Rocky
sudo dnf install postfix

# ============================================
# PART 2: CONFIGURE main.cf
# ============================================
sudo tee -a /etc/postfix/main.cf > /dev/null <<'EOF'
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = $myhostname, localhost.$mydomain, $mydomain
mynetworks = 127.0.0.0/8, 192.168.1.0/24
inet_interfaces = all
EOF

# ============================================
# PART 3: CONFIGURE ALIASES
# ============================================
sudo tee -a /etc/aliases > /dev/null <<'EOF'
postmaster: root
root: admin@example.com
EOF
sudo postalias /etc/aliases

# ============================================
# PART 4: RELOAD AND CHECK THE SERVICE
# ============================================
sudo postfix reload
sudo systemctl status postfix
# Output: active (running)

# ============================================
# PART 5: CHECK THE LISTENING PORT
# ============================================
sudo ss -tlnp | grep :25
# Output: LISTEN 0 100 *:25 *:* users:(("master",pid=...))

# ============================================
# PART 6: SEND A TEST MESSAGE
# ============================================
echo "Test message body" | mail -s "Test subject" root

# ============================================
# PART 7: CHECK THE QUEUE
# ============================================
mailq
# The message should be delivered or queued

# ============================================
# PART 8: CHECK THE LOG
# ============================================
sudo tail -5 /var/log/mail.log    # Debian
# Output shows the connection, the delivery, and status=sent

# ============================================
# PART 9: TEST SMTP MANUALLY
# ============================================
telnet localhost 25
# EHLO localhost
# MAIL FROM:<test@example.com>
# RCPT TO:<root@localhost>
# DATA
# Subject: Manual test
#
# Test body
# .
# QUIT

# ============================================
# PART 10: CHECK THE MAILBOX
# ============================================
sudo cat /var/mail/root
# The test messages are in the root mailbox

The ten parts demonstrate the Postfix basics: installation, main.cf configuration, alias setup, reload, port verification, test message, queue inspection, log inspection, manual SMTP conversation, and mailbox check. The critical verification is Part 8: the log shows the delivery status, and a status=sent confirms that Postfix accepted and delivered the message.


Quick Reference

main.cf Parameters

ParameterPurposeExample
myhostnameServer FQDNmail.example.com
mydomainParent domainexample.com
myoriginDomain in outbound mail$mydomain
mydestinationDomains delivered locally$myhostname, $mydomain
mynetworksClients allowed to relay127.0.0.0/8, 192.168.1.0/24
inet_interfacesListening interfacesall or loopback-only

Queue Commands

CommandPurpose
mailqShow the queue
postqueue -pShow the queue (Postfix syntax)
postqueue -fFlush deferred messages
postsuper -d <ID>Delete a message
postsuper -h <ID>Hold a message
postsuper -H <ID>Release a held message

Log Locations

DistributionLog File
Debian / Ubuntu/var/log/mail.log
RHEL / Rocky/var/log/maillog
systemdjournalctl -u postfix

Alias Commands

CommandPurpose
postalias /etc/aliasesRebuild alias database
newaliasesSendmail-compatible equivalent

Best Practices

โœ… Do This:

# Set mydestination to include the domain
mydestination = $myhostname, localhost.$mydomain, $mydomain  # โœ…

# Restrict mynetworks to the local subnet
mynetworks = 127.0.0.0/8, 192.168.1.0/24                    # โœ…

# Set the postmaster alias
postmaster: root                                             # โœ…

# Rebuild the alias table after editing
postalias /etc/aliases                                       # โœ…

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

# Use postfix reload after changing main.cf
postfix reload                                               # โœ…

โŒ Don’t Do This:

# Set mynetworks to any
mynetworks = 0.0.0.0/0    # Open relay                        # โŒ

# Omit the postmaster alias
# Mail to postmaster bounces                                 # โŒ

# Forget to rebuild the alias table
# New aliases are not active                                 # โŒ

# Edit main.cf and restart without reloading
# The changes are not applied                                # โŒ

# Ignore the queue
# Deferred messages pile up and disk fills                   # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Relay access deniedmynetworks does not include the clientAdd the subnet
User unknownmydestination missing the domainAdd $mydomain
Open relaymynetworks includes public interfacesRestrict to local subnets
Alias not workingpostalias not runRebuild the table
Mail stuck in queueDestination unreachable or DNS issueCheck mailq and the log
Service not listeninginet_interfaces set to loopback-onlySet to all

Real-World Examples

1. Basic main.cf

myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = $myhostname, localhost.$mydomain, $mydomain
mynetworks = 127.0.0.0/8, 192.168.1.0/24

2. Postmaster alias

postmaster: root

3. Rebuild aliases

sudo postalias /etc/aliases

4. Reload Postfix

sudo postfix reload

5. Check queue

mailq

6. Flush deferred queue

sudo postqueue -f

7. Delete a message

sudo postsuper -d <QUEUE_ID>

8. Check the mail log

sudo tail -f /var/log/mail.log

9. Test SMTP

telnet localhost 25

10. Send a test message

echo "Test" | mail -s "Test" root

Visual

Postfix Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  POSTFIX IS AN MTA, NOT A COMPLETE MAIL SYSTEM               โ”‚
โ”‚                                                              โ”‚
โ”‚  MUA (Thunderbird)                                           โ”‚
โ”‚       โ”‚ SMTP                                                 โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Postfix (MTA)                                          โ”‚  โ”‚
โ”‚  โ”‚ โ”œโ”€โ”€ accepts mail via SMTP                              โ”‚  โ”‚
โ”‚  โ”‚ โ”œโ”€โ”€ queues it                                         โ”‚  โ”‚
โ”‚  โ”‚ โ”œโ”€โ”€ delivers locally to /var/mail/$USER                โ”‚  โ”‚
โ”‚  โ”‚ โ””โ”€โ”€ relays to remote servers via SMTP                 โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                             โ”‚                                โ”‚
โ”‚                             โ–ผ                                โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ MDA (Dovecot)                                          โ”‚  โ”‚
โ”‚  โ”‚ โ”œโ”€โ”€ reads /var/mail/$USER or ~/Maildir                 โ”‚  โ”‚
โ”‚  โ”‚ โ””โ”€โ”€ serves IMAP/POP3 to the client                     โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚  Postfix moves mail; Dovecot lets users read it.             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

main.cf Parameters

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THE FOUR CRITICAL PARAMETERS                                โ”‚
โ”‚                                                              โ”‚
โ”‚  myhostname = mail.example.com                               โ”‚
โ”‚  โ†’ The server's own name                                    โ”‚
โ”‚                                                              โ”‚
โ”‚  mydomain = example.com                                      โ”‚
โ”‚  โ†’ The parent domain                                        โ”‚
โ”‚                                                              โ”‚
โ”‚  mydestination = $myhostname, $mydomain                      โ”‚
โ”‚  โ†’ Domains Postfix delivers locally                          โ”‚
โ”‚                                                              โ”‚
โ”‚  mynetworks = 127.0.0.0/8, 192.168.1.0/24                    โ”‚
โ”‚  โ†’ Clients allowed to relay                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Mail Queue States

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  POSTFIX HAS FIVE QUEUE STATES                               โ”‚
โ”‚                                                              โ”‚
โ”‚  incoming โ†’ arriving messages                                โ”‚
โ”‚  active   โ†’ being delivered                                  โ”‚
โ”‚  deferred โ†’ temporary failure; will retry                    โ”‚
โ”‚  hold     โ†’ administratively paused                          โ”‚
โ”‚  corrupt  โ†’ unreadable                                       โ”‚
โ”‚                                                              โ”‚
โ”‚  mailq shows the deferred and active queues.                 โ”‚
โ”‚  postqueue -f retries all deferred messages.                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
RoleMail Transfer Agent (MTA)
Configuration/etc/postfix/main.cf
Primary daemonmaster
SMTP port25
Local delivery/var/mail/$USER or ~/Maildir
Domain servedmydestination
Relay clientsmynetworks
Required aliaspostmaster
Queue viewmailq or postqueue -p
Log (Debian)/var/log/mail.log
Log (RHEL)/var/log/maillog
Reloadpostfix reload

Key takeaways:

  • Postfix is an MTA, not a mail client. It moves mail between servers via SMTP and delivers it locally. Reading mail requires an MDA like Dovecot for IMAP/POP3.
  • mydestination defines the local domains. Mail addressed to a domain in this list is delivered to a local mailbox. Mail to any other domain is relayed or rejected. A domain-wide server must list $mydomain .
  • mynetworks defines the relay clients. Clients in this list can send mail through the server to any destination. An incorrect setting makes the server an open relay. The default authorizes the local subnets .
  • The postmaster alias is required. It must point to a human who can receive delivery failure reports. The postalias command rebuilds the alias table after editing .
  • The queue is the diagnostic center. mailq shows the messages, and the log shows the reason for deferral or bounce. A stuck queue is usually a DNS or connectivity problem .
  • postfix reload applies configuration changes. A full restart is not necessary for main.cf changes. The reload re-reads the file without dropping active connections .

Remember: Postfix is the SMTP engine. It accepts mail, queues it, and delivers it either locally or to a remote server. The main.cf file controls the domain it serves, the clients it relays for, and the interfaces it listens on. The mydestination and mynetworks parameters are the two that determine whether the server works correctly or becomes an open relay. The queue and the log are the tools that show what happened to a message. Configure the parameters, reload the service, 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!