Demonic deployment tool that embraces systemd
0

Configure Feed

Select the types of activity you want to include in your feed.

at master 3 folders 7 files
README.md

Mail App#

The mail App runs a Stalwart mail server and Bulwark webmail as rootless Podman Quadlet services.

Setup flow#

0. Provision .env#

Copy the template and fill the user-specific values:

cp apps/mail/.env.example apps/mail/.env

Then edit apps/mail/.env and replace every CHANGE_ME placeholder:

  • STALWART_RECOVERY_ADMIN — the recovery admin address and password as admin@<your-mail-domain>:<strong-random-password>.
  • JMAP_SERVER_URL — public URL Bulwark uses to reach Stalwart's JMAP endpoint, e.g. https://mail.example.com.
  • STALWART_PUBLIC_URL — public URL Stalwart advertises, e.g. https://mail.example.com (usually the same as JMAP_SERVER_URL).
  • DOMAIN — the bare mail subdomain cert-refresh operates on, e.g. mail.example.com.
  • SESSION_SECRET*_SECRET values auto-generate on first deploy: leave CHANGE_ME and the local sync wrapper (deploy-to.sh, via ensure_env in apps/lib/common.sh) replaces it with a random value, or set your own.

Prefer not to keep secrets in a static file? Provision .env from a secret store instead — e.g. skate get … > apps/mail/.env — before running deploy-to.sh. The deploy wrapper stays agnostic to how .env got its contents: it syncs whatever .env exists and rejects any file that still contains a CHANGE_ME placeholder.

Never commit .env; it is gitignored.

1. Recovery admin credentials#

The recovery admin user is admin@<domain> with the password set in .env (see step 0). ensure_env (from apps/lib/common.sh) auto-provisions .env from .env.example on first deploy if it does not exist.

2. Stalwart Setup Wizard#

Tunnel to the host and open the wizard:

ssh -L 8080:127.0.0.1:8080 admin@<host> -N

Open http://127.0.0.1:8080/ in your browser and log in with the recovery admin credentials from .env.

3. Configure SMTP relay (SMTP2GO)#

In the Stalwart WebUI:

  • Add SMTP2GO as an outbound relay (Settings → Routing → Outbound)
  • Host: mail.smtp2go.com, port 587 (STARTTLS) or 465 (TLS)
  • Credentials: your SMTP2GO API key or SMTP username + password

4. DNS records#

For your mail domain:

  • MX record pointing to the mail subdomain (e.g. mail.example.com)
  • A record for the mail subdomain → host IP
  • SPF: v=spf1 include:smtp2go.com ~all
  • DKIM: get the public key from SMTP2GO, add as TXT record
  • DMARC: v=DMARC1; p=none; rua=mailto:admin@<domain>

5. TLS certificates#

A weekly timer (mail-cert-refresh.timer) copies the proxy's Let's Encrypt cert into ~/apps/mail/certs/. Stalwart can use these for IMAPS (993) and SMTP STARTTLS (587).

In the WebUI, point Stalwart's TLS to:

  • Certificate: /etc/stalwart/certs/fullchain.pem
  • Private key: /etc/stalwart/certs/privkey.pem

Note: the cert-refresh script writes cert.pem/key.pem — this is a known discrepancy (see improvement issue 02). Until resolved, adjust the WebUI paths or rename the files after the refresh.

The container bind-mounts ~/apps/mail/certs//etc/stalwart/certs/.

6. Post-setup#

Once the wizard completes:

  • Stalwart writes config.json into ~/apps/mail/config/
  • Restart Stalwart: systemctl --user restart mail-stalwart
  • Bulwark auto-connects (it uses Stalwart's JMAP endpoint)

Troubleshooting#

podman logs mail-stalwart
podman logs mail-bulwark
systemctl --user status mail-stalwart mail-bulwark