Discard Selected Postfix Mail Safely with discard(8)

A retired test domain keeps generating mail you never want to see, and bouncing it only makes more noise. You will route mail for one chosen destination through Postfix's discard delivery agent: Postfix accepts the message into its queue, records the destination as the reason, marks the matching recipients delivered, and sends it nowhere. That suits a test domain or a retired internal destination you are deliberately suppressing.

1. Check the installed discard service

discard(8) is a Postfix delivery daemon, not a command you run by hand. The queue manager sends it delivery requests through the service definition in master.cf. The daemon speaks that internal protocol, so running the binary directly tells you nothing.

$ postconf -M discard
discard    unix  -       -       y       -       -       discard
$ postconf -h transport_maps
$ dpkg-query -W -f='${Version}\n' postfix
3.8.6-1ubuntu0.1

Checkpoint: The exact spacing can differ. What matters is that the first line names the discard service and ends with the discard daemon. An empty transport_maps value means no transport table is configured yet.

2. Choose a narrow destination

Transport rules match by address, domain or subdomain. Start with a domain you control, such as discard-test.example. A bare domain entry matches that domain. Add a second entry beginning with a dot if its subdomains should be discarded too.

Warning: Do not use a wildcard while you are learning this feature. A rule for * can discard mail for every destination that reaches the transport table, which is a service-wide data-loss event.

Warning: The commands below create /etc/postfix/transport from scratch and overwrite any file already there. If you already have one, back it up and add the two lines by hand instead.

$ sudo install -o root -g root -m 0644 /dev/null /etc/postfix/transport
$ sudo sh -c 'cat > /etc/postfix/transport' <<'EOF'
discard-test.example discard:
.discard-test.example discard:
EOF
$ sudo postmap /etc/postfix/transport
$ postmap -q discard-test.example hash:/etc/postfix/transport
discard:
$ postmap -q [email protected] hash:/etc/postfix/transport
discard:

Checkpoint: The first query confirms the domain rule. If you added the dotted rule, query an address in a subdomain too. No output means that lookup did not match.

3. Enable the transport table

Before changing Postfix, record the current setting. If it already holds another transport map, keep it and add the new map to the existing comma-separated list. Replacing an existing value can silently change unrelated mail routing.

$ postconf -h transport_maps
$ sudo postconf -e 'transport_maps = hash:/etc/postfix/transport'
$ postconf -h transport_maps
hash:/etc/postfix/transport

That command is safe only when the earlier value was empty. If it printed an existing map, edit main.cf deliberately, or set a combined value such as hash:/etc/postfix/other, hash:/etc/postfix/transport, keeping the established order. Check the syntax before reloading:

$ sudo postfix check
$ sudo postfix reload
postfix/postfix-script: refreshing the Postfix mail system

postfix check validates the installation. postfix reload asks the master process to reread configuration. Both need elevated privileges. A reload is a service operation, but it does not flush the queue or deliver a message by itself.

4. Send one controlled test

Use a disposable recipient and a message with nothing sensitive in it. The command below queues one message through the local submission command.

Warning: Do not substitute a real customer address until you have reviewed the transport lookup and accept permanent loss.

$ printf 'From: postmaster@YOUR-HOSTNAME
To: [email protected]
Subject: discard test

This test message should be discarded.
' | sendmail -v [email protected]

The -v option makes submission more visible, but the useful evidence is in the Postfix log. On systems using systemd, look at recent mail records with:

$ sudo journalctl -u postfix --since '5 minutes ago' --no-pager | grep -E 'discard|discard-test.example'

On a traditional syslog setup, the same records may be in /var/log/mail.log.

Checkpoint: You should see a queue ID followed by a delivery record for the discard transport. The exact wording varies by logging daemon and configuration.

5. Understand what discard does

The daemon pretends to deliver every recipient in its request. It:

The value after discard: is empty in this example, so Postfix uses the recipient domain as the nexthop information before handing the request to the discard service. To make the logged reason explicit, put text after the colon, for example discard:automated test traffic. Keep reasons short and free of secrets or personal data.

Warning: Discard is not the error transport. error reports a non-delivery condition and can bounce or defer recipients. discard completes the delivery request successfully from the queue manager's point of view. That is the main safety boundary: a typo in a discard rule causes silent, irreversible mail loss.

6. Remove the rule and recover

When the test is done, remove the two transport entries, rebuild the map, restore the previous transport_maps value, and reload. This stops future matching mail being discarded. It cannot restore messages discard has already processed.

$ sudo sed -i '/^discard-test\.example[[:space:]]/d; /^\.discard-test\.example[[:space:]]/d' /etc/postfix/transport
$ sudo postmap /etc/postfix/transport
$ sudo postconf -e 'transport_maps ='
$ sudo postfix check
$ sudo postfix reload
$ postconf -h transport_maps

Recovery: The last command should print nothing only if transport_maps was empty before step 3. If you had an existing map, restore the exact value you recorded in step 3 instead of clearing it.

Tip: To keep the table for audit, copy it to a protected location before removing entries. Never leave an active wildcard or broad rule in place.

Done means