services: postgres: image: postgres:16-alpine environment: POSTGRES_DB: quizsystem POSTGRES_USER: quizsystem POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-devpassword} volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U quizsystem -d quizsystem"] interval: 5s timeout: 5s retries: 10 networks: [quizsystem] server: build: context: . dockerfile: src/Server/Dockerfile environment: ConnectionStrings__Postgres: "Host=postgres;Port=5432;Database=quizsystem;Username=quizsystem;Password=${POSTGRES_PASSWORD:-devpassword}" Jwt__Secret: ${JWT_SECRET:?Set JWT_SECRET in .env — see .env.example} Client__Origin: ${CLIENT_ORIGIN:-http://localhost:8081} ASPNETCORE_ENVIRONMENT: Production depends_on: postgres: condition: service_healthy # Bound to loopback only: the client container is the sole public entry # point (it proxies /api/* to this service itself — see # src/Client/nginx.conf), so nothing outside this host needs to reach # the API directly. Still published on localhost for local debugging. ports: ["127.0.0.1:5144:8080"] networks: [quizsystem] client: build: context: . dockerfile: src/Client/Dockerfile # Doesn't fix the DNS race on its own (nginx.conf's lazy resolver does # that) — just avoids nginx crashing into a restart loop for no reason # by not starting client before `server` exists at all. depends_on: - server # Loopback-only in production, where a host-level nginx (TLS + the real # domain) is the actual public entry point and proxies here — see # docs/DEPLOY.md. For local `docker compose up`, still reachable at # http://localhost:8081 same as before. ports: ["127.0.0.1:8081:80"] networks: [quizsystem] volumes: pgdata: networks: quizsystem: