BearerMail docs

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:

ServiceSoftwareRolePublished ports
mtaPostfix + OpenDKIMReceives internet mail on port 25, signs outbound with DKIM25:25
mailstoreDovecotStores mail (Maildir), serves IMAP to plMail and to the world993: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:

  1. 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.
  2. Check Account → Sender domains: your domain must be validated there.
  3. 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

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.

Double DKIM is normal

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:

FieldValue
Host / portmail.yourdomain.com / 993, SSL/TLS
Usernameyour full address
Passwordthe 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_ACCOUNTS variable, not a UI.
  • Webmail is plMail itself — that's the point.