Share Postfix Lookup Tables Safely with proxymap
By the end, a Postfix lookup such as MySQL-backed virtual aliases can be served through one controlled pool of proxymap processes, with the map explicitly allowed and the change verified. This guide targets Postfix 3.8.6 as installed on the reference system. Allow 10 to 20 minutes if the lookup table already exists; the database credentials and map syntax are outside this guide.
The route
Jump straight to the step you need, or tick off Done means at the end.
What proxymap changes
proxymap(8) is a Postfix-managed lookup proxy. You do not normally start its binary yourself and it does not accept mail from users. Postfix services ask it to open, read or, through the separate proxywrite service, update an approved table.
The useful read-only case is sharing an expensive table between Postfix processes. For example, this setting tells the virtual alias lookup to use a proxied MySQL map:
virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias.cf
The proxy can also work around a chroot boundary, but that is not a reason to proxy every lookup. It is a shared daemon handling multiple clients, so avoid high-latency tables. The manpage also warns against using it for sensitive data such as UNIX IDs, mailbox paths or external commands.
1. Record the installed defaults
Run these ordinary, read-only commands as the Postfix administrator. They show the version, the configuration directory, and the two allow-lists that enforce the proxy boundary.
$ postconf mail_version config_directory data_directory max_idle max_use
mail_version = 3.8.6
config_directory = /etc/postfix
data_directory = /var/lib/postfix
max_idle = 100s
max_use = 100
$ postconf proxy_read_maps proxy_write_maps
max_idle is how long an idle server waits before leaving, and max_use is the maximum number of incoming connections one server handles before leaving. The Postfix master creates more servers when needed, subject to the service process limit in master.cf.
Checkpoint
Save the current output somewhere suitable for your change record. If the map is writable, also identify the Postfix-owned data directory before changing anything.
2. Add a read-only map
First make a backup. This is an elevated command because main.cf is normally root-owned.
$ sudo cp --preserve=mode,ownership,timestamps /etc/postfix/main.cf /etc/postfix/main.cf.before-proxymap
Edit /etc/postfix/main.cf and set the application parameter. Keep the proxy: prefix: without it, the client opens the table directly and proxymap is not involved.
virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias.cf
Now check the resulting configuration and confirm that the expanded parameter includes the map family you changed:
$ sudo postconf virtual_alias_maps proxy_read_maps
virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias.cf
proxy_read_maps = ... $virtual_alias_maps ...
The exact default list varies with the package build. If you replaced proxy_read_maps with a custom value, include the relevant lookup parameter there; otherwise proxymap will refuse the request with a DENY status.
3. Reload and exercise the real lookup
Reloading Postfix is an elevated, service-affecting action. It asks the master to reread configuration; it does not rewrite your map.
$ sudo postfix check
$ sudo postfix reload
postfix/postfix-script: refreshing the Postfix mail system
Use the normal application path that consumes the map, then inspect the mail log for a failed connection, permission error, missing map, or proxy denial. The proxymap manpage says that problems and transactions are logged through syslogd or postlogd; the exact log file is distribution-specific.
$ sudo journalctl -u postfix --since "10 minutes ago" --no-pager
$ postconf -h virtual_alias_maps
A successful configuration check is not proof that the database credentials or SQL query work. Verification must include a safe lookup through the Postfix feature that uses the table, with a test address or key that cannot redirect real mail.
4. Treat proxywrite as a separate risk
Read-only proxying uses proxymap. Updates and deletes use proxywrite, and only maps listed in proxy_write_maps are approved. Postfix 2.5 and later support these operations; the installed 3.8.6 version does too.
Do not add a writable map merely to make a failed read work. If an application genuinely needs updates, store the data under the Postfix-owned data_directory or another directory writable only by the mail system. Never put a Postfix-writable file in a root-owned directory with mismatched ownership. For a file-based map that cannot safely have multiple writers, the manpage requires a process limit of 1 on the proxywrite service in master.cf.
There is a further boundary: read-write proxymap does not explicitly close tables between updates. Do not use it with formats that can remain persistently inconsistent between updates, such as CDB. Use a format with sync-on-update support or a real database system when its consistency model is suitable.
5. Recover from a bad change
If the lookup fails after the reload, stop testing against production traffic. Restore the saved configuration, validate it, and reload again:
$ sudo cp --preserve=mode,ownership,timestamps /etc/postfix/main.cf.before-proxymap /etc/postfix/main.cf
$ sudo postfix check
$ sudo postfix reload
$ postconf -h virtual_alias_maps
For a narrower rollback, remove only the proxy: prefix and restore the previously working map setting, if direct access is safe in your deployment. Do not delete a database or rebuild a map as a first response. Preserve the log entry and compare the map path, permissions, allow-list, chroot visibility and backend credentials.
Done means
- The intended lookup parameter contains the documented
proxy:prefix. - The map is allowed by
proxy_read_maps, or byproxy_write_mapsfor an explicitly required update path. postfix checkpasses and the master has been reloaded.- A harmless real lookup succeeds and recent Postfix logs contain no proxy denial or backend failure.
- Any writable data is owned and confined to the Postfix mail system, with a rollback copy of
main.cf.