← Back to Wiki
Self-Hosting / Email

Run Proton Mail Bridge Headless as an SMTP Relay for Your Whole Fleet

Every self-hosted service eventually wants to send mail. Monitoring alerts, a contact form, a scanner, a password reset. If your mail lives at Proton, none of them can send it, because Proton does not expose SMTP to third-party clients. Bridge is the missing piece, and running it on a headless container is a different exercise than installing it on a desktop.

Share on X

What Bridge actually is

Bridge authenticates to Proton's API and exposes an ordinary local IMAP and SMTP server that speaks the normal protocols with STARTTLS and a self-signed certificate. Anything that can talk to a smarthost can talk to it. Put it in its own container and let the fleet point at it.

ExecStart=/usr/local/bin/proton-bridge --noninteractive --log-smtp --log-level info
# SMTP 1025, IMAP 1143, both STARTTLS with a self-signed cert

Two deployment choices you have to make on purpose

BE WARNED: the daemon and the CLI cannot run at the same time. Both want an exclusive lock on Bridge's lock file, so proton-bridge --cli will simply hang while the service is up. Stop the service, use the CLI, start it again. Attach a real terminal when you do, because help, list and login all hang without a pty:
systemctl stop proton-bridge
ssh -tt host "su - proton -c 'proton-bridge --cli'"
systemctl start proton-bridge
And do not cycle it more than you need to. Restarting mid-sync restarts the sync.

"locked" does not always mean locked out

An account listed as locked is also what an initial full mailbox sync looks like. On an account with a hundred thousand messages that state lasts a long time, and re-authenticating because of it just starts the sync again.

Check the progress file before concluding anything. It holds a synced count and a total, which answers the question in one read:

cat ~/.config/protonmail/bridge-v3/imap-sync/sync-*

The From address is not yours to choose

The most common failure once everything else works, and it produces a real SMTP error rather than silence:

5.0.0 Error: The sender or recipient address is not valid.

Bridge will not relay mail claiming a From address the linked Proton account does not own. A tidy per-service sender like alerts@yourdomain is rejected until it exists as a real alias on that account. Create the alias, then use it. That is also the mechanism that gives each service a distinct sender without a second mailbox.

One password per account, not per application

Bridge issues a single generated SMTP password for the account, and it is not your Proton password. Every service on the fleet uses that same credential.

Worth knowing before you design around per-app credentials, because you cannot revoke one client without revoking all of them. The compensating control is network scope: keep the SMTP port reachable only from the hosts that need it, and for anything off-site, over a tunnel rather than the internet.

It will need more memory than you gave it

Bridge holds mailbox state, and its appetite grows with the account rather than with the number of clients. Ours became the shared relay for four services, then sat at 99.8% swap used on 1GB and thrashed. Two gigabytes and a restart fixed it.

Monitor memory and swap on this container specifically. A relay that is alive but thrashing delays exactly the alerts telling you something is wrong.

Prove delivery with a real message

Many applications have a "test notification" button that never touches the network. Checkmk's, for instance, is a dry run: the log says it would notify, which confirms a rule matches and proves nothing about SMTP.

Force one genuine event and read the result in the mail log. What you want to see is the relay's own acknowledgement:

Output: success 250 - 2.0.0 OK: queued

If your client stores SMTP credentials in a config file rather than a UI, edit that file directly and verify afterwards. We hit an application whose own settings dialog silently dropped the entire SMTP section on save, because the dialog did not render those fields at all in the edition we ran.

Give services their own relay path where it matters

One shared relay is the point, and it does create a shared failure. When the container filled its disk, every service that depended on it lost mail at once.

So back the container up, alert on it, and keep the dependency in mind when a service is the thing that tells you the fleet is broken. Mail from your monitoring should not be the only signal that your mail relay is down.

When this isn't your problem