x-info: repo: https://github.com/CodeWithCJ/SparkyFitness releases: https://github.com/CodeWithCJ/SparkyFitness/releases documentation: https://codewithcj.github.io/SparkyFitness/ # nodes/heimdall/sparkyfitness/compose.yaml # Deployed via Komodo | Managed by GitOps # Service: sparkyfitness # Source: https://github.com/CodeWithCJ/SparkyFitness networks: proxy-net: external: true sparkyfitness-network: driver: bridge services: # ── Database ──────────────────────────────────────────────────────────────── sparkyfitness-db: image: postgres:18.3-alpine container_name: sparkyfitness-db restart: unless-stopped networks: - sparkyfitness-network environment: POSTGRES_DB: sparkyfitness_db POSTGRES_USER: sparky POSTGRES_PASSWORD: "${SPARKY_FITNESS_DB_PASSWORD}" PUID: 1000 GUID: 1000 volumes: - /mnt/appdata/sparkyfitness/data/postgresql:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U sparky -d sparkyfitness_db"] interval: 10s timeout: 5s retries: 5 start_period: 30s deploy: resources: limits: cpus: "1.0" memory: 512M logging: driver: "json-file" options: max-size: "10m" max-file: "3" # ── Backend Server ─────────────────────────────────────────────────────────── sparkyfitness-server: image: codewithcj/sparkyfitness_server:v0.16.6.1 container_name: sparkyfitness-server restart: unless-stopped networks: - sparkyfitness-network environment: # Database connection 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: "${SPARKY_FITNESS_DB_PASSWORD}" SPARKY_FITNESS_APP_DB_USER: sparky_app SPARKY_FITNESS_APP_DB_PASSWORD: "${SPARKY_FITNESS_APP_DB_PASSWORD}" # Security — values sourced from .env (git-crypt) SPARKY_FITNESS_API_ENCRYPTION_KEY: "${SPARKY_FITNESS_API_ENCRYPTION_KEY}" BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET}" # Application SPARKY_FITNESS_FRONTEND_URL: "https://fitness.castaldifamily.com" SPARKY_FITNESS_LOG_LEVEL: ERROR NODE_ENV: production TZ: Etc/UTC SPARKY_FITNESS_DISABLE_SIGNUP: "false" SPARKY_FITNESS_FORCE_EMAIL_LOGIN: "true" ALLOW_PRIVATE_NETWORK_CORS: "false" PUID: 1000 GUID: 1000 # Garmin microservice (disabled — service not yet stable) # GARMIN_MICROSERVICE_URL: http://sparkyfitness-garmin:8000 volumes: - /mnt/appdata/sparkyfitness/data/backup:/app/SparkyFitnessServer/backup - /mnt/appdata/sparkyfitness/data/uploads:/app/SparkyFitnessServer/uploads depends_on: sparkyfitness-db: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:3010/ 2>&1 | grep -q '.' || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 60s deploy: resources: limits: cpus: "1.5" memory: 512M logging: driver: "json-file" options: max-size: "10m" max-file: "3" # ── Frontend ───────────────────────────────────────────────────────────────── sparkyfitness-frontend: image: codewithcj/sparkyfitness:v0.16.6.1 container_name: sparkyfitness-frontend restart: unless-stopped networks: - proxy-net - sparkyfitness-network environment: SPARKY_FITNESS_FRONTEND_URL: "https://fitness.castaldifamily.com" SPARKY_FITNESS_SERVER_HOST: sparkyfitness-server SPARKY_FITNESS_SERVER_PORT: 3010 PUID: 1000 GUID: 1000 depends_on: sparkyfitness-server: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -q --spider http://localhost:80/ || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 30s labels: # Enable Traefik - "traefik.enable=true" # HTTPS Router - "traefik.http.routers.sparkyfitness.rule=Host(`fitness.castaldifamily.com`)" - "traefik.http.routers.sparkyfitness.entrypoints=websecure" - "traefik.http.routers.sparkyfitness.tls=true" - "traefik.http.routers.sparkyfitness.tls.certresolver=cloudflare" - "traefik.http.routers.sparkyfitness.service=sparkyfitness" - "traefik.http.routers.sparkyfitness.middlewares=security-headers@file" # Service definition - "traefik.http.services.sparkyfitness.loadbalancer.server.port=80" deploy: resources: limits: cpus: "0.5" memory: 128M logging: driver: "json-file" options: max-size: "10m" max-file: "3"