Dissecting where email lives: the difference between mbox and Maildir, the anatomy of the cur, new, and tmp directories, configuring mail_location in Dovecot, then connecting Postfix to LMTP via virtual_transport so every message is delivered correctly to the mailbox.

After webmail came alive in episode 8, it's time to go down to the storage level: where does email actually live? The answer is in the mailbox — and how the mailbox is organized determines reliability, speed, and ease of backup.
This episode dissects Maildir, the modern storage format used by our architecture, compares it with mbox, then connects Postfix to Dovecot via LMTP so every inbound message is placed in the right mailbox. By the end of the episode, you'll be able to trace one email from the Postfix queue to a file on disk.
There are two dominant mailbox storage formats:
Maildir is the choice of modern architectures. Backup, migration, and concurrent IMAP access are all safer. Dovecot and Postfix both support it natively.
A Maildir is a directory containing three subdirectories with distinct roles:
Maildir/
├── new/ <- new messages, not yet read by the client
├── cur/ <- messages already "seen" by the client, stores flags
└── tmp/ <- temporary write area, corruption-proofA message's lifecycle: written to tmp/ with a unique name → moved to new/ → after the client reads it (in IMAP context, after seeing the listing), moved to cur/ with a status marker like :2, at the end of the filename. That marker is why Maildir filenames always end with a colon and digits — never "tidy up" these filenames manually.
In episode 4 we set the mailbox location. Now we refine it for virtual users:
doveadm config set mail_location 'maildir:/var/mail/vhosts/%d/%n'
sudo dovecot reloadThe doveadm config set command is the official way to change configuration without editing files — the equivalent of postconf -e in Postfix. The value above places each user's Maildir under /var/mail/vhosts/<domain>/<user>/. Verify the active value:
doveconf mail_locationMake sure the parent directory has the correct ownership — everything is held by the vmail user:
sudo chown -R vmail:vmail /var/mail/vhosts
sudo chmod 700 /var/mail/vhostsPermission 700 means only the vmail user gets in — clients never access the files directly, only via IMAP.
LMTP (Local Mail Transfer Protocol) is SMTP's sibling designed specifically for local delivery. The differences from regular SMTP:
Dovecot provides an LMTP service via a Unix socket, and Postfix calls it for every inbound message. Enable the listener in the Dovecot configuration (conf.d/20-lmtp.conf):
protocol lmtp {
mail_plugins = sieve
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}Notice: the socket is placed inside the Postfix chroot directory (/var/spool/postfix/private/), so Postfix can reach it without leaving chroot. This is a pattern worth remembering.
In episode 7 we used virtual_transport = virtual, meaning Postfix delivered via the internal virtual program. Now we switch to LMTP:
sudo postconf -e 'virtual_transport = lmtp:unix:private/dovecot-lmtp'
sudo postconf -e 'mailbox_transport = lmtp:unix:private/dovecot-lmtp'
sudo postfix check
sudo postfix reloadWhat the two parameters do:
virtual_transport — the transport for email to virtual domains; now via LMTP.mailbox_transport — the transport for system users (fallback), also pointed at LMTP so all paths stay consistent.Info
The name private/dovecot-lmtp is relative to the Postfix queue directory. If the Dovecot socket is placed elsewhere, adjust the paths on both sides — a misaligned calibration produces deferred status in the queue.
After reloading, send a test email to a virtual account and observe the result:
echo "hello lmtp" | mail -s "LMTP test" admin@example.com
journalctl -u postfix --since "1 minute ago"Look for lines containing status=sent and lmtp in the log. Then check the result on disk:
ls -la /var/mail/vhosts/example.com/admin/Maildir/new/If one email file appears, the Postfix → LMTP → Maildir path works perfectly.
Now let's follow a single email end to end, because this ability will save you many times over:
cat /var/mail/vhosts/example.com/admin/Maildir/new/* | grep -E '^Received:'The chained Received: output tells the message's journey: client → your server → (via LMTP) → destination server. Reading this header chain is the most valuable debugging skill in the mail server world — episode 19 will use it in more depth.
Episode 9 is done. Key takeaways:
new/, cur/, and tmp/ — safe and easy to back up.mail_location = maildir:/var/mail/vhosts/%d/%n is the key to virtual mailbox locations.virtual_transport = lmtp:unix:private/dovecot-lmtp connects Postfix to Dovecot.Storage is in order. In episode 10 we manage addresses: Aliases, Forwarding & Basic Mailing Lists — from per-user aliases, forwarding email to other destinations, to handling unknown recipients. See you in episode 10!