# 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 # CPU/memory limits — tuned for a 4-core Intel Atom running ffmpeg transcode. # cpus limit: allow all 4 cores so the H.265→H.264 transcode can use # multi-threaded decoding and encoding simultaneously. # Lower this (e.g. "2.0") if other services share the host and you want # to guarantee headroom for them. # memory: 512M covers the Go process + one active ffmpeg transcode at 1080p. deploy: resources: limits: cpus: "4.0" memory: 512M reservations: cpus: "1.0" memory: 128M # 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