# SparkyFitness - Táplálkozás- és edzéskövető # Domain: ${SUBDOMAIN}.${DOMAIN} # Database: PostgreSQL 15 (dedicated) # RAM: ~400M (mem_limit: 1792M) | Pi-compatible: No # # Topology: frontend (nginx SPA, :80) proxies /api,/uploads internally to # server (Node, :3010) -> PostgreSQL. Only the frontend is exposed via Traefik; # the server and DB stay on the internal network. # # Environment variables: # DOMAIN - Customer domain (auto) # SUBDOMAIN - App subdomain (default: sparky) # DB_PASSWORD - PostgreSQL superuser password (auto-generated) # APP_DB_PASSWORD - Limited app DB role password (auto-generated) # API_ENCRYPTION_KEY - Data-encryption key, 64-char hex (auto; NEVER changes) # BETTER_AUTH_SECRET - Session/2FA signing secret (auto; NEVER changes) services: sparkyfitness-db: image: postgres:15-alpine container_name: sparkyfitness-db restart: unless-stopped environment: - POSTGRES_USER=sparky - POSTGRES_PASSWORD=${DB_PASSWORD} - POSTGRES_DB=sparkyfitness_db - PGDATA=/var/lib/postgresql/data/pgdata - TZ=Europe/Budapest volumes: - sparkyfitness_db_data:/var/lib/postgresql/data networks: - sparkyfitness-internal deploy: resources: limits: memory: 512M healthcheck: test: ["CMD-SHELL", "pg_isready -U sparky -d sparkyfitness_db"] interval: 10s timeout: 5s retries: 5 start_period: 20s sparkyfitness-server: image: codewithcj/sparkyfitness_server:v0.17.2 container_name: sparkyfitness-server restart: unless-stopped depends_on: sparkyfitness-db: condition: service_healthy environment: - SPARKY_FITNESS_DB_HOST=sparkyfitness-db - SPARKY_FITNESS_DB_PORT=5432 - SPARKY_FITNESS_DB_NAME=sparkyfitness_db - SPARKY_FITNESS_DB_USER=sparky - SPARKY_FITNESS_DB_PASSWORD=${DB_PASSWORD} - SPARKY_FITNESS_APP_DB_USER=sparkyapp - SPARKY_FITNESS_APP_DB_PASSWORD=${APP_DB_PASSWORD} - SPARKY_FITNESS_API_ENCRYPTION_KEY=${API_ENCRYPTION_KEY} - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} - SPARKY_FITNESS_FRONTEND_URL=https://${SUBDOMAIN}.${DOMAIN} - SPARKY_FITNESS_DISABLE_SIGNUP=false - ALLOW_PRIVATE_NETWORK_CORS=true - SPARKY_FITNESS_LOG_LEVEL=INFO - NODE_ENV=production - TZ=Europe/Budapest - PUID=1000 - GUID=1000 volumes: - sparkyfitness_uploads:/app/SparkyFitnessServer/uploads - sparkyfitness_backup:/app/SparkyFitnessServer/backup networks: - sparkyfitness-internal deploy: resources: limits: memory: 1024M healthcheck: # Node-exec probe (image-inspected 2026-07-03: node v24.17.0 at /usr/local/bin/node; wget/curl # also present, but the node HTTP GET checks the real /api/health status, not just reachability). test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:3010/api/health',r=>process.exit(r.statusCode<400?0:1)).on('error',()=>process.exit(1))"] interval: 30s timeout: 10s retries: 5 start_period: 120s sparkyfitness-frontend: image: codewithcj/sparkyfitness:v0.17.2 # container_name = the stack name EXACTLY: the controller-side health probe dials the container # named like the stack (fallback = FIRST prefix match, which here could be the DB) — see the # felhom-app-catalog skill / REUSE.md probe-naming row. container_name: sparkyfitness restart: unless-stopped depends_on: sparkyfitness-server: condition: service_healthy environment: - SPARKY_FITNESS_SERVER_HOST=sparkyfitness-server - SPARKY_FITNESS_SERVER_PORT=3010 - SPARKY_FITNESS_FRONTEND_URL=https://${SUBDOMAIN}.${DOMAIN} - NGINX_LISTEN_PORT=80 - TZ=Europe/Budapest - PUID=1000 - GUID=1000 networks: - traefik-public - sparkyfitness-internal labels: - "traefik.enable=true" - "traefik.http.routers.sparkyfitness.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)" - "traefik.http.routers.sparkyfitness.entrypoints=websecure" - "traefik.http.routers.sparkyfitness.tls=true" - "traefik.http.routers.sparkyfitness.tls.certresolver=letsencrypt" - "traefik.http.services.sparkyfitness.loadbalancer.server.port=80" deploy: resources: limits: memory: 256M healthcheck: # BusyBox-wget family (image-inspected 2026-07-03: /usr/bin/wget present in this Alpine/nginx # image; curl too — wget kept as the catalog's most common family). test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:80/"] interval: 30s timeout: 10s retries: 5 start_period: 30s volumes: sparkyfitness_db_data: sparkyfitness_uploads: sparkyfitness_backup: networks: traefik-public: external: true sparkyfitness-internal: