Skip to content


The cake function baseline_mailrelay executes the Ansible role-mailrelay-opensmtpd, which installs and configures opensmtpd to relay emails generated by (Web) applications to authenticated SMTP-Servers (like gmail).

It can also relay emails to the matrix chat.

Simplified, the opensmtpd-mailrelay works as follows:

  • Opensmtpd uses action and match directives in /etc/smtpd.conf to handle mail generated by Webapps, Cronjobs and so on
  • The mails are sorted by the "MAIL FROM" header (set by the Mail User Agent) using the match directive, which associates the email with an action
  • The action defines over which external relay server (for example to relay the mail


Key Value
Playbook path plays/baseline/mailrelay.yml
Config file Description
/etc/mailname Contains the hostname the server should use during SMTP connections as well as writing into the email headers
/etc/smtpd.conf Main config file
/etc/smtpd/ Main config directory
/etc/smtpd/tables Directory for database-like files, like smtp-user and -password information
/etc/smtpd/tables/monitoring_{from,recipients} Contains database of email addresses to relay to monitoring (matrix chat)
/etc/smtpd/tables/relay_secrets Contains relay host authentication data ( login)
/etc/smtpd/filters Directory for scripts that scan and modify or accept / reject email
/etc/smtp-to-matrix/config.yml Contains matrix chat authentication data



pass edit mailrelay_smtp_to_matrix_username
pass edit mailrelay_smtp_to_matrix_password


# role-mailrelay-opensmtpd
# Hostname for this mailrelay - should not be the servers actual hostname
# Commonly just the domain of the company running the server
# Will be written into the header of all outgoing emails
# Has to be an existing mailserver with a MX record

#mailrelay_smtp_to_matrix_username: from password store
#mailrelay_smtp_to_matrix_password: from password store
mailrelay_smtp_to_matrix_server: ""
mailrelay_smtp_to_matrix_room_id: "!"


  - root
  - borgbackup



    # Alias for this relay
  - alias: myrelay

    # Relay server DNS name or IP
    server: ""

    # mail-from email address

    # Relay server protocol (Documentation:
    protocol: "smtp+tls"

    # Enforce TLS (default: True)
    tls: True

    # Do not verify TLS certificates (default: False)
    tls: True

    # Relay server port
    port: "587"

    # Relay server SMTP login

    # Relay server SMTP password
    # This should be in the password-store!
    password: "{{ cus_www_prod_web_email_password }}"

    # Linux of sender addresses to apply this relay for
      - root
      - www-data

  # Simple version
  - alias: backup
    server: ""
    protocol: "smtps"
    tls: True
    port: "465"
    password: superduperhyperextremelysecret
      - backuppc


Password for encrypted mails in queue

The opensmtpd mail queue is encrypted. To interact with it, you need to enter a password. This password is in ansible-cake in the password store:

CAKE master pass show mailrelay_opensmtpd_conf_queue_encryption

CAKE master cake ssh web-2 root pub

root@cus-www-prod-web-2 ~ # smtpctl show message pmRXuDvpF4892vik
key> <ENTER mailrelay_opensmtpd_conf_queue_encryption PASSWORD HERE>
root@cus-www-prod-web-2 ~ # smtpctl show envelope pmRXuDvpF4892vik

Common commands

Show mails in queue

smtpctl show queue

Show number of mails in queue

smtpctl show queue | nl     # Few mails
smtpctl show queue | wc -l  # Lots of mails

Show size of mail queue directory

du -sh /var/spool/smtpd/

Delete specific mail(s) in the queue

smtpctl show queue
smtpctl remove <queue-id> <que-id>

Delete all mails in the queue

smtpctl remove all

Show a specific emails body

smtpctl show <queue-id>
smtpctl show message a2c25ab9d701c1c6

Show a specific emails envelope

smtpctl show <queue-id>
smtpctl show envelope a2c25ab9d701c1c6

Re-attempt delivery of all mails in the queue

smtpctl schedule all

Debugging while sending

To debug the path an email takes on the system, use the following commands:

Sending a test email, setting MAIL FROM to "root":

root@server ~ # echo testbody | mail -s testsubject

Sending a test email, setting MAIL FROM to "grafana":

root@server ~ # echo fo | mail -s testsubject -a "From: grafana"

Sending a test email, setting MAIL FROM to a user not defined in any "match" statement:

root@server ~ # echo fo | mail -s testsubject -a "From: nonexistinguser"
mail: cannot send message: Process exited with a non-zero status

Showing the email in the mail queue:

smtpctl show queue


Logs for a successfully relayed email. The most important part is the stat="250 Ok at the end, which is the answer of the relaying mailserver ( in this example) saying: Ok, I have taken this email and will relay it accordingly.

tail -n 0 -f /var/log/mail.log
# Now send the email and watch the logs
smtp connected address=local
smtp message msgid=d994c46d size=464 nrcpt=1 proto=ESMTP
smtp envelope evpid=d994c46d2502c97b from=<> to=<>
smtp disconnected reason=quit
mta connecting address=smtp+tls://
mta connected
mta tls ciphers=TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256
mta server-cert-check result="success"
mta delivery evpid=d994c46d2502c97b from=<> to=<> rcpt=<-> source="" relay=" (" delay=1s result="Ok" stat="250 Ok 0107017ee057c78c-d9f1e0e9-a73b-48e0-bb5b-430640e0edd7-000000"

Logs for an invalid MAIL FROM. Note that opensmtpd complains about the RCTP TO, even though the MAIL FROM is the problem, as there is no match defined for this MAIL FROM:

smtp connected address=local
smtp failed-command command="RCPT TO:<> " result="550 Invalid recipient: <>
smtp disconnected reason=disconnect

Logs for an unreachable relay-server. In this case the local firewall blocks the connection (connection refused):

smtp connected address=local
smtp message msgid=07ecd737 size=464 nrcpt=1 proto=ESMTP
smtp envelope evpid=07ecd737503da7ae from=<> to=<>
smtp disconnected reason=quit
mta connecting address=smtp+tls://
mta error reason=IO Error: Connection refused
smtp-out: Disabling route [] <-> ( for 15s

Logs for a mailserver relay that has a bad SSL certificate (sadly very common):

smtp connected address=local
smtp message msgid=8d2b2bfd size=518 nrcpt=1 proto=ESMTP
smtp envelope evpid=8d2b2bfd60715eac from=<> to=<>
mta connecting address=smtp+tls://
smtp disconnected reason=quit
mta connected
mta tls ciphers=TLSv1.2:DHE-RSA-AES256-SHA256:256
mta error reason=SSL certificate check failed
smtp-out: Disabling route [] <-> ( for 15s

The fix for this is to set tls no-verify in the action like so:

action "example_bad_tls_relay" \
    relay \
        tls no-verify \
        host smtp+tls:// \
    auth <relay_secrets> \

/etc/smtpd.conf in detail

This example /etc/smtpd.conf goes into detail about the relevant configurations:

# Read an opensmtpd "table" (a table is like a small database file)
# Doc:
# The contents of the table "relay_secrets" declare a purpose to a specific smtp-user and -password pair
# Example:
# purpose    user:password
# monitoring
table relay_secrets file:/etc/smtpd/tables/relay_secrets

# Declare an opensmtpd "filter"
# Doc:
# A filter is used to either accept, reject or modify an email
filter regex_replace_data proc-exec "/etc/smtpd/filters/"

# Only open the port on on the loopback interface
# Doc:
listen on lo \
    # Listen on tcp port 25
    # Doc:
    port 25 \
    # When accepting a SMTP dialogue, announce yourself as
    # Also write as processing mailserver into the email header
    # This hostname is also set in /etc/mailname
    # Doc:
    hostname \
    # Apply the filter "regex_replace_data" to all emails
    # Doc:
    filter regex_replace_data

# Listen on /var/run/smtpd.sock for SMTP connections
# Doc:
listen on socket \
    # Omit the from part when prepending “Received” headers
    # Doc:
    mask-src \
    # Assign the connection this specific tag (only used inside opensmtpd, not written to the email header)
    # Doc:
    tag "socket" \
    # Apply the filter "regex_replace_data" to all emails
    # Doc:
    filter regex_replace_data

# Define the action "relay_monitoring"
# Doc:
action "relay_monitoring" \
    # This action relays emails to another SMTP server
    # Doc:
    relay \
    # Require TLS when relaying
    # Doc:
    tls \
    # Relay the email to over this particular host
    # Doc:
    # Note the <monitoring> string, which refers to the line in the table "relay_secrets" we defined above!
    # is NOT an email address! It is the syntax <relay_secrets_table_line>@host:port
    host smtp+tls:// \
    # For the <monitoring> relay credentials defined in the previous line, use the table relay_secrets to look up the credentials
    # Doc:
    auth <relay_secrets> \
    # Use the given email address as the MAIL FROM address in the SMTP transaction
    # Doc: FROM

# When tools, webapps, linux users and others try to relay email over opensmtpd, they always state a "MAIL FROM" line in the SMTP transaction.
# "match" lines associate MAIL FROM with a particular relay
# Doc:
match \
    # Connection can only come from a local IP (
    from local \
    # Match "MAIL FROM: root"
    MAIL FROM "root" \
    # Match any destionation
    for any \
    # Use the action "relay_monitoring" defined above
    action "relay_monitoring"

# More examples
match from local MAIL FROM "prometheus-alertmanager" for any action "relay_monitoring"
match from local MAIL FROM "grafana" for any action "relay_monitoring"

# Reject everything else
match from any reject