← Back to docs
guides·Updated May 2, 2026

Deployment Guide

How to build, deploy, and operate ArrowFlow on Azure Container Apps — from ACR image builds to production promotion.

ArrowFlow is deployed as a Docker container on Azure Container Apps (ACA). This guide covers the full deployment lifecycle from building an image to promoting to production.

Architecture Overview

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  ACR Build   │────▶│   Staging    │────▶│  Production  │
│  (image tag) │     │  arrowflow-  │     │  arrowflow-  │
│              │     │   staging    │     │     app      │
└──────────────┘     └──────────────┘     └──────────────┘
ComponentResourceURL
Productionarrowflow-apphttps://app.arrowflow.app
Stagingarrowflow-stagingOn-demand
Websitearrowflow-websitehttps://arrowflow.app
Registryarrowflowacr.azurecr.io
Environmentarrowflow-app-envNorth Central US

Prerequisites

  • Azure CLI (az) logged in
  • Access to subscription SAWORKS DEV Subs
  • Resource group: arrowflow-rg

Step 1: Build the Image

ArrowFlow uses a multi-stage Dockerfile (Dockerfile.backend):

  • Stage 1: Build the Vite frontend
  • Stage 2: Node 20 Alpine production image with backend + frontend dist
# Build and push to ACR
VERSION=v3.5.0
SHORT_SHA=$(git rev-parse --short HEAD)
IMAGE_TAG="${VERSION}-${SHORT_SHA}"

az acr build \
  --registry arrowflowacr \
  --image arrowflow-app:${IMAGE_TAG} \
  --file Dockerfile.backend .

Step 2: Deploy to Staging

Deploy using the Bicep template to validate before production:

az deployment group create \
  --resource-group arrowflow-rg \
  --template-file infra/staging.bicep \
  --parameters containerImage="arrowflowacr.azurecr.io/arrowflow-app:${IMAGE_TAG}" \
  --parameters cosmosConnectionString="<secret>" \
  --parameters storageConnectionString="<secret>" \
  --parameters clerkSecretKey="<secret>" \
  --parameters clerkPublishableKey="<secret>" \
  --parameters credentialMasterKey="<secret>" \
  --parameters adminApiKey="<secret>" \
  --parameters payloadInternalApiKey="<secret>" \
  --parameters payloadContentEncryptionKey="<secret>"

Smoke Test Staging

STAGING_FQDN=$(az containerapp show --name arrowflow-staging \
  --resource-group arrowflow-rg --query "properties.configuration.ingress.fqdn" -o tsv)

curl -s "https://${STAGING_FQDN}/api/health" | jq .
curl -s -o /dev/null -w "%{http_code}" "https://${STAGING_FQDN}/"

Step 3: Promote to Production

Update the production container app to use the new image:

az containerapp update \
  --name arrowflow-app \
  --resource-group arrowflow-rg \
  --image "arrowflowacr.azurecr.io/arrowflow-app:${IMAGE_TAG}"

Verify Production

curl -s https://app.arrowflow.app/api/health | jq .
curl -s -o /dev/null -w "%{http_code}" https://app.arrowflow.app/

Step 4: Tear Down Staging

Once validated, remove the staging app to save costs:

az containerapp delete --name arrowflow-staging \
  --resource-group arrowflow-rg --yes

Production Container Resources

SettingValue
CPU0.75 vCPU
Memory1.5 Gi
Min replicas1
Max replicas10
Scale ruleHTTP — 50 concurrent requests
IdentitySystemAssigned (Key Vault RBAC)

Required Secrets

All secrets are stored as ACA secrets and mapped to environment variables:

Env VarSecret NamePurpose
AZURE_COSMOS_CONNECTION_STRINGcosmos-conn-strCosmosDB access
AZURE_STORAGE_CONNECTION_STRINGstorage-conn-strBlob storage
CLERK_SECRET_KEYclerk-secret-keyAuth backend
CLERK_PUBLISHABLE_KEYclerk-publishable-keyAuth frontend
CREDENTIAL_MASTER_KEYcredential-master-keyAES-256-GCM key encryption
ADMIN_API_KEYadmin-api-keyAdmin API authentication
PAYLOAD_INTERNAL_API_KEYpayload-internal-api-keyPayload CMS internal
PAYLOAD_CONTENT_ENCRYPTION_KEYpayload-content-encryption-keyPayload 64-char hex
AZURE_FOUNDRY_API_KEYfoundry-api-keyAzure AI Foundry

Non-secret env vars (set as plain values):

  • NODE_ENV=production
  • AZURE_FOUNDRY_ENDPOINT=https://arrowflow-ai.cognitiveservices.azure.com/

Clerk Authentication

The app uses Clerk for authentication with a custom domain setup:

  • Frontend API: clerk.arrowflow.app
  • Sign-in domain: app.arrowflow.app

Redirect URLs (Clerk Dashboard)

These must be added to the Clerk dashboard under "Redirect URLs":

  • https://app.arrowflow.app/
  • https://arrowflow-app.politehill-bd759da7.northcentralus.azurecontainerapps.io/

If deploying staging with auth, also add the staging FQDN.

Rollback

To roll back to a previous image:

# List recent revisions
az containerapp revision list \
  --name arrowflow-app \
  --resource-group arrowflow-rg \
  --query "[].{name:name, active:properties.active, traffic:properties.trafficWeight}" -o table

# Revert to previous image tag
az containerapp update \
  --name arrowflow-app \
  --resource-group arrowflow-rg \
  --image "arrowflowacr.azurecr.io/arrowflow-app:<previous-tag>"

Reference Files

  • deployment/production.env — Full production ACA config (non-secret values)
  • infra/staging.bicep — Staging Bicep template
  • Dockerfile.backend — Multi-stage build definition
  • .github/instructions/deployment.instructions.md — Agent deployment instructions