Home / Alt manpages / postmap(1)

  • postmap(1)
  • User command
  • linux

Build and Test Postfix Lookup Tables with postmap

Edit a Postfix access or transport table and nothing changes until postmap rebuilds the compiled database Postfix actually reads. This walks through building that database, querying it safely, and rebuilding it after every edit. Examples use Postfix 3.8.6, Ubuntu package version 3.8.6-1ubuntu0.1. Budget fifteen minutes for a small table. You need a shell and a Postfix install; changing files under /etc/postfix normally needs elevated privileges.

1. Check which database types are available

Postfix can query more table types than postmap can create. Ask the installed configuration what it supports before picking one:

$ postconf -m
btree
cidr
regexp
... 

The exact list depends on the build. A normal file-backed map is usually hash:, btree:, dbm:, lmdb:, cdb: or sdbm:, whichever type your build supports. Leave the type out and postmap falls back to default_database_type from Postfix configuration.

Checkpoint

Pick a type that postconf -m actually prints. Do not copy hash: blindly from another host; database support differs between packages.

2. Create a small source table

  • One key and value per line, separated by whitespace.
  • Blank lines and comments are ignored. A comment is any line whose first non-whitespace character is #.
  • Leading whitespace continues the previous line. Useful for long values, easy to trigger by accident.

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

File order is not what decides the outcome for an indexed access map: the lookup rules try the most specific address form first. The same source format serves many Postfix tables, including canonical, generic, relocated and transport, but the value means something different in each one.

In a transport table, for instance, 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, not 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 it creates a new result file, postmap copies the source's group and other read permissions, and holds an advisory exclusive lock on the table while it updates it. A clean run is silent and returns status 0.

Warning

A plain rebuild replaces the database from source and can overwrite existing entries. Do not reach for -i unless you specifically want incremental input, or -r to permit duplicate updates casually, or -w to hide them. Duplicate keys usually mean the policy is not what you think it is.

$ 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 lookup; it does not perform the iterative parent-domain or parent-network searches that some Postfix features apply when they consult 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 and status 1 means this exact query matched nothing, not that the access service will necessarily reject it too: that service may still try broader keys of its own. That distinction saves a common debugging trap: a passing postmap -q test proves one exact entry, not the whole lookup policy.

Test several exact keys at once by piping them in with -:

$ 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 every element when the database type supports it. The order is the database's own, not your source file's:

$ postmap -s hash:/etc/postfix/access
192.0.2.17 OK
192.0.2.0 REJECT

Never edit the .db file directly. Edit the source, then rebuild: the source is the recoverable record, the compiled map is just an output file.

Destructive action

postmap -d KEY MAP removes an entry from the database only. It does not touch the text source, so a later rebuild can bring the deleted entry straight back. Prefer this sequence instead:

$ 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 turns out wrong, restore the backup and run the same rebuild command again. Keep that backup private: table values can carry routing, recipient or policy information.

6. Choose the right table family

Indexed maps suit exact keys and Postfix's own documented address or domain search rules. Reach for cidr: when the policy genuinely needs network and netmask notation. Use regexp: or pcre: when patterns must be tried in source order against the whole lookup string; pattern tables do not do parent-domain searches automatically, and their case sensitivity is set by the pattern itself, not ordinary key folding.

tcp: hands the lookup off to a server over an unauthenticated, unencrypted connection, which is exactly why the installed tcp_table documentation warns against using it for anything security-critical. memcache:, nisplus: and other map classes are query mechanisms, not files for postmap to compile. The access, canonical, generic, relocated and transport manpages each spell out the search and value rules for their own service.

7. Connect a rebuilt map to Postfix

Only wire the map into configuration once standalone queries return exactly what you expect:

$ 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 configuration; both need elevated privileges. Validate with postfix check first. 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

  • Type confirmed. postconf -m lists the chosen map type as installed.
  • Source clean. One deliberate key and value per entry, comments and continuations checked.
  • Map rebuilt. postmap returned status 0 after the rebuild.
  • Both paths tested. A matching key and a missing key both checked with postmap -q.
  • Source edited, not the database. A recovery copy kept before any service change.
  • Config validated. Any main.cf change passed postfix check before reload.