Files
webmail-client/docker/.env.prod.example
T
lukasandClaude Opus 5.5 139fff826e Deploy: LanguageTool spell checker in production, with optional n-grams
- docker-compose.yml: a `languagetool` service (erikvl87/languagetool:6.8),
  internal only, 1 GB heap, one CPU, a 28 GB memory limit that is room for the
  n-gram page cache (reclaimable, not memory in use), and a low-priority
  warm-up read of the n-gram files at start so first checks hit RAM even on
  mechanical disks. `init` creates $DATA_ROOT/languagetool-ngrams; the app
  containers get LANGUAGETOOL_URL (empty turns the check off).
- `webmail.sh ngrams [langs]` (default en de fr es): deploy/ngrams.py, run in
  the backend image, downloads LanguageTool's n-gram data with resume, checks
  free space first (unpacked is ~2x the zip: ~26 GB for all four), unpacks into
  a temp folder and moves it into place, then recreates the engine to load it.
  languagetool.org refuses Python's default User-Agent, so it sends its own.
- `doctor` reports whether the spell checker answers and which n-gram
  languages are installed.
- docs/nas-install.md and .env.prod.example describe all of it.

Verified locally: German n-grams (1.58 GB -> 3.06 GB) load, and catch what the
rules alone do not ("liegt ihm Schrank" -> im, "Der Patenten" -> Patienten).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 07:44:36 +02:00

145 lines
5.7 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
# --- Spelling and grammar check (LanguageTool) -------------------------------
# The compose window's spell checker. Leave empty to turn it off for everyone;
# unset uses the bundled container (http://languagetool:8010).
#LANGUAGETOOL_URL=
# Java heap. 1g covers a household or small team.
LANGUAGETOOL_HEAP=1g
# Mostly file cache for the optional n-gram data (`webmail.sh ngrams`, ~26 GB
# unpacked for en/de/fr/es), which the kernel gives back under memory pressure.
# Without n-grams it never comes near this. Lower it on a small NAS.
LANGUAGETOOL_MEM_LIMIT=28g
LANGUAGETOOL_CPUS=1.0
# --- 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