Services/SnappyMail
As of: 2026-07-05 — migrated from `services/SNAPPYMAIL.md`
SnappyMail Webmail
Container & Files
| Item | Value |
|---|---|
| Container | snappymail
|
| Compose | /library/snappymail-app/docker-compose.yml
|
| Data dir | /library/snappymail-app/data/
|
| Image | djmaze/snappymail:latest
|
| Internal port | 8888
|
| External port | 8890 → proxied via VPS nginx
|
<syntaxhighlight lang="bash"> cd /library/snappymail-app && docker compose up -d docker logs --tail=50 snappymail 2>&1 | strings </syntaxhighlight>
Architecture Note — Unified vs Multi-Account
SnappyMail connected accounts ≠ unified inbox.
Adding external accounts (Gmail, iCloud, SDF) in SnappyMail Settings → Accounts shows them as separate silos side-by-side. There is no combined view.
The right approach for a unified inbox is to use getmail to pull all external accounts into Stalwart, then connect SnappyMail (and phones/clients) only to Stalwart. Sieve rules on Stalwart do the filtering. This way:
- One IMAP server to connect to from anywhere
- All mail filtered by sieve on arrival
- SnappyMail just sees one account with well-organized folders
Use SnappyMail's "additional accounts" feature only for occasional read-only access to an external account, not as the primary mail flow mechanism.
Critical: MTU Must Be 1280
The host's surfshark WireGuard VPN (surfshark_wg) has MTU 1280. All Docker bridge networks must match or large TLS packets (Server Hello + cert chain, ~4–6 KB) get silently dropped mid-handshake. Without MTU 1280, all external IMAP SSL connections hang for ~2 minutes then reset.
The docker-compose.yml already has this fix:
<syntaxhighlight lang="yaml"> networks:
default:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1280
</syntaxhighlight>
If SnappyMail is redeployed or the network is recreated, verify MTU with: <syntaxhighlight lang="bash"> docker network inspect snappymail-app_default | grep -i mtu </syntaxhighlight>
After changing docker-compose.yml, you must docker compose down && docker compose up -d — restart alone does NOT recreate the network.
Domain Config Files
Path: /library/snappymail-app/data/_data_/_default_/domains/
One JSON file per domain. Controls IMAP/SMTP server settings for that domain's accounts.
Ownership: Files must be owned by uid 82 / gid 82 (container's www-data). If created as root, SnappyMail can't read them and shows "Domain is not allowed" or "no domain configuration".
<syntaxhighlight lang="bash"> sudo chown 82:82 /library/snappymail-app/data/_data_/_default_/domains/*.json </syntaxhighlight>
Active domain configs:
| File | Server | Notes |
|---|---|---|
wilsoz.com.json
|
host.docker.internal
|
Stalwart — primary account |
craniumslows.com.json
|
host.docker.internal
|
Stalwart — secondary address |
gmail.com.json
|
imap.gmail.com:993
|
Requires App Password |
icloud.com.json
|
imap.mail.me.com:993
|
Requires App Password |
me.com.json
|
imap.mail.me.com:993
|
Same server as iCloud |
sdf.org.json
|
mx.sdf.org:993
|
shortLogin: true, lowerLogin: true
|
Editing domain configs: Do NOT bind-mount individual files — the container entrypoint does sed -i substitutions on its config files, which fails with "Resource busy" on bind-mounted files. Bind-mount the whole data/ directory (already the case).
Disabled Domains List
Path: /library/snappymail-app/data/_data_/_default_/domains/disabled
Plain text, one domain per line. Domains listed here cannot be added as accounts.
Current contents (as of 2026-02-22): <syntaxhighlight lang="text"> outlook.com qq.com yahoo.com hotmail.com </syntaxhighlight>
gmail.com was in here by default — removed so users can add Gmail accounts.
Stalwart Connection (wilsoz.com / craniumslows.com)
SnappyMail connects to Stalwart via host.docker.internal (resolves to the Docker bridge gateway → host → Stalwart port 993/587).
<syntaxhighlight lang="json"> {
"IMAP": { "host": "host.docker.internal", "port": 993, "type": 1 },
"SMTP": { "host": "host.docker.internal", "port": 587, "type": 2 }
} </syntaxhighlight>
The extra_hosts: ["host.docker.internal:host-gateway"] in docker-compose.yml enables this.
External Account App Passwords
For reference (these are app-specific passwords, not account passwords):
| Account | IMAP Server | App Password |
|---|---|---|
| [email protected] | imap.gmail.com:993 | jeuwezdxaoaysvni
|
| [email protected] | imap.gmail.com:993 | wikjuwnmklkeisid
|
| [email protected] | imap.mail.me.com:993 | pekx-kvid-iopx-lhyy
|
| [email protected] | mx.sdf.org:993 | (SDF account password) |
Troubleshooting
"Can't connect to host ssl://..."
- First check: MTU. Run
docker network inspect snappymail-app_default | grep -i mtu— must be 1280. - Test SSL from inside container:
docker exec snappymail php -r "...stream_socket_client('ssl://imap.gmail.com:993'...)" - If MTU is fine, test from host:
curl -v --max-time 10 imaps://imap.gmail.com:993
"Domain is not allowed" / "no domain configuration"
- Check
disabledfile — is the domain listed there? - Check if a
.jsonfile exists for that domain - Check ownership:
sudo ls -la /library/snappymail-app/data/_data_/_default_/domains/ - Fix:
sudo chown 82:82 /library/snappymail-app/data/_data_/_default_/domains/<domain>.json
Admin panel
- URL:
http://localhost:8890/?admin(or via VPS proxy) - Default admin password set on first run, stored in
data/_data_/_default_/configs/application.ini