The directory behind the mail server: Postfix virtual domains and alias lookups, Dovecot IMAP/POP3 authentication, Thunderbird and Outlook address book integration, and extending the schema for email attributes.

In episode 19 you made Linux systems authenticate through LDAP. Now comes another classic consumer: the mail server. Episode 20 covers how Postfix reads domains, aliases, and local recipients from the directory; how Dovecot authenticates IMAP/POP3 logins against it; how email clients use LDAP as an address book; and how to extend the schema when you need more than the base attributes.
A mail server stores three kinds of data, and each can live in the directory:
The benefits are obvious: accounts live in one place, users change data once and mail is affected everywhere, and applications from the webmail to the address book use the same source of truth.
A realistic email tree adds domain information and user records with email attributes:
dc=example,dc=com
├── ou=mail
│ ├── mailHost=mail01.example.com
│ └── mailHost=mail02.example.com
└── ou=people
└── uid=budi
├── mail: budi@example.com
├── mailLocalAddress: budi@example.com
├── mailLocalAddress: budi@alternate.example.com
└── userPassword: {SSHA512}...mail — the primary address.mailLocalAddress — alternate addresses (aliases to the same mailbox).mailHost — which mail server hosts a user's mailbox.The mail and mailLocalAddress attributes come from the inetOrgPerson and inetLocalMailRecipient object classes; mailHost belongs to inetLocalMailRecipient or the inetMailUser variant used by some mail servers.
Postfix reads the directory through maps — a bridge that turns an LDAP query into a Postfix lookup. Three maps are typical:
virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf
virtual_mailbox_maps = ldap:/etc/postfix/ldap-mailbox.cf
virtual_mailbox_domains = ldap:/etc/postfix/ldap-domains.cfEach .cf file defines the query:
server_host = ldap://ldap.example.com
server_port = 389
search_base = dc=example,dc=com
bind = yes
bind_dn = cn=admin,dc=example,dc=com
bind_pw = secret
query_filter = (mail=%s)
result_attribute = mailHomeDirectory
version = 3query_filter: (mail=%s), result_attribute: mailHomeDirectory — maps an address to a mailbox path.query_filter: (mailLocalAddress=%s), result_attribute: mail — forwards an alias address to the primary.query_filter: (objectClass=mailHost), result_attribute: mailHost — declares which domains the server accepts.server_host = ldap://ldap.example.com
search_base = dc=example,dc=com
bind = yes
bind_dn = cn=admin,dc=example,dc=com
bind_pw = secret
query_filter = (mailLocalAddress=%s)
result_attribute = mail
version = 3After changing the maps, reload Postfix and test with postmap:
postmap -q budi@example.com ldap:/etc/postfix/ldap-mailbox.cf
postmap -q budi@alternate.example.com ldap:/etc/postfix/ldap-aliases.cfDovecot uses a passdb and a userdb — password database and user database — which can both point at LDAP. In dovecot.conf (or the conf.d files):
passdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf
}
userdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf
}/etc/dovecot/dovecot-ldap.conf holds the connection and query:
hosts = ldap://ldap.example.com
base = dc=example,dc=com
deref = never
scope = subtree
dn = cn=admin,dc=example,dc=com
dnpass = secret
auth_bind = yes
pass_filter = (&(objectClass=inetOrgPerson)(mail=%u))
user_filter = (&(objectClass=inetOrgPerson)(mail=%u))
pass_attrs = uid=user,userPassword=password
user_attrs = mailHomeDirectory=home,uidNumber=uid,gidNumber=gidauth_bind = yes makes Dovecot authenticate users by binding to LDAP with their own DN and password — the cleanest approach because the directory's password policy applies. The user_attrs line maps directory attributes to mail system settings: the mailbox home directory, UID, and GID.
Test with the Dovecot testing tool:
doveadm auth test budi@example.com 'password'Email clients can consume the directory directly:
(|(cn=*)(mail=*)); addresses then autocomplete from the directory.ldapsearch remains the verification tool: search (mail=*) to confirm the data clients will see.ldapsearch -x -H ldap://ldap.example.com \
-b ou=people,dc=example,dc=com \
'(&(objectClass=inetOrgPerson)(mail=*))' cn mail telephoneNumberThe base schema has a good foundation but not everything — for example, an officeLocation isn't in inetOrgPerson. You can extend the schema as in episode 28 with your own object class, or start with a good mail-specific schema such as the Courier schema (schema/courier.schema) which provides mailHost, mailHomeDirectory, and mailLocalAddress attributes plus a mailUser object class.
dn: cn=officeLocation,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: officeLocation
olcAttributeTypes: {0}( 1.3.6.1.4.1.4203.666.1.1 NAME 'officeLocation' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
dn: cn=mailUserExtension,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: mailUserExtension
olcObjectClasses: {0}( 1.3.6.1.4.1.4203.666.1.2 NAME 'mailUserExtension' SUP top AUXILIARY MAY ( officeLocation $ mail ) )Extending schemas is a powerful tool — and a dangerous one; always test new definitions in a staging environment before adding them to production.
Note
Don't forget the -en suffix convention for any thumbnail referenced from a translated series. Keep image paths in sync so clients don't render broken images.
In this episode 20 you integrated email with LDAP: the directory design for recipients, aliases, and hosts; Postfix maps with query_filter and result_attribute; Dovecot passdb/userdb with auth_bind; address book search in Thunderbird and Outlook; and extending the schema with custom attribute types and object classes.
Key takeaways:
.cf file is a simple filter + result mapping.auth_bind is the cleanest Dovecot path — the directory enforces the password policy.postmap -q, doveadm auth test, and ldapsearch confirm each integration.In the next episode, episode 21, the mail server is just one of many consumers: web application integration — how PHP, Python, Java, Node.js, and Go applications authenticate and read data from LDAP, plus the SSO protocols that unify them.