services: backend: build: ./backend container_name: bellsystems-backend restart: unless-stopped env_file: .env volumes: - ./backend:/app - ./data:/app/data - ./data/built_melodies:/app/storage/built_melodies - ./data/firmware:/app/storage/firmware - ./data/flash_assets:/app/storage/flash_assets - ./data/melody_binaries:/app/storage/melody_binaries - ./data/firebase-service-account.json:/app/firebase-service-account.json:ro ports: - "8000:8000" depends_on: postgres: condition: service_healthy networks: - internal frontend: build: ./frontend container_name: bellsystems-frontend restart: unless-stopped networks: - internal nginx: image: nginx:alpine container_name: bellsystems-nginx restart: unless-stopped ports: - "90:80" # access v2 on localhost:8001 volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - backend - frontend networks: - internal postgres: image: postgres:16-alpine container_name: bellsystems-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data networks: - internal healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 5 networks: internal: driver: bridge volumes: # Docker-managed volume (lives inside the WSL2 VM's own filesystem) instead # of a ./data/postgres bind mount onto the Windows filesystem. The bind # mount hit a WSL2/Docker Desktop bug where the 9p/virtiofs bridge reports # normal postgres:postgres 0600 ownership but the kernel still refuses the # postgres user's own open() calls for write — silent on read, fatal on any # WAL write, which took the whole database down after an unclean shutdown. # Migrated 2026-09-04; the old bind-mounted data is untouched at # ./data/postgres/ as a backup (that folder itself was too wedged by the # same bug to even rename — leave it alone; it's not used anymore). postgres-data: name: bellsystems-postgres-data external: true