Trace Postfix Mail Through cleanup(8) Safely
A message went in with one set of headers and came out of the queue with another, and Postfix cleanup(8) is the daemon that did it. This guide shows you what cleanup does on this host, which settings steer it, and how to verify a change without treating the daemon as an interactive mail command.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 15 minutes for inspection, or longer if you send a controlled test message. It describes Postfix 3.8.6, installed from the Ubuntu package postfix 3.8.6-1ubuntu0.1 on the reference system. Configuration names and defaults come from the installed cleanup(8) manual page, so check your own package version before applying an example.
1. Confirm the daemon and package version
cleanup(8) is a Postfix service, not something you normally run against a message file. Postfix's process manager starts it from master.cf. Mail arrives through services such as smtpd(8) or the local sendmail(1) compatibility interface. Cleanup then canonicalises the message, places it in the incoming queue, and informs the queue manager.
Run these read-only checks as your normal user:
$ postconf -h mail_version
3.8.6
$ postconf -M cleanup
cleanup unix n - y - 0 cleanup
$ postconf -h config_directory
/etc/postfix
$ postconf -h queue_directory
/var/spool/postfix
The service line is the important part: it proves this installation has a cleanup service entry. Do not try to run an undocumented cleanup --help command. The manual synopsis is just cleanup plus generic Postfix daemon options, because master supplies the service context.
Checkpoint
Note the version, configuration directory and queue directory. If postconf fails, stop and use the package's normal repair procedure rather than editing master.cf by hand.
2. Understand what cleanup changes
Cleanup sits between an arriving message and its queue file. It does these jobs:
- Adds missing headers. It can add
From:,To:,Message-Id:andDate:when the relevant settings allow it. - Removes headers. It drops those listed by
message_drop_headers. On this installation the documented default list includesBcc,Content-Length,Resent-BccandReturn-Path. - Normalises addresses. It can apply
canonical_mapsand masquerading rules, expand recipients usingvirtual_alias_maps, and remove duplicate envelope recipients. Address rewriting is delegated to thetrivial-rewrite(8)daemon. - Runs content checks. Header and body checks, plus optional non-SMTP Milters, also run at this point in the receive path.
That explains a classic diagnostic trap: the message a sender submits is not necessarily byte-for-byte the queued message. A header may have been inserted, a blind-copy header removed, or an address rewritten before delivery.
Cleanup is not a general-purpose content filter. The manual describes header_checks and body_checks as protection against floods of worms or viruses, not as attachment extraction or archive inspection.
3. Inspect effective settings first
Use postconf to ask Postfix for the effective value of a setting. This is read-only and tells an empty table apart from an inherited default:
$ postconf -h always_add_missing_headers
no
$ postconf -h message_drop_headers
bcc, content-length, resent-bcc, return-path
$ postconf -h local_header_rewrite_clients
permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
$ postconf -h canonical_maps
$ postconf -h virtual_alias_maps
Blank output means a lookup table setting is empty, not that cleanup is broken. Defaults for local_header_rewrite_clients and other parameters are installation-specific, so trust your own output rather than copying a value from another host.
For a full view of non-default settings, run:
$ postconf -n
Look for address mapping, header and body checks, Milters, recipient limits and the soft_bounce safety setting.
Warning
Treat postconf -n as diagnostic output. Do not paste the whole result into a ticket or public issue without removing hostnames, internal domains and mailbox details.
4. Change one setting deliberately
Most investigations need no change. If you have a specific operational reason to alter a setting, save the current value first and change one thing at a time. Editing Postfix configuration affects mail processing and needs elevated privileges.
For example, to request insertion of missing standard headers, record the current value first:
$ postconf -h always_add_missing_headers
no
$ sudo postconf -e 'always_add_missing_headers = yes'
$ postconf -h always_add_missing_headers
yes
This does not add headers to messages already queued. It changes how mail processed from now on is handled. The manual says cleanup processes run for a limited time and pick up main.cf changes automatically; postfix reload speeds that up.
Warning
Do not casually change message_drop_headers, address rewriting maps, recipient limits or Milter actions on a production relay. Removing Return-Path or allowing an address rewrite can affect downstream delivery and investigation.
Recovery
If you changed a setting only for a test, restore the saved value with sudo postconf -e and reload again.
$ sudo postconf -e 'always_add_missing_headers = no'
$ sudo postfix reload
A configuration edit has no undo transaction, so the old value you recorded is your recovery reference.
5. Reload and verify the running configuration
Reload only after checking the edited value. It asks Postfix to reread configuration and is normally brief, but it still changes a live mail service. Use an administrator account:
$ sudo postfix reload
postfix/postfix-script: refreshing the Postfix mail system
The wording can vary with the package. Confirm the value after the reload and check service health:
$ postconf -h always_add_missing_headers
yes
$ sudo postfix status
postfix/postfix-script: the Postfix mail system is running: PID: ...
Checkpoint
The PID and exact status text vary. A clean reload proves only that Postfix accepted the configuration operation, not that a message passed every check. Use logs and a controlled message for behavioural verification.
6. Test the receive path
A real test message changes queue and delivery state. Use a mailbox you control, a disposable local account, or a test domain whose delivery you understand, and never send test mail to an unrelated person. This local submission uses the installed Sendmail-compatible interface and needs no root privileges:
$ /usr/sbin/sendmail -t -oi <<'EOF'
From: [email protected]
To: YOUR_TEST_ADDRESS
Subject: cleanup test
cleanup test message
EOF
$ printf 'sendmail exit status: %s\n' "$?"
sendmail exit status: 0
Replace YOUR_TEST_ADDRESS with a real test address before running it. The example.invalid sender is deliberately non-deliverable, so use it only where your local policy accepts that envelope and header. A zero exit status means the submission interface accepted the request, not that the recipient received it.
Now inspect the queue and logs with the tools your distribution has:
$ postqueue -p
$ sudo journalctl -u postfix --since '5 minutes ago' --no-pager
$ sudo tail -n 50 /var/log/mail.log
Tip
Some systems use syslog files rather than a postfix journal unit, and the log may be /var/log/maillog instead of /var/log/mail.log. Use the command that exists. Search for the queue ID, cleanup and any rejection or defer diagnostic. Cleanup problems go to syslog or postlogd(8), so the log is far more useful than trying to attach to the daemon.
7. Diagnose failures and recover safely
If submission fails, separate the layers:
- Rejected recipient or authentication failure. That belongs to the SMTP or policy path, not necessarily cleanup.
- Accepted by Sendmail but still queued. A later delivery transport may have deferred it.
- Header or address differs. That points towards cleanup settings, mapping tables, content checks or a Milter.
For an unexpected queued message, note its queue ID from postqueue -p and inspect it with the installed Postfix queue tools before removing anything. Do not delete queue files directly from /var/spool/postfix.
Warning
If a test message must go, use the documented queue administration command for that queue ID, and first confirm it is your test. Queue deletion is destructive and has no general undo.
If a configuration change causes widespread deferrals or rejections, restore the previous value, reload Postfix and watch the logs. Keep the original test message and queue ID until you have captured the diagnostic. Do not answer a transient error by disabling all checks or setting broad address rewrites; that can turn a local fault into unwanted mail delivery.
Done means
- Version confirmed. You checked the installed Postfix version and the
cleanupservice entry. - Role understood. Cleanup canonicalises messages and puts them in the incoming queue; it is not an interactive converter.
- Settings read first. You inspected effective settings with
postconfbefore changing anything. - Changes tracked. Any configuration edit was recorded, made with the required privileges, reloaded and tested.
- Acceptance is not delivery. You told submission acceptance from final delivery and checked logs for the queue ID.
- Recovery ready. You know how to restore a temporary setting and will not remove queue files directly.