Azure Installation
Deploy Broch on Azure with Container Apps, Postgres Flexible Server, and Key Vault for secrets. The Terraform module lives in broch-io/broch-deploy/terraform/azure-container-apps — clone the repo, fill in terraform.tfvars, terraform apply, then bind the custom domain.
Marketplace listing: an Azure Marketplace listing with a Bicep template is on the roadmap. Until then, the Terraform module is the supported deployment path.
What gets provisioned
Section titled “What gets provisioned”- Resource group + Log Analytics workspace (required by Container Apps Environment)
- Container Apps Environment + Container App with HTTPS ingress
- User-assigned managed identity for the Container App
- Key Vault (RBAC mode) holding the generated
BROCH_MASTER_KEY, the OIDC client secret, and the Postgres connection string - Postgres Flexible Server v16 + database, with the
AllowAzureServicesfirewall rule - Custom domain stub on the Container App (binding requires a one-time manual step — see below)
All resources are created in your subscription. Broch has no access to your environment.
This stack runs Container Apps + Postgres Flexible Server, with no equivalent of an ALB or NAT gateway — a simpler footprint than the AWS stack. To estimate spend, use the Azure Pricing Calculator with your chosen sizes.
Prerequisites
Section titled “Prerequisites”- Terraform 1.6+ + Azure CLI (
az loginagainst the target subscription) - Owner or Contributor + User Access Administrator on the target subscription (Key Vault RBAC role assignment needs the latter)
- DNS control for your wildcard hostname’s parent domain (Azure DNS or any provider — you create records yourself)
- Wildcard domain & DNS configured
- Identity provider app registration created
- License — activated in-app after first sign-in (nothing to set before deploy)
az loginaz account set --subscription <subscription-id>
git clone https://github.com/broch-io/broch-deploy.gitcd broch-deploy/terraform/azure-container-apps
cp terraform.tfvars.example terraform.tfvars$EDITOR terraform.tfvars
terraform initterraform planterraform applyThis gets the Container App running on its default *.azurecontainerapps.io hostname. The custom-domain binding is a separate one-time manual step because Azure needs you to prove DNS control before it’ll bind the cert.
Bind the custom domain (one-time, post-apply)
Section titled “Bind the custom domain (one-time, post-apply)”VERIF_ID=$(terraform output -raw container_app_verification_id)HOSTNAME=$(terraform output -raw broch_url | sed 's|https://||')
# Add these DNS records in your provider:# A <hostname> → IP of the Container App# TXT asuid.<hostname> → $VERIF_ID# Azure requires the TXT to validate ownership.
az containerapp hostname bind \ --hostname "$HOSTNAME" \ --resource-group broch-rg \ --name broch-app \ --validation-method CNAMERe-run as needed if cert provisioning hasn’t propagated (~5–10 min).
Wildcard cert — the Azure-vs-AWS gap
Section titled “Wildcard cert — the Azure-vs-AWS gap”Azure Container Apps’ managed certs don’t issue wildcards. For tunnel subdomains (*.tunnels.example.com), you have three options:
- Front Door / Application Gateway with a managed wildcard in front of Container Apps. Adds a Front Door resource but is the cleanest production answer.
- Provision a wildcard cert separately (Let’s Encrypt via certbot + DNS-01, or a commercial CA) and upload it via
az containerapp env certificate upload+az containerapp hostname bind. - Skip wildcards if your deployment doesn’t use tunnel subdomains.
The module README covers the binding flow in more detail.
Upgrading
Section titled “Upgrading”$EDITOR terraform.tfvars # set broch_image = "ghcr.io/broch-io/broch:1.6.0"terraform applyContainer Apps rolls out a new revision with the new image. Old revision drains based on the ingress traffic-weight config (100% to latest by default).
Database migrations run automatically on the new revision’s first start.
Rotating secrets
Section titled “Rotating secrets”Edit the Key Vault secret value, then restart the Container App revision:
az keyvault secret set \ --vault-name $(terraform output -raw key_vault_name) \ --name auth-client-secret \ --value "<new-client-secret>"
az containerapp revision restart \ --name broch-app \ --resource-group broch-rg \ --revision $(az containerapp revision list --name broch-app --resource-group broch-rg --query '[0].name' -o tsv)Backup
Section titled “Backup”Postgres Flexible Server performs automated daily backups with point-in-time restore; retention is fixed at 7 days. Restore through the Azure portal or az postgres flexible-server restore. Take a manual backup before risky operations — see the Azure Database for PostgreSQL backup docs.
Tradeoffs / what the default module is not
Section titled “Tradeoffs / what the default module is not”- Public-access Postgres with the
AllowAzureServicesfirewall rule. For VNet-private Postgres, adddelegated_subnet_id+private_dns_zone_idto the postgres resource and drop the firewall rule. - Single replica (
min_replicas = max_replicas = 1). No autoscaling configured — broch’s workload is usually steady-state and predictable bill is preferable. - No Front Door in front of Container Apps. Add it when you want wildcard certs managed automatically, or for global edge caching.
purge_protection_enabled = falseon Key Vault for fast iteration. Flip totruebefore going to real production.
See the module README for the full list.
Azure VM + Docker Compose (alternative)
Section titled “Azure VM + Docker Compose (alternative)”If you’d rather run on an Azure VM with Docker Compose (simpler, single-VM), use the Docker Compose installation guide and run it on an Ubuntu VM you provision yourself.
Teardown
Section titled “Teardown”terraform destroyKey Vault soft-delete means the vault sticks around for 7 days after destroy. If you want to re-apply with the same name immediately, either change name_prefix in tfvars or purge: az keyvault purge --name <vault-name> (needs the Key Vault Contributor role).