# BetterFrame stack: server + Angie proxy + Node-RED. # Kiosk runs on the Pi natively (not in Docker, needs Wayland/HDMI). # # Lives at repo root by convention — Docker Compose + Coolify both default # to looking here. All paths are repo-root-relative so they resolve # identically whether compose is invoked with or without --project-directory. # # Usage: # docker compose up -d --build # from repo root # # Volumes (override per-deployment via env — see Coolify "Environment"): # BF_DATA_VOLUME_NAME default "betterframe-data" # NODERED_DATA_VOLUME_NAME default "nodered-data" # BF_HOST_PORT default 80 (host edge port mapped to angie) # # Coolify ops: set these env vars on the resource so each deployment owns its # own named volumes (e.g. "bf-prod-data" vs "bf-staging-data"). For host bind # mounts or NFS / S3-CSI volumes, use Coolify's per-service "Storage" UI # rather than templating driver_opts here — JSON injection via env is brittle. # # Only ${BF_HOST_PORT}:80 is published on the host. Backend services and # Node-RED are reachable only from within the Docker network. version: "3.8" services: server: build: context: . dockerfile: deploy/docker/Dockerfile.server args: BF_SERVER_VERSION: ${BF_SERVER_VERSION:-} container_name: betterframe-server restart: unless-stopped environment: - TZ=UTC volumes: - type: bind source: ${SERVER_SEC_CONFIG:-./sec-config.yaml} target: /home/bsb/sec-config.yaml read_only: true expose: - "18080" - "18081" - "18082" healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:18080/healthz || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 30s networks: - betterframe angie: build: context: . dockerfile: deploy/docker/Dockerfile.angie container_name: betterframe-angie restart: unless-stopped depends_on: - server - nodered ports: - "${BF_HOST_PORT:-80}:80" networks: - betterframe nodered: build: context: . dockerfile: deploy/docker/Dockerfile.nodered container_name: betterframe-nodered restart: unless-stopped environment: - TZ=UTC volumes: - nodered-data:/data expose: - "1880" healthcheck: test: ["CMD-SHELL", "wget -q --spider http://localhost:1880/nrdp/ || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 90s networks: - betterframe # Optional: uncomment to use PostgreSQL instead of SQLite. # Set BF_DB=postgres and BF_PG_URL on the server service to activate. postgres: image: postgres:18-alpine container_name: betterframe-postgres restart: unless-stopped environment: - POSTGRES_DB=${BF_PG_DB:-betterframe} - POSTGRES_USER=${BF_PG_USER:-betterframe} - POSTGRES_PASSWORD=${BF_PG_PASSWORD:-betterframe} volumes: - postgres-data:/var/lib/postgresql expose: - "5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${BF_PG_USER:-betterframe}"] interval: 10s timeout: 5s retries: 5 start_period: 10s networks: - betterframe profiles: - postgres volumes: # Top-level keys are the in-compose references used above. `name:` sets the # actual docker volume name on the host so multiple Coolify deployments on # the same host can share machine without name collisions. Default keeps # backward compat with existing single-host deployments. betterframe-data: name: ${BF_DATA_VOLUME_NAME:-betterframe-data} nodered-data: name: ${NODERED_DATA_VOLUME_NAME:-nodered-data} postgres-data: name: ${BF_PG_VOLUME_NAME:-betterframe-postgres} networks: betterframe: driver: bridge