Brevo as an Outbound Smart Host for OpenBSD Postfix (iRedMail/Amavis)
Using Brevo for Your Self-Hosted OpenBSD Email: A Concise, Reproducible Walkthrough
This post documents the exact steps we used to relay outbound mail from an OpenBSD/iRedMail stack (Postfix + Amavis/Dovecot) through Brevo’s SMTP service. It’s minimal, auditable, and avoids exposing secrets.
tl,dr
Create a SASL map with your Brevo SMTP login/key, set relayhost to Brevo on port 587, loosen TLS only for local Amavis hops via a tls_policy map, then restart Postfix and verify in logs that Brevo returns 250 2.0.0 OK.
0) Prereqs & sanity
On this host, base64(1) wasn’t present. We added a safe fallback using OpenSSL:
install -d -m 700 "$HOME/bin"
cat > "$HOME/bin/base64" <<'EOF'
#!/bin/sh
# Fallback base64 using OpenSSL; -A = single-line output
exec openssl base64 -A "$@"
EOF
chmod +x "$HOME/bin/base64"
hash -r
command -v base64
Create a private env file for Brevo (no world-read access):
install -m 700 -d "$HOME/.config/brevo"
cat > "$HOME/.config/brevo/api.env" <<'EOF'
# ~/.config/brevo/api.env
BREVO_API_KEY="YOUR_BREVO_API_KEY" # for REST API (optional)
BREVO_SMTP_LOGIN="xxxxxxxx@smtp-brevo.com" # SMTP login
BREVO_SMTP_KEY="xxxxxxxxxxxxxxxx" # SMTP master password/key
BREVO_DEFAULT_SENDERS="postmaster no-reply security"
EOF
chmod 600 "$HOME/.config/brevo/api.env"
(If you test the REST API and see 401 Key not found, that affects only REST—SMTP works with the SMTP key.)
1) Postfix SASL credentials
Create the SASL password map for Brevo and lock it down:
doas sh -c 'printf "%s\n" "[smtp-relay.brevo.com]:587 xxxxxxxx@smtp-brevo.com:xxxxxxxxxxxxxxxx" > /etc/postfix/sasl_passwd'
doas postmap /etc/postfix/sasl_passwd
doas chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
doas chown root:_postfix /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db 2>/dev/null || true
2) Core relay + SASL settings
Tell Postfix to relay via Brevo on 587, enable SASL auth, and require no anonymous mechanisms:
doas postconf -e 'relayhost=[smtp-relay.brevo.com]:587'
doas postconf -e 'smtp_sasl_auth_enable = yes'
doas postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
doas postconf -e 'smtp_sasl_security_options = noanonymous'
3) TLS policy for Amavis (iRedMail)
If you run the standard iRedMail/Amavis pipeline (127.0.0.1:10024/10025/10026), you may hit:
“TLS is required, but was not offered by host 127.0.0.1”
Fix this by explicitly allowing no TLS requirement on those local hops, while enforcing encryption to Brevo. Use a tls_policy map:
doas sh -c 'cat > /etc/postfix/tls_policy << "EOF"
[smtp-relay.brevo.com]:587 encrypt
[127.0.0.1]:10024 none
[127.0.0.1]:10025 none
[127.0.0.1]:10026 none
EOF'
doas postmap /etc/postfix/tls_policy
doas chmod 600 /etc/postfix/tls_policy /etc/postfix/tls_policy.db
Enable and use the map. Set global security level to may (Brevo is forced to encrypt by the map):
doas postconf -e 'smtp_tls_policy_maps = hash:/etc/postfix/tls_policy'
doas postconf -e 'smtp_tls_security_level = may'
Remove deprecated smtp_use_tls if present:
doas postconf -X smtp_use_tls
4) Reload and basic health check
doas postfix check
doas rcctl restart postfix
Optional polish (nice EHLO string):
doas postconf -e 'smtp_helo_name = obsd1.blackbagsecurity.com'
doas rcctl reload postfix
5) Send a test and verify hand-off
Send from the shell (adjust sender/recipient):
RCPT="your.real.inbox@example.com"
FROM="postmaster@mail.blackbagsecurity.com"
/usr/sbin/sendmail -v -f "$FROM" "$RCPT" <<'EOF'
Subject: Brevo relay test (obsd1)
This is a test from obsd1 relaying through Brevo.
EOF
Tail logs and confirm encrypted hand-off to Brevo plus 250 2.0.0 OK:
doas tail -f -n 0 /var/log/maillog | egrep -i 'postfix/.+smtp|brevo|status=|queued as'
What you want to see:
Trusted TLS connection established to smtp-relay.brevo.com:587 … TLSv1.3 …status=sent (250 2.0.0 OK: queued as <…@obsd1.blackbagsecurity.com>)
Files we touched (auditable)
/etc/postfix/sasl_passwd+/etc/postfix/sasl_passwd.db(mode600)/etc/postfix/tls_policy+/etc/postfix/tls_policy.db(mode600)main.cfkeys (viapostconf -e):relayhost,smtp_sasl_auth_enable,smtp_sasl_password_maps,smtp_sasl_security_options,smtp_tls_policy_maps,smtp_tls_security_level, optionalsmtp_helo_name- (Optional)
~/.config/brevo/api.envfor local testing
Troubleshooting quick table
| Symptom | Where to Look | Fix |
|---|---|---|
TLS is required, but was not offered by host 127.0.0.1 |
/var/log/maillog during Amavis hops |
Add none for [127.0.0.1]:10024/10025/10026 in /etc/postfix/tls_policy, keep Brevo as encrypt |
401 Key not found on REST |
Curling /v3/account |
Unrelated to SMTP; confirm you’re using the **SMTP key** for Postfix. Generate a valid REST API key only if you need the API. |
| SASL auth fails to Brevo | Postfix log lines with smtp/… SASL |
Rebuild map (postmap), verify exact format in /etc/postfix/sasl_passwd, check file modes are 600 |
Closing
The minimal set of changes above produces a secure, encrypted relay path from OpenBSD Postfix through Brevo, while keeping local Amavis hops happy and leaving the rest of your iRedMail stack untouched. From here, improve deliverability by authenticating your sending domain in Brevo (SPF/DKIM) and adding a DMARC policy—then watch your logs for the expected 250 2.0.0 OK.