LPIC-2 E-Mail Services: Postfix and Dovecot
- Explain the MTA / MDA / IMAP-server split and where Postfix and Dovecot each sit
- Set Postfix's identity in main.cf with postconf -e: myhostname, mydestination, mynetworks, and relayhost
- Apply alias and map-file changes correctly with newaliases and postmap
- Recognise and avoid an open relay caused by a loose mynetworks
- Configure Dovecot for IMAP/POP3 and make Postfix and Dovecot agree on Maildir and its path
Why mail needs two different programs
Understand the MTA / MDA / IMAP-server split before touching any config.
Delivering email is two separate jobs, and Linux splits them across two daemons. Mixing them up is the root cause of most "my mail server is broken" tickets, so get the map straight first.
Postfix is the MTA (Mail Transfer Agent). Its job is to move messages between servers over SMTP — accepting mail addressed to your domain and handing your users' outbound mail onward to the internet.
Dovecot is the MDA / mailbox server. It does local delivery (writing an accepted message into a user's mailbox) and then serves that mailbox back to mail clients over IMAP and POP3 so people can read it.
Think of a town's postal system. Postfix is the sorting office: trucks (SMTP) arrive and leave, letters get routed toward the right neighbourhood. Dovecot is the part that drops the letter into your mailbox and lets you open it (IMAP) to read what's inside. A letter that reaches the sorting office but never lands in a mailbox you can open has gone exactly nowhere useful — which is why these two have to cooperate.
Check yourself
A user can receive mail but cannot read it in their IMAP client. Which daemon's configuration is the more likely culprit: Postfix or Dovecot?
Give Postfix its identity in main.cf
Set myhostname, mydestination, and mynetworks the safe way with postconf -e.
Postfix's behaviour lives in
/etc/postfix/main.cf. Before it can accept or route a single message it has to know who it is and what it is responsible for. Three parameters answer that:myhostname— this server's fully-qualified name, e.g.mail.example.com.mydestination— the list of domains for which this box is the final destination (mail for these is delivered locally, not relayed onward).mynetworks— the IP ranges Postfix trusts to relay through it without authenticating. This is a security control, covered in depth in the next step.
You can edit
main.cfby hand, but the safe, scriptable, exam-preferred way ispostconf -e("edit"). It rewrites the parameter in place, preserving formatting and avoiding the classic duplicate-key mistake.Example — not runnable on your lab box
sudo postconf -e 'myhostname = mail.example.com' 'mydestination = $myhostname, localhost, example.com'- -e
- Edit: set (or replace) the named parameter in main.cf in place. Silent on success.
Two parameters set in one call. Success is silent — no output means it worked. mydestinationuses$myhostname(Postfix expands$namereferences to other parameters) plusexample.com, declaring this server the final stop for mail to that domain. Verify any value afterward with a read-onlypostconf myhostname.Outbound mail is the other half of identity. By default Postfix tries to deliver directly to each recipient's MX server. On many networks that's blocked (residential IPs, port-25 egress filtering), so you point Postfix at a smarthost with
relayhost— an upstream mail server that accepts your outbound mail and forwards it for you.Example — not runnable on your lab box
sudo postconf -e 'relayhost = [smtp.example.com]:587'relayhostnames the smarthost for all outbound mail. The square brackets tell Postfix "use this host literally — do not look up its MX record"; without them Postfix would resolve the domain's MX, which is usually not what you want for a named relay.:587selects the submission port.Check yourself
Which main.cf parameter lists the domains for which this server is the final delivery destination (delivered locally rather than relayed onward)?
mynetworks and the open-relay trap
Keep mynetworks tight so your server never becomes an open relay.
An open relay is a mail server that will accept mail from anyone and forward it to anyone — no authentication required. Spammers hunt for these constantly, because relaying through your server hides their origin and burns your IP reputation instead of theirs. Within hours of going open, your domain lands on blocklists and your legitimate mail stops being delivered anywhere.
The single parameter that most often causes this is
mynetworks. It lists the client IP ranges Postfix trusts to relay outbound mail without logging in. Set it too wide and you've handed the keys to the internet.Example — not runnable on your lab box
sudo postconf -e 'mynetworks = 127.0.0.0/8, 192.0.2.0/24'A tight mynetworks: loopback (127.0.0.0/8) so the local host can send, plus one documentation subnet (192.0.2.0/24) standing in for your own internal LAN. Everything not in this list must authenticate (SASL) to send mail through you. Remote users who roam should authenticate on the submission port (587), not be added here by IP.Example — not runnable on your lab box
postconf mynetworksmynetworks = 127.0.0.0/8, 192.0.2.0/24
postconfwith no `-e` reads a parameter — the safe, non-destructive way to confirm what's actually in effect. Always read backmynetworksafter changing it: this one value is the difference between a healthy server and a blocklisted one.Check yourself
What is the name for a mail server that accepts and forwards mail from anyone to anyone without authentication — the thing a too-wide mynetworks creates?
Aliases and maps: make edits actually take effect
Run newaliases after /etc/aliases and postmap after editing hash map files.
Postfix reads several lookup tables —
/etc/aliases(forwarding rules likepostmaster: root), plus optional maps such astransportandaccess. For speed, Postfix doesn't read these text files at delivery time; it reads a compiled binary built from them. So editing the text file alone changes nothing until you rebuild that binary.This is the most common "I changed it and nothing happened" trap in the whole topic. Two commands rebuild the two kinds of table.
Example — not runnable on your lab box
sudo newaliasesRun after editing `/etc/aliases`. It compiles the text aliases into the indexed /etc/aliases.dbthat Postfix actually consults. Silent on success. Skip it and your newpostmaster:or user-forwarding alias is simply ignored — mail keeps going to the old target.Example — not runnable on your lab box
sudo postmap /etc/postfix/transportRun after editing any hash/btree map file — here transport(per-destination routing).postmapbuildstransport.dbnext to the source file. The same applies toaccess,canonical, and friends: edit the text file, thenpostmapit, or the change never loads.newaliasesis really just a specialised shortcut forpostmapon the aliases file.Check yourself
You added a forwarding entry to /etc/aliases but mail still goes to the old address. Which command did you most likely forget to run?
Dovecot: serving mailboxes and choosing Maildir
Configure Dovecot for IMAP/POP3, prefer Maildir, force TLS, and know where Sieve fits.
Once Postfix has accepted a message, Dovecot takes over: it stores the mail and serves it back to clients over IMAP (keeps mail on the server, syncs across devices) and POP3 (typically downloads and removes). Two storage formats exist, and the choice matters:
- mbox — one big file per mailbox, every message concatenated together. Simple, but the whole file is locked during writes, so concurrent delivery and reading collide and it scales poorly.
- Maildir — one file per message inside
new/,cur/, andtmp/directories. No global lock, safe for concurrent access, and a corrupted message can't take the whole mailbox down with it.
Prefer Maildir. It's the modern default for exactly these reliability reasons.
Example — not runnable on your lab box
doveconf -n | grep -E 'mail_location|ssl|protocols'protocols = imap pop3 ssl = required mail_location = maildir:~/Maildir
- -n
- Print only settings that differ from the built-in defaults — the concise, real configuration.
doveconf -nis Dovecot's equivalent ofpostconf: it shows the effective config without the noise of every default. Three lines to read:protocolsenables IMAP and POP3;ssl = requiredforces TLS so credentials and mail are never sent in clear text; andmail_location = maildir:~/Maildirsets Maildir format under each user's home. Note that path — the next step hinges on it.Check yourself
Which mailbox storage format stores one message per file across new/cur/tmp directories and avoids the global locking that hurts concurrency?
The handshake: Postfix and Dovecot must agree
Ensure both daemons use the same mailbox format and path so delivered mail appears to clients.
Here is the failure that wastes the most hours, because nothing errors out. Postfix delivers a message to its idea of the mailbox; Dovecot serves the client from its idea of the mailbox. If those two locations (or formats) differ, the mail is written successfully and then never appears in the client — it looks like messages are vanishing into thin air.
Concretely: if Postfix delivers to mbox at
/var/mail/$USERwhile Dovecot reads Maildir at~/Maildir, every message lands in a place Dovecot never looks. No bounce, no log error on the read side — just an empty inbox.The fix is to make them agree: same format (Maildir) at the same path.
Example — not runnable on your lab box
postconf home_mailbox; doveconf -n | grep mail_locationhome_mailbox = Maildir/ mail_location = maildir:~/Maildir
Read both sides at once and compare. Postfix's home_mailbox = Maildir/means it delivers to~/Maildir/in Maildir format (the trailing/is what selects Maildir over mbox). Dovecot'smail_location = maildir:~/Maildirreads Maildir from the same~/Maildir. Format matches, path matches — so a message Postfix delivers is exactly where Dovecot looks. That agreement is the whole point of this lesson.Check yourself
Postfix's log says delivery succeeded, but the user's IMAP client shows no new mail and there are no errors. What single root cause should you check first?
Try it in your live terminal
Inspect a mail stack on the real sandbox — read-only, safe whether or not a server is installed.
You have a real root shell to your right. These commands are read-only inspection — they change nothing. A mail server may or may not be installed on this box; that's fine. The goal is to practise the moves you'd use to audit any mail host: confirm Postfix's identity, check its relay trust, and see which mail ports are actually listening.
postconf mail_version 2>/dev/null || echo 'no postfix'Probe for Postfix without erroring out. If it's installed you get the version; if not, the ||branch printsno postfixinstead of a scary command-not-found. A safe first question on any unknown host.postconf mynetworks 2>/dev/null || echo 'no postfix'The single most important value to audit: who is trusted to relay. If this includes anything wider than loopback and subnets you own, you may be looking at an open relay. 2>/dev/nullhides the error when Postfix is absent.ss -tlnp | grep -E ':25|:143|:993' || trueShow listeners on the mail ports: 25 (SMTP, Postfix), 143 (IMAP) and 993 (IMAPS, Dovecot). || truekeeps the step from failing if nothing matches. Seeing 25 served bymaster(Postfix) and 143/993 bydovecotconfirms the two-daemon split you learned, live on the box.
- Postfix is the MTA (moves mail between servers over SMTP); Dovecot is the MDA + IMAP/POP3 server (delivers locally and serves mailboxes). Match the symptom to the right daemon.
- Set Postfix identity with
postconf -e(myhostname,mydestination,mynetworks);relayhostis the smarthost for outbound.postconf -ereplaces in place — no duplicate keys. - Keep
mynetworkstight — loopback plus subnets you own — or you become an open relay and land on blocklists. - Edit a Postfix lookup file → rebuild its
.db:newaliasesafter/etc/aliases,postmapaftertransport/access. A reload alone won't do it. - Prefer Maildir over mbox, force TLS, use Sieve for server-side filtering — and make Postfix and Dovecot agree on format and path, or delivered mail silently never appears.
| Command or setting | Belongs to | Effect |
|---|---|---|
postconf -e 'myhostname = ...' | Postfix | Set the server's identity in main.cf, in place |
mydestination | Postfix | Domains delivered locally (final destination) |
mynetworks | Postfix | IPs trusted to relay — keep TIGHT to avoid an open relay |
relayhost = [host]:587 | Postfix | Smarthost for outbound mail (brackets = no MX lookup) |
newaliases | Postfix | Compile /etc/aliases into aliases.db (run after editing) |
postmap file | Postfix | Compile a hash map (transport/access) into file.db |
mail_location = maildir:~/Maildir | Dovecot | Serve mailboxes in Maildir format at a set path |
ssl = required | Dovecot | Force TLS for IMAP/POP3 |
| Format + path agreement | Both | Postfix delivers where Dovecot reads, or mail 'vanishes' |
