#version: '3.8' services: digiserver-app: build: . container_name: digiserver-v2 # Don't expose directly; use Caddy reverse proxy instead # Port 5000 is also mapped directly for dev/testing access when Caddy isn't running expose: - "5000" ports: - "5000:5000" volumes: # Code is in the Docker image - no volume mount needed # Only mount persistent data folders: - ./data/instance:/app/instance - ./data/uploads:/app/app/static/uploads # Staged player source (git clone). Persisted so a rebuild of the app # container does not throw away the ~140 MB staged checkout, and so the # SSH deployment step has something to ship. - ./data/player:/app/data/player # The app GENERATES the Caddyfile (env bootstrap at startup, and the # Admin → HTTPS Configuration page at runtime) then asks Caddy to reload. # It therefore needs write access to the same file Caddy reads, so this # must be mounted here as well as in the caddy service. # The file is host-owned (uid 1000 == appuser), so writes succeed. - ./data/Caddyfile:/etc/caddy/Caddyfile:rw environment: - FLASK_ENV=production - SECRET_KEY=${SECRET_KEY:-your-secret-key-change-this} - ADMIN_USERNAME=${ADMIN_USERNAME:-admin} - ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123} # --------------------------------------------------------------------- # Deploy-time TLS bootstrap (all optional). # # If HOSTNAME_INTERNAL and HOST_IP are BOTH set, the container configures # Caddy for HTTPS using that address at startup — no manual step needed. # If either is missing, the app stays on the plain-HTTP fallback and you # can enable HTTPS later from Admin → HTTPS Configuration (which reloads # Caddy live, no restart required). # # Leave DOMAIN empty for Caddy's internal CA. That needs NO public DNS # and NO ACME, which is the right choice for an intranet name that is not # resolvable from the internet. # --------------------------------------------------------------------- - HOSTNAME_INTERNAL=${HOSTNAME_INTERNAL:-} - HOST_IP=${HOST_IP:-} - DOMAIN=${DOMAIN:-} - SSL_EMAIL=${SSL_EMAIL:-} # Externally published ports (must match the caddy service mappings below). # They are used to build correct HTTP→HTTPS redirect targets. - HTTP_PORT=${HTTP_PORT:-80} - HTTPS_PORT=${HTTPS_PORT:-443} # Set to "false" to serve TLS only and redirect plain HTTP to HTTPS. - HTTPS_HTTP_FALLBACK=${HTTPS_HTTP_FALLBACK:-true} # Post-bootstrap check: probe HTTPS and fall back to HTTP if it is broken. - HTTPS_VERIFY=${HTTPS_VERIFY:-true} restart: unless-stopped healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/').read()"] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: - digiserver-network # Caddy reverse proxy. # Port 80 → always answers, for both the IP and the hostname. # Port 443 → HTTPS when configured; plain HTTP is served alongside it by # default so clients that cannot trust the internal CA still work. # Ports are configurable so the stack also works on a host where 80/443 are # already taken (e.g. HTTP_PORT=8080 HTTPS_PORT=8443). caddy: image: caddy:2-alpine container_name: digiserver-caddy ports: - "${HTTP_PORT:-80}:80" - "${HTTPS_PORT:-443}:443" volumes: # The app container regenerates this file on startup (env bootstrap) and # whenever HTTPS is changed in the Admin UI, then hot-reloads Caddy via # its admin API (http://caddy:2019/load). Because the file on disk is # always current, a plain restart of Caddy picks up the latest config. - ./data/Caddyfile:/etc/caddy/Caddyfile:rw - ./data/caddy-data:/data - ./data/caddy-config:/config - ./data/caddy-logs:/var/log/caddy depends_on: digiserver-app: condition: service_started restart: unless-stopped healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80/"] interval: 30s timeout: 10s retries: 3 start_period: 10s networks: - digiserver-network networks: digiserver-network: driver: bridge