services: db: restart: unless-stopped image: postgres:16-alpine environment: POSTGRES_USER: central POSTGRES_PASSWORD: central POSTGRES_DB: central volumes: - pgdata:/var/lib/postgresql/data - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro ports: - "5433:5432" # host 5433 → container 5432 (avoid clashing local pg) healthcheck: test: ["CMD-SHELL", "pg_isready -U central -d central"] interval: 5s timeout: 3s retries: 10 server: restart: unless-stopped # The server spawns exiftool + mount.nfs children; without an init as PID 1 # their orphans become unreapable zombies and docker can't stop the container # ("PID … is zombie and can not be killed"). tini forwards signals + reaps. init: true build: context: . dockerfile: server/Dockerfile environment: DATABASE_URL: postgres://central:central@db:5432/central BIND_ADDR: 0.0.0.0:8080 # Folder the ingest scans. Locally this is ./sample-data (mounted below); # on the VM, mount the Synology share here instead and point INGEST_DIR at it. INGEST_DIR: /data # Phase 3: master token that grants admin (provision users, create projects). # CHANGE THIS in production — it is a break-glass credential. ADMIN_TOKEN: 198eb78d0687cca88e22f76759f0154bf85c3f926f225c6c7c84538a57c25ef7 # Claim lease length in seconds (auto-released after this if not heartbeated). LEASE_SECS: ${LEASE_SECS:-900} # NAS host for `nfs`-kind projects. The IP stays here (server-side); users only ever enter the # export path (e.g. /volume4/Saudi_Video_Sync). Leave empty to disable in-app NFS mounting. NFS_HOST: ${NFS_HOST:-192.168.1.199} NFS_OPTS: ${NFS_OPTS:-nfsvers=4,ro,soft,timeo=30,retrans=2,retry=0} volumes: - ./sample-data:/data:ro # Needed for the server to mount `nfs`-kind shares on demand. Not required if you only use # `local`-kind projects (plain paths or compose-managed NFS volumes). If mount.nfs still fails # on your host, replace these two with `privileged: true`. cap_add: - SYS_ADMIN security_opt: - apparmor:unconfined depends_on: db: condition: service_healthy ports: - "8091:8080" dashboard: restart: unless-stopped build: ./dashboard environment: # The dashboard calls the API through nginx's /api proxy, so no host needed. VITE_API_BASE: /api depends_on: - server ports: - "8090:80" # Nightly pg_dump → NAS, with automatic daily/weekly/monthly pruning. # Writes gzipped SQL dumps to /backups (an NFS mount of the NAS folder below). db-backup: image: prodrigestivill/postgres-backup-local:16 restart: unless-stopped environment: POSTGRES_HOST: db POSTGRES_PORT: "5432" POSTGRES_DB: central POSTGRES_USER: central POSTGRES_PASSWORD: central # Schedule is evaluated in this timezone (IST). 02:00 IST daily (off-peak). TZ: Asia/Kolkata SCHEDULE: "0 2 * * *" # Tiered retention — tune to taste (DB is tiny, so generous is cheap). BACKUP_KEEP_DAYS: "30" BACKUP_KEEP_WEEKS: "8" BACKUP_KEEP_MONTHS: "6" # Healthcheck window so a missed run is visible (`docker inspect` health). HEALTHCHECK_PORT: "8080" volumes: - nasbackups:/backups depends_on: db: condition: service_healthy volumes: pgdata: # NFS mount of the NAS backup folder. The folder must already exist on the NAS and # allow rw NFS from this Docker host. Host needs an NFS client (nfs-common). nasbackups: driver: local driver_opts: type: nfs o: "addr=${NFS_HOST:-192.168.1.199},nfsvers=4,rw,soft,timeo=30,retrans=2" device: ":/volume4/Saudi_Video_Sync/Verification_Dashboard_Backup"