Skip to main content
The service runs as a single Azure Container App plus a scheduled job. The entire footprint is one Bicep file — infra/main.bicep:
Why one warm replica? Traffic is a handful of calls per day plus the orchestrator ping, but the calls that matter are the customer’s — a forecast POST arriving on a scaled-to-zero app pays a cold start, and one arriving in the scale-in drain window gets a 503. minReplicas: 1 removes both; max one replica keeps the file-based state single-writer.

Bootstrap

The ACR doesn’t exist until the template runs, so the first deployment uses a placeholder image and then swaps in the real one:
1

Deploy the infrastructure

2

Build the image in ACR (no local Docker needed)

3

Point the app at the real image

The public URL is in the deployment output appFqdn.
Re-running step 1 is idempotent; code redeploys only need steps 2–3.

Continuous deployment

The manual GitHub Actions workflow deploy.yml runs the same ACR build + app update. It needs:
  • repository secret AZURE_CREDENTIALS (service principal with contributor on the resource group),
  • repository variables AZURE_RESOURCE_GROUP and AZURE_NAME_PREFIX.

Security posture

  • Two scoped API keys: EXTERNAL_API_KEY (hand to the customer; forecast ingestion only) and PROCESS_API_KEY (internal pipeline + orchestrator timer). /health stays open for probes.
  • Secrets live as Container Apps secrets — move to Key Vault references when the team standardizes on it.
  • Before live Engrate submissions, set the real balancing-group EICs (AREA_MARKET_IDS) and the ECC counterparty EIC. The bridge starts against Engrate staging (ENGRATE_ENVIRONMENT=staging, submissions carry Engrate’s test flag); going live is one switch — store the production key in ENGRATE_PRODUCTION_API_KEY and set ENGRATE_ENVIRONMENT=production (also possible at runtime from the console’s Engrate tab).
  • If the customer calls from fixed egress IPs, add ingress IP restrictions on the Container App.