Azure application hosting¶
Azure Container Apps is the backend platform (issue #563 migrated it off Azure App Service). The
frontend has separately moved off Azure App Service entirely, onto Cloudflare Pages (issue #564; see
docs/deployment/frontend-cloudflare-pages.md), because its build output is a static bundle with no
server-side runtime. The historical frontend and backend App Service resources are both retained as
acceptance-period/rollback targets — see the per-component docs for details — but neither is the normal
deployment path any longer.
ADR 0003 records the original App Service decision for both components and has been marked superseded
for both; see docs/adr/0003-azure-hosting.md.
Selected mapping¶
| Component | Service | Runtime/build context |
|---|---|---|
| React frontend | Cloudflare Pages | Node.js 22 LTS build environment; static Vite bundle |
| Express backend API | Azure Container Apps | Node.js 22 non-root production container |
| Async batch worker | Azure Container Apps | Node.js 22 LTS non-root container |
| Private object bytes | Azure Blob Storage | Backend managed identity and private container |
The PostgreSQL database and managed authentication remain on Supabase. The public MkDocs documentation site is hosted separately on Cloudflare Pages, alongside the frontend application (a separate Pages project).
The current public application endpoints are:
| Component | URL |
|---|---|
| Frontend | https://sport-analytics-tool-web.pages.dev/ |
| Backend API | https://statsthegame-dev-api.calmground-aa50efe2.southafricanorth.azurecontainerapps.io/api/v1 |
App Service history, Container Apps migration and frontend static hosting¶
ADR 0003 records the original App Service reasons:
- Azure student resources;
- managed HTTPS;
- native Node.js support; and
- a relatively simple deployment model for Sprint 1.
The backend now uses the existing Container Apps environment, ACR, Key Vault and Blob Storage with
separate pull and runtime managed identities. Its ingress is external HTTPS-only, its probes use
/api/v1/health, and its normal deployment is an immutable container image. The worker remains a
separate Container App with the Service Bus boundary; the API has no Service Bus configuration.
The historical backend App Service statsthegame-api-dev may remain as an Azure resource for
incident evidence or later decommissioning, but it is not a supported deployment or recovery target.
Backend manual recovery now redeploys the existing statsthegame-dev-api Container App through the
same Bicep and immutable-image path used by automatic CI.
The frontend is a pre-built static bundle with no server-side runtime, so it does not need App Service
compute at all: it is now deployed to a Cloudflare Pages project and served from Cloudflare's edge, with
client-side routing preserved through a Pages _redirects SPA fallback. The historical frontend App
Service statsthegame-web-dev is kept live in parallel only until Cloudflare Pages acceptance succeeds,
then retired — see Frontend deployment (Cloudflare Pages) for the
acceptance checklist, required secrets, backend CORS and Supabase Auth configuration, and the retirement
sequence.
Once the frontend's Azure App Service deployment is retired, neither the frontend nor the backend's normal deployment path depends on Azure App Service; the shared App Service Plan can then be reviewed for removal (see the same document's retirement section).
Other alternatives recorded¶
Azure Static Web Apps¶
Advantages:
- optimised for static frontend hosting.
Reason not selected for the original foundation:
- would have introduced a separate hosting/deployment path while the team initially selected App Service for both application components. The frontend has since moved to Cloudflare Pages instead, which the project already used for its documentation site, avoiding introducing a third hosting provider.
Deployment verification¶
The frontend workflow builds the Vite bundle, scans it for embedded server-only secrets, then deploys it
to Cloudflare Pages with Wrangler and smoke checks the public URL. The backend workflow builds a Node 22
container, proves its local health endpoint with inert configuration, pushes an immutable SHA image,
deploys Bicep, waits for the matching healthy revision, then verifies both /api/v1/health and a
read-only database-backed competition request.
Workflow definitions are not evidence of a successful release by themselves. Each release must retain the corresponding passing Gitea Action link and smoke-check output. Rollback evidence and runner availability remain operational evidence rather than claims made by this technology-selection document.
The Container Apps backend runtime identity accesses the private staged-ingestion container in
statsthegameblobdev through DefaultAzureCredential; the container receives only non-secret Blob
identifiers. See the private object-storage operations guide for the
external RBAC and verification requirements.
AI Declaration¶
The preceding document was reviewed and aligned with the accepted Azure ADR with the assistance of ChatGPT-Web[GPT-5.6 Sol] and updated for the automated deployment checks with the assistance of Codex[GPT-5]. The managed-identity Blob Storage deployment mapping was updated with the assistance of Codex[GPT-5]. The separately scoped Container Apps worker mapping was updated with the assistance of Codex[GPT-5]. The Issue #563 backend Container Apps migration documentation was updated with the assistance of Codex[GPT-5]. The Issue #564 frontend Cloudflare Pages migration documentation was updated with the assistance of Claude (Anthropic).