Files
webmail-client/docker/docker-compose.yml
T
lukasandClaude Opus 5 566f02a3f4 Deploy: the rspamd healthcheck probed with a tool the image does not have
`wget -qO- .../ping | grep -qi pong` exits 127 forever: the rspamd image ships
neither wget nor curl. The container therefore reported "unhealthy" from the
moment it started while answering /ping perfectly — and `webmail.sh up` waits on
health, so a working engine failed the bring-up.

`rspamc stat` instead, which ships with the engine and needs no password from
inside the container.

Worth noting what this did NOT break: nothing declares depends_on: rspamd and
the health gate deliberately ignores it, so mail kept flowing throughout. The
cost was a bring-up that looked failed and an alarming status in the UI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
2026-09-17 20:57:28 +02:00

136 lines
4.3 KiB
YAML

name: webmail
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: webmail
POSTGRES_PASSWORD: webmail_dev
POSTGRES_DB: webmail_dev
ports: ["127.0.0.1:5433:5432"]
volumes: [pgdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U webmail -d webmail_dev"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7
ports: ["127.0.0.1:6380:6379"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
meilisearch:
image: getmeili/meilisearch:v1.8
environment:
MEILI_MASTER_KEY: dev_master_key_change_me
MEILI_ENV: development
ports: ["127.0.0.1:7700:7700"]
volumes: [meilidata:/meili_data]
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:7700/health | grep -q available"]
interval: 5s
timeout: 3s
retries: 10
# Sync-engine v2 workers (refactor §1): two roles sharing one image. Scale the
# sync role with `--scale worker-sync=N` (lease protocol prevents double-owning).
worker-sync:
build: { context: ../backend }
command: ["python", "-m", "workers.run"]
environment: &worker-env
WORKER_ROLE: sync
APP_DOMAIN: dev.webmail.local
DATABASE_URL: postgresql+asyncpg://webmail:webmail_dev@postgres:5432/webmail_dev
REDIS_URL: redis://redis:6379/0
MASTER_KEY: CHANGE_ME_base64_32_bytes
BLOB_ROOT: /var/blobs
volumes: [blobs:/var/blobs]
ulimits: { nofile: 65536 }
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
worker-jobs:
build: { context: ../backend }
command: ["python", "-m", "workers.run"]
environment:
<<: *worker-env
WORKER_ROLE: jobs
volumes: [blobs:/var/blobs]
ulimits: { nofile: 65536 }
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
# Anti-spam engine, mirroring the production stack (see ../docker-compose.yml).
# Dev publishes the ports on loopback so the plugin can be driven from the host
# (RSPAMD_URL=http://127.0.0.1:11333) as well as from a container.
rspamd:
image: rspamd/rspamd:3.9
# Root only long enough to write the controller password; rspamd then drops
# to _rspamd itself. /etc/rspamd is root-owned in the image.
user: root
entrypoint:
- /bin/sh
- -c
- |
mkdir -p /etc/rspamd/override.d
printf 'password = "%s";\nenable_password = "%s";\n' \
"$$RSPAMD_CONTROLLER_PASSWORD" "$$RSPAMD_CONTROLLER_PASSWORD" \
> /etc/rspamd/override.d/worker-controller.inc
exec /usr/bin/rspamd -f -u _rspamd -g _rspamd
environment:
RSPAMD_CONTROLLER_PASSWORD: ${RSPAMD_CONTROLLER_PASSWORD:-devpass}
volumes:
- ./rspamd/local.d:/etc/rspamd/local.d:ro
- rspamddata:/var/lib/rspamd
ports:
- "127.0.0.1:11333:11333" # scan worker (/checkv2)
- "127.0.0.1:11334:11334" # controller (/learnspam, /stat)
healthcheck:
# rspamc, not wget/curl: the rspamd image ships neither, so the obvious
# HTTP probe fails 127 forever and the container sits "unhealthy" while
# the engine is answering perfectly.
test: ["CMD-SHELL", "rspamc stat > /dev/null 2>&1"]
interval: 10s
timeout: 5s
retries: 12
start_period: 15s
depends_on:
redis-rspamd: { condition: service_healthy }
# Bayes/fuzzy storage, separate from the app redis on purpose (SPEC §C.2).
redis-rspamd:
image: redis:7-alpine
command: [redis-server, --save, "300", "1", --maxmemory, 128mb, --maxmemory-policy, allkeys-lru]
volumes:
- rspamdredis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
# Test mail servers (integration tests + manual dev against a throwaway mailbox)
greenmail:
image: greenmail/standalone:2.0.1
environment:
GREENMAIL_OPTS: >-
-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0
-Dgreenmail.users=dev:devpass@dev.local -Dgreenmail.verbose
ports:
- "127.0.0.1:3025:3025" # SMTP
- "127.0.0.1:3143:3143" # IMAP
- "127.0.0.1:3993:3993" # IMAPS
volumes:
pgdata:
meilidata:
blobs:
rspamddata:
rspamdredis: