This repository deploys Open WebUI on Azure Container Apps using Terraform, including:
- Resource Group
- Storage Account with two Azure File Shares
- Container Apps Environment
- User-assigned managed identity
- Container App with persistent mounts, external ingress, custom domain, managed certificate, autoscaling, and health probes
- Key Vault secret references via managed identity (no plaintext secrets in Terraform code)
azurerm_storage_share.modelsmounted at/app/chat_frontend/modelsazurerm_storage_share.backend_datamounted at/app/backend/data- External ingress enabled on port
8080 - Custom domain bound via
azurerm_container_app_custom_domainwith Azure-managed TLS certificate (certificate_binding_type = Autobehavior managed by Azure) - Scaling configured between
1and10replicas - CPU custom scale rule with
75%utilization target - Liveness and readiness probes on
/health
- Secrets are not hardcoded in
.tffiles. - Secret values are not read by Terraform. The app uses Key Vault Secret Identifier URIs through:
- Container App
secret { key_vault_secret_id = ... identity = ... } - User-assigned managed identity
Key Vault Secrets UserRBAC assignment on the existing Key Vault
- Container App
.tfvarsand state files are ignored by.gitignore.
Note: Azure Container Apps environment storage currently requires an Azure Files access key in the environment storage resource. This value is provider-managed and sensitive, but may still appear in Terraform state metadata depending on provider behavior.
versions.tf- Terraform and provider versionsproviders.tf- AzureRM provider configurationvariables.tf- documented inputsmain.tf- base infrastructure resourcescontainer_app.tf- Open WebUI container app (identity, secrets, probes, scaling, mounts, ingress)dns_custom_domain.tf- custom domain binding and optional Azure DNS recordsoutputs.tf- useful outputs for validation/demoterraform.tfvars.example- sample variable values
- Terraform
>= 1.5 - Azure CLI logged in (
az login) - Sufficient permissions to create:
- Resource group resources
- Role assignments
- Container Apps resources
- Storage resources
- Existing Azure Key Vault with RBAC enabled and at least one secret (for example
WEBUI_SECRET_KEY) - A domain name for custom domain binding
- Copy the example variables:
cp terraform.tfvars.example terraform.tfvars-
Edit
terraform.tfvars:- Set
subscription_id,resource_group_name,location - Set
key_vault_id - Set
key_vault_secretswith secret URIs and environment variable mapping - Set
custom_domain - If DNS zone is in Azure and you want Terraform to create records, set:
create_azure_dns_records = truedns_zone_namedns_zone_resource_group_name
- Set
-
Initialize and deploy:
terraform init
terraform plan -out tfplan
terraform apply tfplanIf create_azure_dns_records = true, Terraform creates:
- TXT:
asuid.<subdomain>withcustom_domain_verification_id - CNAME:
<subdomain>-> Container App FQDN
If DNS is outside Azure:
- Use
terraform output custom_domain_verification_idto create TXT record manually - Use
terraform output container_app_default_fqdnfor CNAME target - Re-run
terraform applyafter DNS propagates
Azure provisions the managed certificate asynchronously after domain validation.
terraform planandterraform applyrun successfully- Open WebUI reachable at
https://<custom_domain> - Data persists after app restart:
- files under
/app/backend/data - files under
/app/chat_frontend/models
- files under
- Autoscaling:
- min replicas =
1 - max replicas =
10 - CPU rule target
75
- min replicas =
- Secrets:
- loaded from Key Vault URI references
- exposed as env vars through container app secret mapping
- Health probes:
- liveness
/health - readiness
/health
- liveness
terraform output
terraform state list
az containerapp show -g <resource_group> -n <app_name> --query "properties.configuration.ingress.fqdn" -o tsv