Three things stood between v1.1.0 and a working anti-spam engine on an existing install, each silent: `update` rolled only worker-sync, worker-jobs, api and web. A release that adds a service — this one adds two — would report success while never creating it. It now converges the rest with `up -d --no-recreate`, which creates what is absent and leaves the rolled services alone. .env.prod.example pinned ENABLED_PLUGINS to the four plugins that existed when it was written. The entrypoint's default includes antispam, but an explicit value in .env wins, so migrate would skip the plugin's tables and every antispam request would fail at runtime rather than at install. The template is fixed for new installs; `doctor` now warns existing ones, comparing .env against the entrypoint's default so there is still one list, not two. And the runbook now documents what the engine costs (~150-300 MB), that nothing depends on it by design, that `init` must be re-run for the controller password, and that its Bayes database is derived data like the search index. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
133 lines
5.2 KiB
Bash
133 lines
5.2 KiB
Bash
# ============================================================================
|
|
# Webmail production configuration.
|
|
#
|
|
# Copy to docker/.env.prod, fill in the three REQUIRED values, keep it safe:
|
|
# cp docker/.env.prod.example docker/.env.prod && chmod 600 docker/.env.prod
|
|
#
|
|
# THIS FILE IS A BACKUP ARTEFACT. MASTER_KEY is not stored anywhere else: lose
|
|
# it and every saved mail-account password becomes permanently undecryptable,
|
|
# even from a perfect database backup. Keep a copy in a password manager.
|
|
# ============================================================================
|
|
|
|
# --- REQUIRED ---------------------------------------------------------------
|
|
|
|
# Where all persistent data lives on the host. Use a real shared folder your NAS
|
|
# backup tool can see — not a Docker named volume.
|
|
# Synology: /volume1/docker/webmail/data
|
|
# QNAP: /share/Container/webmail/data
|
|
DATA_ROOT=/volume1/docker/webmail/data
|
|
|
|
# 32 random bytes, base64. Generate ONCE, then never change it:
|
|
# openssl rand -base64 32
|
|
MASTER_KEY=
|
|
|
|
# Database password. Generate once:
|
|
# openssl rand -base64 24 | tr -d '/+=' | cut -c1-24
|
|
POSTGRES_PASSWORD=
|
|
|
|
# --- Identity ---------------------------------------------------------------
|
|
# The domain your user handles are minted under (user@APP_DOMAIN) and the issuer
|
|
# shown in 2FA authenticator apps. It does NOT have to be publicly resolvable.
|
|
APP_DOMAIN=mail.home.lan
|
|
|
|
# --- Network ----------------------------------------------------------------
|
|
# Two published ports, same container. Avoid 5000/5001 (Synology DSM) and
|
|
# 8080 (QNAP QTS).
|
|
#
|
|
# WEB_PORT is the mail app. The admin API is refused on it, so this is the one
|
|
# you may expose beyond the LAN.
|
|
WEB_PORT=8088
|
|
# ADMIN_PORT is the same app with the admin API reachable. Restrict it to your
|
|
# LAN in the NAS firewall — see docs/nas-install.md §5.1. Publishing it is not
|
|
# what protects it.
|
|
ADMIN_PORT=8089
|
|
# Bind to one NAS interface instead of every network, e.g. WEB_BIND=192.168.1.185
|
|
WEB_BIND=0.0.0.0
|
|
ADMIN_BIND=0.0.0.0
|
|
|
|
TZ=Europe/Warsaw
|
|
|
|
# --- Anti-spam (SPEC Appendix C) ---------------------------------------------
|
|
# Generated by `webmail.sh init`. Internal only: it authenticates the app to the
|
|
# Rspamd controller. Humans never type it — the admin panel gates Rspamd behind
|
|
# the ordinary administrator account.
|
|
RSPAMD_CONTROLLER_PASSWORD=
|
|
# Network checks (RBL/SURBL/fuzzy) are OFF in docker/rspamd/local.d by default:
|
|
# they leak message-derived hashes and URLs to third parties. Enable the modules
|
|
# there first if you want them.
|
|
RSPAMD_MEM_LIMIT=256m
|
|
RSPAMD_REDIS_MEM_LIMIT=160m
|
|
RSPAMD_REDIS_MAXMEMORY=128mb
|
|
|
|
# --- Search -----------------------------------------------------------------
|
|
# pg = Postgres full-text. No extra container, no extra RAM. Default.
|
|
# meili = Meilisearch: typo tolerance, better ranking, faster on large mailboxes.
|
|
# Costs a container and ~0.7-1 GB RAM. Start it with:
|
|
# ./deploy/webmail.sh up --meili
|
|
# and after switching an existing install, rebuild the index:
|
|
# ./deploy/webmail.sh reindex
|
|
SEARCH_BACKEND=pg
|
|
# Required if SEARCH_BACKEND=meili — generate with: openssl rand -base64 32
|
|
MEILI_MASTER_KEY=
|
|
|
|
# --- Images -----------------------------------------------------------------
|
|
# `local` = built on this host from the repo. To deploy prebuilt images from
|
|
# your Gitea registry instead, set both of these (see deploy/build-push.sh):
|
|
# REGISTRY_PREFIX=192.168.1.185:8418/claude/webmail
|
|
# IMAGE_TAG=v1.0.0
|
|
REGISTRY_PREFIX=webmail
|
|
IMAGE_TAG=local
|
|
|
|
# --- Application ------------------------------------------------------------
|
|
LOG_LEVEL=INFO
|
|
UNDO_SECONDS=10
|
|
SESSION_TTL_DAYS=30
|
|
MAX_ATTACHMENT_MB=25
|
|
# Auto-purge Trash/Spam older than N days (0 disables).
|
|
TRASH_RETENTION_DAYS=30
|
|
# Server-side sender avatars (Gravatar + favicon), fetched by the server so
|
|
# senders never see your IP. Set false to make zero outbound web requests.
|
|
SENDER_AVATARS_ENABLED=true
|
|
|
|
GOOGLE_OAUTH_ENABLED=false
|
|
OAUTH_GOOGLE_CLIENT_ID=
|
|
OAUTH_GOOGLE_CLIENT_SECRET=
|
|
OAUTH_MS_CLIENT_ID=
|
|
OAUTH_MS_CLIENT_SECRET=
|
|
|
|
# Plugins whose schema is migrated on every start. Remove one to stop shipping
|
|
# its tables; `hello` is the SDK demo and is intentionally not listed.
|
|
ENABLED_PLUGINS="calendar contacts labels filters antispam"
|
|
|
|
# --- Backups ----------------------------------------------------------------
|
|
BACKUP_AT=03:30
|
|
BACKUP_KEEP_DAYS=14
|
|
|
|
# --- Sizing (defaults suit a personal install on a shared NAS) --------------
|
|
# Peak worker RAM is roughly PARSE_CONCURRENCY x STREAM_PARSE_MB plus overhead.
|
|
INTERACTIVE_CONCURRENCY=8
|
|
BULK_CONCURRENCY=1
|
|
PARSE_CONCURRENCY=2
|
|
STREAM_PARSE_MB=8
|
|
PER_HOST_CONN_CAP=10
|
|
HOT_BUDGET_PER_WORKER=50
|
|
|
|
# Container caps. Memory only: CPU limits (`cpus:`) are deliberately absent
|
|
# because Synology's Docker daemon rejects them (NanoCPUs).
|
|
# Container caps. Total without Meilisearch: ~2.2 GB of *limits*; real steady
|
|
# usage for one or two mail accounts is around 700 MB-1 GB.
|
|
PG_MEM_LIMIT=768m
|
|
PG_SHARED_BUFFERS=256MB
|
|
PG_MAX_CONNECTIONS=100
|
|
PG_WORK_MEM=8MB
|
|
PG_MAINTENANCE_WORK_MEM=128MB
|
|
PG_EFFECTIVE_CACHE_SIZE=768MB
|
|
REDIS_MEM_LIMIT=160m
|
|
REDIS_MAXMEMORY=96mb
|
|
API_MEM_LIMIT=512m
|
|
WORKER_SYNC_MEM_LIMIT=512m
|
|
WORKER_JOBS_MEM_LIMIT=640m
|
|
WEB_MEM_LIMIT=96m
|
|
BACKUP_MEM_LIMIT=256m
|
|
MEILI_MEM_LIMIT=1g
|