# System Design — QA Physical Architecture Physical deployment topology for the QA environment. Source: `deploy/qa/docker-compose.qa.yml`, `scripts/ci/*.sh`, `.github/workflows/ci.yml`. ```mermaid flowchart TB subgraph CI["Self-hosted CI runner (qa)"] Pipeline["deploy-qa.sh / smoke-qa.sh"] end subgraph Registry["Gitea Container Registry"] ApiImage["miccheck-api:qa"] AdminImage["miccheck-admin:qa"] end subgraph QAHost["QA Docker host — network: miccheck-qa-net (bridge)"] subgraph AdminC["admin container
nginx:1.27-alpine"] Nginx["nginx
serves Vue SPA
proxies /api/, /health"] end subgraph ApiC["api container
aspnet:10.0"] Api["MicCheck.Api
ASPNETCORE_URLS=http://+:8080"] end subgraph DbC["db container
postgres:16-alpine"] Db[("miccheck DB")] end end Browser["Browser / QA tester"] Pipeline -->|docker build/push| Registry Registry -->|pull :qa| AdminC Registry -->|pull :qa| ApiC Browser -->|":3001 (QA_ADMIN_PORT)"| Nginx Nginx -->|"http://api:8080 (internal)"| Api Api -->|"Host=db;5432 (internal)"| Db Pipeline -.->|"127.0.0.1:55432 (bridge-gateway bound)"| Db ``` ## Components | Component | Image | Exposure | Notes | |---|---|---|---| | `admin` | `nginx:1.27-alpine` (built from `src/admin/Dockerfile.ci`, `node:22-alpine` build stage) | `${QA_ADMIN_PORT:-3001}:80` published on host | Serves Vue/Vite SPA; nginx (`src/admin/nginx.conf`) reverse-proxies `/api/` and `/health` to `api:8080` | | `api` | `mcr.microsoft.com/dotnet/aspnet:10.0` (built from `src/api/MicCheck.Api/Dockerfile.ci`, `sdk:10.0` build stage) | no published host port — internal only, reached via `admin`'s nginx proxy | `ASPNETCORE_URLS=http://+:8080`; JWT config (`Jwt__SecretKey`/`Issuer=MicCheck`/`Audience=MicCheck`) from `JWT_SECRET_KEY` secret; healthcheck `curl localhost:8080/health` | | `db` | `postgres:16-alpine` | `${DB_BIND_HOST:-127.0.0.1}:55432 → 5432`, bound to docker bridge gateway IP (not `0.0.0.0`) — reachable only from sibling CI containers, not off-box | DB `miccheck`, user `miccheck`, password from `POSTGRES_PASSWORD` secret; volume `miccheck-qa-pgdata` | All three services run on an isolated bridge network `miccheck-qa-net` (project `miccheck-qa`), separate from the local dev Aspire stack. ## Deploy flow 1. `build-and-push` job builds `api` and `admin` images, tags with git SHA and `qa`, pushes to Gitea registry as `$REGISTRY/$REGISTRY_OWNER/miccheck-api` / `miccheck-admin`. 2. `deploy-qa` job (self-hosted runner, `main` branch only) pulls `:qa` images and runs `docker-compose.qa.yml` via `deploy-qa.sh`. 3. `smoke-qa` job hits the deployed stack (`smoke-qa.sh`) to verify health. Secrets used: `REGISTRY`, `REGISTRY_OWNER`, `REGISTRY_USER`, `REGISTRY_TOKEN`, `JWT_SECRET_KEY`, `POSTGRES_PASSWORD`. Vars: `QA_ADMIN_PORT`.