Mail server BearerMail addition
Upstream plMail is a client — it reads other people's mailboxes. BearerMail adds the other half: a real mail server for your own domain, built from Postfix, Dovecot and OpenDKIM, running as part of the same compose stack.
Architecture
internet --port 25--> mta (Postfix) --LMTP--> mailstore (Dovecot)
plMail --IMAP 143--> mailstore (reading, like any account)
plMail --SMTP 587--> mta (sending, DKIM-signed)
Two containers join the stack under the mail compose profile:
| Service | Software | Role | Published ports |
|---|---|---|---|
mta | Postfix + OpenDKIM | Receives internet mail on port 25, signs outbound with DKIM | 25:25 |
mailstore | Dovecot | Stores mail (Maildir), serves IMAP to plMail and to the world | 993:993 (TLS) |
Because delivery lands in Dovecot and plMail syncs it over IMAP like any other account, everything plMail does — threads, full-text search, filters, push — works on your domain mail with zero changes to the app. Mail keeps arriving even when plMail itself is down: Postfix queues it.
Mail lives in the mail_data volume (Maildir); DKIM keys in mail_dkim. Both survive restarts and rebuilds.
Managing mailboxes
Mailboxes come from one variable. localpart:password pairs, comma-separated:
export MAIL_ACCOUNTS="you:STRONG-PASSWORD,info:ANOTHER-STRONG-PASSWORD"
docker compose --profile mail up -d --force-recreate mta mailstore
Logins are the full address (you@yourdomain.com). postmaster@ and abuse@ are aliased to the first mailbox automatically. There is no mailbox-management UI yet — this variable is the interface.
Sending: direct or relayed
By default Postfix delivers outbound mail itself by looking up each recipient's MX (direct delivery). That needs port 25 outbound open — commonly blocked on residential connections and some VPS providers.
Using a relay (recommended for home IPs)
You can point outbound mail at a relay provider from the web UI — no shell needed. Go to Admin → Mail server, enter the provider's SMTP host, username and secret key, and hit Save. The mta container picks it up within seconds.
For Mailjet specifically:
- In Mailjet: Account → API keys — copy the API key and the Secret key. The secret goes into your server; never paste it into chat or a ticket.
- Check Account → Sender domains: your domain must be validated there.
- In BearerMail's Admin → Mail server (or via env vars below):
- Host:
[in-v3.mailjet.com]:587 - Username: your Mailjet API key
- Password: your Mailjet secret key
- Host:
The same settings exist as environment variables (MAIL_RELAYHOST, MAIL_RELAY_USER, MAIL_RELAY_PASS); env vars win when both are set. Clearing the host field in the UI switches back to direct delivery.
Outbound mail is DKIM-signed by your own server first, then handed to the relay over TLS, which adds its own signature. Two signatures on one mail is fine. Keep your SPF/DKIM/DMARC records either way — receivers check them no matter who carries the mail.
Reading mail from other apps
Dovecot serves public IMAP on port 993 (TLS), so Thunderbird, Apple Mail, or any phone mail app can connect directly:
| Field | Value |
|---|---|
| Host / port | mail.yourdomain.com / 993, SSL/TLS |
| Username | your full address |
| Password | the one from MAIL_ACCOUNTS |
For a valid certificate, mount your Let's Encrypt certs so Dovecot uses them:
export MAIL_CERTS_PATH="/etc/letsencrypt/live/mail.yourdomain.com" # fullchain.pem + privkey.pem
docker compose --profile mail up -d --build --force-recreate mailstore
Without mounted certs Dovecot generates a self-signed one — mail apps will warn about it, which is the nudge to mount the real cert.
Certificates, ports, and what's exposed
- Port 25 uses opportunistic TLS; remote servers don't verify it, so the self-signed cert the MTA generates on first start is fine.
- Port 587 is deliberately not published. Submission stays inside the compose network between plMail and Postfix. Don't publish it unless you're setting up external submission with real certificates.
- Port 993 is published for direct IMAP access from your own devices.
Testing
# From OUTSIDE your network — does the internet reach your MX?
telnet mail.yourdomain.com 25
# expect: 220 mail.yourdomain.com ESMTP plMail
# Send yourself a mail from anywhere (Gmail etc.), then watch it land:
docker compose logs -f mta
If nothing arrives, docker compose logs mta shows every SMTP conversation. NOQUEUE: reject: RCPT ... Relay access denied means the recipient isn't in MAIL_ACCOUNTS. A status=bounced with a DNS error means the container can't resolve — check the host's DNS.
What it is not (yet)
- No spam filtering (no Rspamd/SpamAssassin) and no greylisting.
- No sieve filters, no quotas, no per-domain config — one domain per stack.
- Mailbox management is the
MAIL_ACCOUNTSvariable, not a UI. - Webmail is plMail itself — that's the point.