- docs/02-setup/09-architecture.md: explain the one-image-many-containers pattern, configurator lifecycle, request flow, assets-volume requirement, and Redis durability split - docs/04-operations/02-makefile.md: document all make targets, variables, first-run checklist, and update workflow - docs/02-setup/05-overrides.md: fill in TBD entries for assets-volume, backup-cron, multi-bench, and custom-domain overrides - Makefile: add missing mariadb/redis/noproxy/backup-cron overrides to COMPOSE_OVERRIDES so make up starts a working stack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.2 KiB
Container Architecture
One image, many containers
There is no single "bench container" in Frappe Docker. The classic bench CLI that manages everything in a bare-metal setup is decomposed into separate processes, each running in its own container — all from the same image.
frappe-custom:v16 (one image)
│
├── configurator bench set-config ... (runs once, then exits)
├── backend gunicorn :8000 (HTTP API / page rendering)
├── websocket node socketio.js :9000 (realtime / socket.io)
├── queue-short bench worker --queue short,default
├── queue-long bench worker --queue long,default,short
├── scheduler bench schedule (Frappe's internal cron)
└── frontend nginx-entrypoint.sh (nginx reverse proxy)
External services (not from the Frappe image):
| Service | Image | Role |
|---|---|---|
db |
mariadb:11.8 |
Persistent relational storage |
redis-cache |
redis:6.2-alpine |
Page/session cache (no persistence) |
redis-queue |
redis:6.2-alpine |
Background job queue (persistent volume) |
cron |
mcuadros/ofelia |
Docker-native cron for scheduled backups |
Request flow
Browser → :8090
└─► frontend (nginx)
├─► backend:8000 HTTP/REST, page rendering
└─► websocket:9000 realtime events (socket.io)
backend / workers
├─► MariaDB:3306 persistent data
├─► redis-cache:6379 caching
└─► redis-queue:6379 job queue
The configurator
configurator is the most non-obvious piece. It runs before all other services, writes connection addresses into common_site_config.json (the shared sites volume), and then exits with code 0.
All other Frappe containers depend on it via:
depends_on:
configurator:
condition: service_completed_successfully
This is how override files inject infrastructure addresses. For example:
# overrides/compose.mariadb.yaml
services:
configurator:
environment:
DB_HOST: db # MariaDB container name
# overrides/compose.redis.yaml
services:
configurator:
environment:
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
Without the mariadb/redis overrides the .env values DB_HOST and REDIS_CACHE remain empty — Frappe starts but immediately fails to connect.
Why compose.assets-volume.yaml is mandatory for ERPNext
The base compose.yaml mounts only the sites volume. ERPNext requires compiled JS/CSS bundles to be accessible from all service containers at /home/frappe/frappe-bench/sites/assets.
compose.assets-volume.yaml adds a shared assets volume mounted to that path in every service. Without it nginx returns 404 for all static files.
The
compose.yamlsource contains an explicit comment:# ERPNext requires local assets access (Frappe does not).
Redis: two instances, different durability
| Instance | Purpose | Persistence |
|---|---|---|
redis-cache |
Page/session/query cache | None — ephemeral |
redis-queue |
Background job queue | redis-queue-data volume |
Jobs in the queue must survive restarts, so only redis-queue uses a persistent volume.