fluentbit postfix stream bounced emails to bigquery

Installing fluent bit described in docs

wget -qO - https://packages.fluentbit.io/fluentbit.key | sudo apt-key add -
echo 'deb https://packages.fluentbit.io/ubuntu/focal focal main' | sudo tee /etc/apt/sources.list.d/fluentbit.list
sudo apt update
sudo apt-get install td-agent-bit
sudo service td-agent-bit start
sudo systemctl status td-agent-bit

As described in docs we can run fluentbit without any configs by passing all configurations as command line arguments like:

/opt/td-agent-bit/bin/td-agent-bit -i systemd \
    -p systemd_filter=_SYSTEMD_UNIT=[email protected] \
    -p tag='host.*' -o stdout

Also we can grab some journals from /var/log/journal/xxxxxxxx/*.journal and pass Path parameter to fluentbit


    name postfix_smtp_bounced
    format regex
    regex (?<id>[^:]+): to=<(?<to>[^>]+)>, relay=(?<relay>(none|[^\[]+))(\[(?<ip>[^\]]+)\]:(?<port>\d+))?, delay=(?<delay>[^,]+), delays=(?<receive>[^\/]+)\/(?<queue>[^\/]+)\/(?<conn>[^\/]+)\/(?<send>[^,]+), dsn=(?<dsn>[^,]+), status=(?<status>[^ ]+) \((?<msg>[^\)]+)\)

There are many more interesting stuff in postfix logs, here are few other parsers

And here is a sample configuration which was used to parse dumped journal


    Flush        1
    daemon       Off
    Log_Level    warn
    Parsers_File bqp.conf

    name             systemd
    Systemd_Filter   [email protected]
    Systemd_Filter   _COMM=smtp
    Systemd_Filter_Type And
    Read_From_Tail   Off
    Path             /data/
    Strip_Underscores On
    alias smtp
    tag smtp
    Mem_Buf_Limit 128MB

    Name record_modifier
    Match smtp
    Whitelist_key MESSAGE

    name grep
    match smtp
    regex MESSAGE status=bounced

    name parser
    match smtp
    key_name MESSAGE
    parser postfix_smtp_bounced

    name grep
    match smtp
    # regex MESSAGE .+
    exclude MESSAGE .+

    Name record_modifier
    Match smtp
    # Whitelist_key id
    Whitelist_key to

    name   stdout
    format json_lines
    match smtp

    name   bigquery
    match  smtp
    google_service_credentials /data/creds.json
    project_id demo
    dataset_id dev
    table_id   postfix
    skip_invalid_rows On
    ignore_unknown_values On
    Retry_Limit 5

With this setup we have our bounced emails in bigquery table


  • While testing bigquery output can be commented out
  • To test whether all messages were parsed replace exclude MESSAGE .+ with regex MESSAGE .+ in filter
  • We can have multiple input -> filter -> output chains (e.g. if we want to have successfull emails side by side with bounced)

Additional headers

Same way as HTTP requests, emails have their headers which also can be logged

For this to work, somewhere in main.cf add

header_checks = regexp:/etc/postfix/header_checks

with contents like:

/^subject:/      WARN
/^X-FOO:/        INFO

after that in logs there will be records like:

Jan 23 16:29:08 demo postfix/smtpd[448165]: 4JflJ44pRZzVg5M: client=unknown[]
Jan 23 16:29:08 demo postfix/cleanup[448186]: 4JflJ44pRZzVg5M: info: header X-Foo: hello from unknown[]; from=<[email protected]> to=<[email protected]> proto=ESMTP helo=<bsg>
Jan 23 16:29:08 demo postfix/cleanup[448186]: 4JflJ44pRZzVg5M: info: header Subject: hello from unknown[]; from=<[email protected]> to=<marchenko.alex[email protected]> proto=ESMTP helo=<bsg>
Jan 23 16:29:08 demo postfix/cleanup[448186]: 4JflJ44pRZzVg5M: message-id=<>
Jan 23 16:29:08 demo postfix/qmgr[448161]: 4JflJ44pRZzVg5M: from=<[email protected]>, size=438, nrcpt=1 (queue active)
Jan 23 16:29:08 demo postfix/smtpd[448165]: disconnect from unknown[] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
Jan 23 16:29:09 demo postfix/smtp[448187]: 4JflJ44pRZzVg5M: to=<[email protected]>, relay=gmail-smtp-in.l.google.com[]:25, delay=0.96, delays=0.13/0.01/0.48/0.35, dsn=2.0.0, status=sent (250 2.0.0 OK  1642688949 g25si2880938lfh.217 - gsmtp)
Jan 23 16:29:09 demo postfix/qmgr[448161]: 4JflJ44pRZzVg5M: removed