Build and Test Postfix Lookup Tables with postmap
You will finish with a compiled Postfix lookup table, a repeatable query for checking it, and a safe way to rebuild it after editing the source. The examples use Postfix 3.8.6, installed here as Ubuntu package version 3.8.6-1ubuntu0.1. Allow about 15 minutes for a small table. You need a shell and a Postfix installation; changing files under /etc/postfix normally requires elevated privileges.
The route
Jump straight to the step you need, or tick off Done means at the end.
1. Check the database types available
Postfix can query more table types than postmap can create. Ask the installed configuration which types are available before choosing a map:
$ postconf -m
btree
cidr
regexp
...
The exact list depends on the build. On this machine, the installed command reports its version through the package as Postfix 3.8.6. A normal file-backed map is usually built as hash:, btree:, dbm:, lmdb:, cdb: or sdbm:, when that type is supported. If you omit the type, postmap uses default_database_type from Postfix configuration.
Checkpoint
Choose a type printed by postconf -m. Do not copy hash: blindly from another host, because database support differs between packages.
2. Create a small source table
Use a source file with one key and value separated by whitespace. Blank lines and lines whose first non-whitespace character is # are ignored. A line beginning with whitespace continues the previous logical line, which is useful for long values but easy to trigger accidentally.
This example is an access table. It permits one test client and rejects the rest of its IPv4 network:
$ sudo install -o root -g root -m 0640 /dev/null /etc/postfix/access
$ sudo sh -c 'cat > /etc/postfix/access' <<'EOF'
# Test policy: the specific host must appear after compilation, not in this file's order.
192.0.2.0 REJECT
192.0.2.17 OK
EOF
The order is not the deciding factor for an indexed access map. The access table's lookup rules try the most specific address form first. The same source format is used by many Postfix tables, including canonical, generic, relocated and transport, but their values have different meanings.
For a transport table, for example, the value is transport:nexthop:
example.net smtp:[mail.example.net]
.example.net smtp:[mail.example.net]
That changes delivery routing, so treat transport, canonical and generic maps as service configuration rather than harmless test data.
3. Compile the map
Build the indexed file with an explicit type. This writes a database beside the source, normally /etc/postfix/access.db for a hash map:
$ sudo postmap hash:/etc/postfix/access
$ ls -l /etc/postfix/access /etc/postfix/access.db
-rw-r----- 1 root root ... /etc/postfix/access
-rw-r----- 1 root root ... /etc/postfix/access.db
When a new result file is created, postmap copies the source file's group and other read permissions. It places an advisory exclusive lock on the table while updating it. A successful run is quiet and returns status 0.
Safety warning
The ordinary rebuild creates a new database from the source and can replace existing entries. Do not use -i unless you specifically want incremental input. Do not use -r to permit duplicate updates casually, and do not use -w to hide them. Duplicate keys often mean the policy is not what you think it is.
Check the result immediately:
$ printf '%s\n' "$?"
0
4. Query one key without changing the map
Use -q to ask for the first matching value. This is a direct database query; it does not perform the iterative parent-domain or parent-network searches that some Postfix features perform when they use a table.
$ postmap -q 192.0.2.17 hash:/etc/postfix/access
OK
$ postmap -q 192.0.2.23 hash:/etc/postfix/access
REJECT
$ postmap -q 198.51.100.9 hash:/etc/postfix/access
$ printf '%s\n' "$?"
1
No output with status 1 means that this exact query had no value. The access service may still try broader keys when it performs its own lookup. That distinction prevents a common debugging trap: a successful postmap -q test proves one exact map entry, not the whole lookup policy.
To test several exact keys, pass - and pipe keys on standard input:
$ printf '%s\n' 192.0.2.17 192.0.2.23 | postmap -q - hash:/etc/postfix/access
192.0.2.17 OK
192.0.2.23 REJECT
5. Inspect and remove entries carefully
-s lists database elements when the database type supports it. Database order is not the original source order:
$ postmap -s hash:/etc/postfix/access
192.0.2.17 OK
192.0.2.0 REJECT
Do not edit the .db file directly. Edit the source, then rebuild it. The source is the recoverable record; the compiled map is an output file.
Destructive warning: postmap -d KEY MAP removes an entry from the database. It does not update the text source, so a later rebuild can restore the deleted entry. Prefer this recovery sequence:
$ sudo cp -p /etc/postfix/access /etc/postfix/access.backup
$ sudoedit /etc/postfix/access
$ sudo postmap hash:/etc/postfix/access
$ postmap -q 192.0.2.17 hash:/etc/postfix/access
If the edit was wrong, restore the backup and run the same rebuild command. Keep the backup private because table values can contain routing, recipient or policy information.
6. Choose the right table family
Indexed maps are a good fit for exact keys and Postfix's documented address or domain search rules. Use a cidr: table when the policy genuinely needs network and netmask notation. Use regexp: or pcre: when patterns must be evaluated in source order against the entire lookup string. Those pattern tables do not automatically perform parent-domain searches, and their case behaviour is controlled by the pattern rather than ordinary indexed-key folding.
tcp: delegates a lookup to a server. The local client sends an unauthenticated, unencrypted connection, so the installed tcp_table documentation explicitly warns against using it for security-critical purposes. memcache:, nisplus: and other map classes are query mechanisms, not ordinary files for postmap to compile. The manpages for access, canonical, generic, relocated and transport describe the search and value rules for each service.
7. Connect a rebuilt map to Postfix
Only after the standalone queries return the intended values should you reference the map from configuration. For example:
$ sudo postconf -e 'smtpd_client_restrictions = check_client_access hash:/etc/postfix/access'
$ sudo postfix check
$ sudo postfix reload
postconf -e changes main.cf, and postfix reload changes the running service configuration. Both require elevated privileges. Validate first with postfix check; if it reports an error, do not reload. To undo the example, restore the previous smtpd_client_restrictions value with postconf -e, run postfix check again, then reload.
Done means
postconf -mconfirmed the chosen map type is installed.- The source file has one deliberate key and value per entry, with comments and continuations checked.
postmaprebuilt the compiled map and returned status 0.postmap -qverified both a matching key and a missing key.- You edited the source, not the compiled database, and kept a recovery copy before service changes.
- Any Postfix configuration change passed
postfix checkbefore reload.