services: caddy: image: caddy:2-alpine@sha256:78bd35025c7ba85961dd149ae670315129954e77abe2383ac47c89106b35ade9 ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data - caddy_config:/config depends_on: api: condition: service_healthy web: condition: service_healthy restart: unless-stopped logging: driver: "json-file" options: max-size: "50m" max-file: "3" deploy: resources: limits: memory: 128M cpus: '0.5' api: build: context: . dockerfile: api/Dockerfile environment: - API_PORT=8080 - SQLITE_PATH=/data/pypx.db - GITHUB_TOKEN=${GITHUB_TOKEN:-} - GITLAB_TOKEN=${GITLAB_TOKEN:-} volumes: - api_data:/data expose: - "8080" restart: unless-stopped healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/api/health"] interval: 30s timeout: 5s retries: 3 start_period: 10s logging: driver: "json-file" options: max-size: "50m" max-file: "3" deploy: resources: limits: memory: 512M cpus: '1.5' web: build: context: . dockerfile: web/Dockerfile environment: - NUXT_API_BASE=http://api:8080/api - NUXT_PUBLIC_API_BASE=/api # EthicalAds publisher ID for pypx.app — public, not a secret (it ships # in the served HTML). Override to empty in .env to disable ads. - NUXT_PUBLIC_ADS_PUBLISHER=${NUXT_PUBLIC_ADS_PUBLISHER-pypxapp} - NUXT_PUBLIC_ADS_TYPE=${NUXT_PUBLIC_ADS_TYPE:-image} # Root app secret (32+ chars). nuxt-og-image derives its URL-signing key # from it; without it every OG image request returns 500 while pages # still render. `:?` makes compose refuse to start instead. # Generate with: openssl rand -hex 32 - NUXT_APP_SECRET=${NUXT_APP_SECRET:?set NUXT_APP_SECRET (32+ chars) in .env} expose: - "3000" depends_on: api: condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000"] interval: 30s timeout: 5s retries: 3 start_period: 15s logging: driver: "json-file" options: max-size: "50m" max-file: "3" deploy: resources: limits: memory: 512M cpus: '0.75' volumes: caddy_data: caddy_config: api_data: