Andrew Mercer
on this page

Once a message reaches the MTA that's authoritative for its domain, something has to put it where a user (or their IMAP client) can read it. That last step is delivery, and it's conceptually separate from everything SMTP.

Storage formats

Format Layout Pros Cons
mbox One file per folder; messages concatenated, each starting with a From line Simple; one file to copy; the default for /var/mail/<user> Needs locking; concurrent writers corrupt it; deleting a message rewrites the file; From lines in bodies get mangled (>From)
Maildir One file per message in tmp/, new/, cur/ per folder Lock-free (write to tmp/, atomic rename into new/); robust; easy to back up incrementally; supported by every tool Many small files (inode usage); slow ls on huge folders
Maildir++ Maildir with subfolders as dot-prefixed directories (.Sent/, .Lists.Linux/) plus quota files The de facto convention for IMAP servers Same as Maildir
sdbox / mdbox (Dovecot) Dovecot's own formats: one or many messages per file, with indexes Fastest for very large mailboxes; mdbox saves inodes Dovecot-only; use doveadm for everything, never touch files directly

Maildir is the sensible default for anything new. It's what Postfix's virtual(8) writes when the path ends in /, what Dovecot uses unless told otherwise, and what sync tools (mbsync, OfflineIMAP) produce.

Postfix chooses the format by path: a mailbox path ending in / means Maildir, otherwise mbox. (home_mailbox = Maildir/ for system users; virtual_mailbox_maps values like domain.tld/user/ for virtual ones.)

System accounts vs virtual mailboxes

Postfix distinguishes two very different delivery models. Mixing them up causes a lot of confusion in older configuration examples.

  • System (local) accounts: the recipient is a real Unix user in /etc/passwd. The domain is listed in mydestination, and mail is delivered by local(8) to /var/mail/<user> (mbox) or ~/Maildir/ (with home_mailbox = Maildir/). local(8) also processes /etc/aliases and ~/.forward.
  • Virtual mailboxes: recipients don't need Unix accounts. Domains are listed in virtual_mailbox_domains, and a lookup table (flat file, SQL, LDAP) maps each address to a storage path. All mail is stored under one service UID (conventionally vmail). This lets one server host many domains and thousands of users without creating system accounts. It's the model behind PostfixAdmin and every Postfix + Dovecot + SQL setup.

A domain must appear in exactly one address class. Never list the same domain in both mydestination and virtual_mailbox_domains (or virtual_alias_domains). Postfix warns, and delivery becomes unpredictable.

Postfix address class Listed in Delivered by
Local mydestination local_transport (local:)
Virtual alias virtual_alias_domains Rewritten via virtual_alias_maps, then re-routed
Virtual mailbox virtual_mailbox_domains virtual_transport (virtual: or lmtp: to Dovecot)
Relay relay_domains relay_transport (typically to an internal mail server)
Default Everything else default_transport (smtp:), the internet

Aliases vs mailboxes

An alias only redirects an address to one or more other addresses. It has no storage of its own. Postfix has two alias mechanisms that look alike but serve different scopes:

  • /etc/aliases (compiled with postalias or newaliases, referenced by alias_maps) applies only to local recipients (mydestination domains). Classic uses: sending root to a real inbox, a tiny mailing list, or piping an address to a program (list: "|/usr/local/bin/handler").
  • virtual_alias_maps (compiled with postmap) applies to any domain and is processed first. Use it to forward a whole extra domain elsewhere (a virtual alias domain), or to alias addresses in virtual mailbox domains.

The MDA: where delivery is handed off

Postfix can write mailboxes itself (local(8), virtual(8)), but its delivery agents are deliberately simple: no server-side filtering, quotas with limited behaviour, and no index updates. Production setups hand final delivery to Dovecot, which then does double duty:

  • As the MDA, it accepts mail from Postfix over LMTP, applies Sieve filters and quotas, writes the mailbox, and updates its indexes so IMAP clients see new mail instantly.
  • As the IMAP/POP3 server, it serves the same mailboxes to clients, and provides SASL authentication to Postfix (smtpd_sasl_type = dovecot) so one user database controls both "can send" and "can read".
# /etc/postfix/main.cf: deliver virtual mailboxes through Dovecot LMTP
virtual_transport = lmtp:unix:private/dovecot-lmtp
# /etc/dovecot/conf.d/10-master.conf
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode  = 0600
    user  = postfix
    group = postfix
  }
}

Older guides use Dovecot's LDA (dovecot-lda) through a pipe(8) service in master.cf instead. That works, but it starts a process per message and needs dovecot_destination_recipient_limit = 1. LMTP is the modern choice.

Historically procmail filled the MDA role for system accounts (mailbox_command = /usr/bin/procmail). It's unmaintained. Use Dovecot + Sieve (with ManageSieve so users can edit rules from their client) instead.

"Postfix + Dovecot + SQL" is such a common combination for this reason: Postfix handles SMTP in and out, and Dovecot handles everything about storing and serving mailboxes.