Home / Alt manpages / postfix-collate(1)

  • postfix-collate(1)
  • User command
  • linux

Group Scattered Postfix Log Lines with postfix-collate

postfix-collate turns a wall of scattered Postfix log lines back into one readable block per mail session. Postfix normally writes one record per event, so a single message can be spread across SMTP reception, cleanup, queue management and delivery lines. This tool reads those records and prints related lines together, separated by blank lines, giving you one block per session for manual inspection or for a script that reads blank-line-separated records.

This guide uses the postfix-collate shipped by Postfix 3.8.6 on the target machine. It takes no options: give it zero or more log files, or pipe records to standard input. Allow about ten minutes for a first useful grouping, plus time to interpret the mail events in each block.

Before you start

  • Use a copy of the log when experimenting. The command only reads its inputs, but a copied sample makes repeated tests easier.
  • Use an account that can read the selected log. Reading a system log may need elevated privileges, but running the command itself does not.
  • Choose a bounded log file or a carefully filtered stream. Do not casually feed an entire busy mail history into a terminal.

Checkpoint

Confirm the installed command and package version before relying on details in a script.

command -v postfix-collate
dpkg-query -W -f='\${Package} \${Version}\n' postfix

On this machine the command is /usr/sbin/postfix-collate and the package reports postfix 3.8.6-1ubuntu0.1. The manpage itself identifies the upstream script as Postfix 3.8.6, and there is no documented version option. On this installation, postfix-collate --version is treated as a filename and fails if that file does not exist.

1. Collate a saved log file

Start with a file rather than a live stream. Replace the placeholder with a log that your account can read. Keep the output in a new file so the original stays unchanged.

postfix-collate /path/to/mail.log > /tmp/postfix-sessions.txt

The output contains the original matching log lines, with an empty line between grouped records. Open /tmp/postfix-sessions.txt in a pager and look for a block containing the queue identifier, recipient, and delivery result.

less /tmp/postfix-sessions.txt

Only lines in the format the script recognises are useful: the usual syslog prefix, a Postfix instance and a Postfix process name with a process ID. Lines that do not match that shape are skipped rather than copied through, which is a common reason an apparently relevant line is absent.

2. Read a stream from standard input

With no filename, the command reads standard input. This is useful when another command has already selected a time range or a host. Put the filter first, then pass its output through the collator.

journalctl --no-pager -u postfix.service --since '2026-09-26 08:00:00' \
  | postfix-collate > /tmp/postfix-today.txt

The exact journal unit and available timestamps depend on the local service setup. If your logs are plain files, the equivalent is:

sed -n '/Sep 26 08:00/,/Sep 26 09:00/p' /var/log/mail.log \
  | postfix-collate

When a pipeline produces no output, check the input before debugging the grouping. Run the selector without postfix-collate first, then check that the lines include the normal Postfix process prefix.

Checkpoint

The collated output should contain blank-line-separated blocks. A blank line is a record separator, not evidence that the message was rejected.

3. Understand what forms a block

  • An SMTP server connection starts a log, and a queue ID ties that activity to the transaction.
  • Queue-manager records close a transaction when they say the message was removed.
  • Pickup, cleanup, bounce, SMTP, LMTP and delivery-agent records can extend the same transaction when their queue ID matches.
  • Each queue ID is its own transaction: if one SMTP client connection handles multiple messages, they each get separated out. The script also keeps the Postfix instance name with the queue ID, so identical queue IDs from different instances do not get confused.
  • Records do not need perfect order. The script stores incomplete transactions and prints any that remain at end of input, ordered by when the transaction was first seen. An end-of-file block is not necessarily a failed delivery; it may just be a truncated time range or a message still in the queue.
  • Some lines are deliberately ignored. The script recognises the Postfix commands and delivery agents it knows about and does not act as a general syslog formatter. Do not treat missing lines as proof Postfix never emitted them.

4. Inspect and verify a result

Count the blocks before investigating them. This simple check confirms the output has the separators the manpage describes.

awk 'BEGIN { RS=""; ORS="\n" } { print "session " NR ": " NF " fields" }' \
  /tmp/postfix-sessions.txt

To locate a queue ID or recipient without losing the surrounding session, use an awk record search rather than a line-only grep:

awk -v needle='QUEUEID' 'BEGIN { RS="" } $0 ~ needle { print $0 "\n" }' \
  /tmp/postfix-sessions.txt

Replace QUEUEID with the identifier from your log. The blank-line record separator is the useful contract here: Perl can use $/="", and awk can use RS="". If your later tool expects one line at a time, split or transform the blocks explicitly rather than assuming the original line order is preserved as independent events.

5. Handle incomplete or sensitive investigations safely

Do not run this command directly against a log that may contain addresses, message IDs or other operational data while sharing a terminal or recording a session. Save output with restrictive permissions when it will be kept.

umask 077
postfix-collate /path/to/mail.log > /tmp/postfix-private.txt
less /tmp/postfix-private.txt

The command does not rewrite the input and does not contact Postfix, so there is no service restart or delivery change to undo. Recovery is simply rerunning it with the correct input or a narrower time range. Remove the temporary file deliberately, once you have checked no other process needs it.

For a live incident, do not assume the last block is complete. Capture a window that ends after the relevant queue-manager or delivery record, then compare the result with the original log. If a block ends right at the edge of your sample, extend the time range before drawing a conclusion.

Common traps

  • Using unsupported flags. The manpage documents only postfix-collate file.... Treat filenames beginning with a hyphen carefully and do not assume GNU-style options exist.
  • Expecting all syslog lines. Non-matching lines are skipped. Preserve the original log when you need an audit trail.
  • Calling every block a delivery. Blocks can represent SMTP reception, pickup, a bounce, an incomplete transaction or a completed delivery.
  • Reading a changing file as a snapshot. The command processes only the input it receives. Use a bounded copy or journal time range for repeatable analysis.
  • Confusing queue IDs across instances. The script includes the instance name internally, so inspect the full context when multiple Postfix instances are in use.

Done means

  • Checked the installed Postfix version and command path.
  • Ran postfix-collate on a bounded file or filtered standard input.
  • Found related lines grouped into blank-line-separated sessions.
  • Checked the original log before interpreting skipped or incomplete records.
  • Kept temporary output private and left the Postfix service unchanged.