# The hybrid deploy: # one command - `docker compose -f deploy/docker-compose.yml up --build`. # # static -> the immutable half: the Go single binary serves prerendered # documents, build-precompressed brotli, ETag 304s and the SPA # fallback, logs one access line per request to stdout and # answers /metrics (Prometheus text format). # dynamic -> the live half: server actions, /api handlers, middleware, # $data - the Node server, same code, same compiler output. # # Every request the static half cannot answer itself (any POST, any /api # route without a baked body) is reverse-proxied to `dynamic` via UPSTREAM. # Drop the UPSTREAM line and the static service becomes the pure-static # deploy: POSTs answer 405, unbaked APIs answer the JSON 404. services: static: build: context: .. dockerfile: deploy/Dockerfile.static environment: PORT: "8080" UPSTREAM: http://dynamic:3000 ports: - "8080:8080" depends_on: dynamic: condition: service_healthy restart: unless-stopped dynamic: image: node:22-alpine working_dir: /app # Build inside the container: the host's node_modules may hold # foreign-platform binaries (esbuild ships one per OS). command: sh -c "npm ci && npm run build && npm run preview" environment: PORT: "3000" volumes: - ..:/app - /app/node_modules healthcheck: test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:' + (process.env.PORT || 3000) + '/').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"] interval: 5s timeout: 3s retries: 20 restart: unless-stopped