diff --git a/docker-compose-prod.yaml b/docker-compose-prod.yaml new file mode 100644 index 0000000..18c953d --- /dev/null +++ b/docker-compose-prod.yaml @@ -0,0 +1,98 @@ +# CamMonitor — production / staging compose file +# +# Copy this file to the target server and run: +# docker compose -f docker-staging-prod.yaml up -d +# +# Mandatory values to change before first run (marked with CHANGE_ME): +# FOOTAGE_ROOT — absolute path to the camera footage directory on this host +# ADMIN_PASS — strong password for the bootstrap admin account +# +# The image is pulled from your local registry; make sure the host can reach +# 192.168.1.11:5000 and has it listed in /etc/docker/daemon.json: +# { "insecure-registries": ["192.168.1.11:5000"] } + +services: + app: + image: 192.168.1.11:5000/cammonitor:latest + restart: unless-stopped + + ports: + - "127.0.0.1:8080:8080" # Bind to loopback only — expose via reverse proxy. + # Change to "0.0.0.0:8080:8080" if you access + # the app directly without a proxy in front. + + environment: + LISTEN_ADDR: ":8080" + + # Absolute path on the HOST that contains the dated footage folders. + # Example: /mnt/cams → /mnt/cams/20260518/images/… + FOOTAGE_ROOT: /footage # CHANGE_ME — set to your real footage path + + DB_PATH: /data/cammonitor.db + + # Bootstrap admin created on first start only (no effect if DB already exists). + ADMIN_USER: admin + ADMIN_PASS: changeme # CHANGE_ME — use a strong password + + SESSION_TTL: 24h # How long a login session lasts + SCAN_INTERVAL: 5m # How often the footage tree is re-scanned + + volumes: + # Footage directory — mounted read-only so the app can never modify recordings. + - /path/to/footage:/footage:ro # CHANGE_ME — replace left side with real path + + # Persistent data (SQLite database). + - cammonitor_data:/data + + # Drop all Linux capabilities the Go binary does not need. + cap_drop: + - ALL + + # The app only needs to bind a TCP port; no other privileges required. + cap_add: + - NET_BIND_SERVICE + + # Read-only root filesystem — the only writable surface is the /data volume. + read_only: true + tmpfs: + - /tmp:size=64m,mode=1777 # Needed by the Go runtime for temp files + + # Prevent the container from gaining new privileges via setuid/setgid binaries. + security_opt: + - no-new-privileges:true + + # Limit CPU and memory so a spike in thumbnail requests does not starve the + # host (tune to your Atom's actual capacity). + deploy: + resources: + limits: + cpus: "1.0" + memory: 256M + reservations: + cpus: "0.25" + memory: 64M + + # Aggressive health-check so the orchestrator restarts a hung container fast. + healthcheck: + test: ["CMD", "cammonitor", "-health-check"] + # Fallback if the binary has no -health-check flag — use wget instead: + # test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/health || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + + logging: + driver: json-file + options: + max-size: "10m" + max-file: "5" + +volumes: + cammonitor_data: + driver: local + # Optional: pin the volume to a specific host path so backups are predictable. + # driver_opts: + # type: none + # o: bind + # device: /var/lib/cammonitor/data