Azure Container Apps¶
This guide covers deploying a Haute pipeline to Azure Container Apps - Microsoft's managed container platform. When you merge to main, CI builds a Docker image from your pipeline, pushes it to a registry, and (once the SDK integration lands) creates a new revision of your app.
What is Azure Container Apps?
Azure Container Apps is Microsoft's serverless container service. You give it a Docker image and it runs it for you - handling scaling, load balancing, and HTTPS certificates automatically. It can even scale to zero when there's no traffic, so you only pay when the API is being used.
Platform service update is not yet implemented
Haute currently builds and pushes the Docker image for Azure Container Apps, but the automatic service update step (creating a new revision) is still in development. After CI pushes the image, your IT team will need to update the app manually until the SDK integration lands.
This target requires IT support
Azure Container Apps involves cloud infrastructure setup (registries, environments, service principals) that is done by an IT or platform team. The "Infrastructure setup" section below is written for your IT team. As an analyst, your role is to configure haute.toml and merge to main - CI and IT handle the rest.
If your organisation uses Databricks, the Databricks target is simpler and doesn't involve containers.
Prerequisites¶
- Python 3.11+ and Haute installed on your machine
- An Azure subscription (your IT team manages this)
- A Haute project initialised with the Azure Container Apps target - open your VS Code terminal and run:
Your team may have already done this
If you cloned an existing project that already has a haute.toml file, skip this step - it's already initialised.
Before you begin
Your IT team needs to set up the Azure infrastructure first (container registry, container app, service principal). If that hasn't been done yet, send them the Infrastructure setup for IT section at the bottom of this page. Once they've done it, they'll give you the values you need for the steps below.
Step 1: Configure haute.toml¶
[project]
name = "motor-pricing"
pipeline = "main.py"
[deploy]
target = "azure-container-apps"
model_name = "motor-pricing"
[deploy.container]
registry = "pricingregistry.azurecr.io"
port = 8080
base_image = "python:3.11-slim"
[deploy.azure-container-apps]
resource_group = "rg-pricing"
container_app_name = "motor-pricing"
environment_name = "pricing-env"
[test_quotes]
dir = "tests/quotes"
What each setting means¶
| Setting | What it does | Example |
|---|---|---|
target |
Tells Haute to deploy to Azure Container Apps | "azure-container-apps" |
registry |
Your ACR login server | "pricingregistry.azurecr.io" |
port |
The port your API listens on | 8080 |
resource_group |
Azure resource group containing your container app | "rg-pricing" |
container_app_name |
Name of the container app | "motor-pricing" |
environment_name |
Name of the Container Apps environment | "pricing-env" |
Step 2: Add credentials to CI¶
CI needs Azure credentials to push images to ACR and update the container app. Add these as encrypted secrets in your CI provider (your IT team will have the values from the infrastructure setup):
| Secret name | Value |
|---|---|
DOCKER_USERNAME |
Your ACR name (e.g. pricingregistry) |
DOCKER_PASSWORD |
Your ACR admin password or service principal secret |
AZURE_SUBSCRIPTION_ID |
Your Azure subscription ID |
AZURE_TENANT_ID |
Your Azure tenant ID |
AZURE_CLIENT_ID |
Your service principal app ID |
AZURE_CLIENT_SECRET |
Your service principal password |
How to add them depends on your CI provider - see GitHub Actions, GitLab, or Azure DevOps.
Step 3: Deploy by merging to main¶
You don't run any deploy command. When you merge to main, CI automatically:
- Validates your pipeline and scores test quotes
- Generates a FastAPI app and Dockerfile
- Builds the Docker image
- Pushes the image to your ACR
- (Coming soon) Creates a new revision of the container app with the new image
What does success look like?
After a successful merge to main, you should see:
- In your CI provider - all pipeline steps show green ✓ (validation, build, push)
- In the CI logs - a message like
Pushed motor-pricing:a1b2c3d to pricingregistry.azurecr.io - In the Azure Portal - your Container App shows a new revision running with the latest image
If CI is green and the container app is healthy, your pipeline is live.
Until the automatic service update lands, CI will output the image tag. Your IT team can then update the container app manually in the Azure Portal:
- Go to your Container App → Revisions
- Click Create new revision
- Update the image to the tag CI printed (e.g.
pricingregistry.azurecr.io/motor-pricing:a1b2c3d) - Click Create
Or using the Azure CLI:
az containerapp update \
--name motor-pricing \
--resource-group rg-pricing \
--image pricingregistry.azurecr.io/motor-pricing:a1b2c3d
Step 4: Test the API¶
Find your app URL in the Azure Portal: Container App → Overview → Application Url.
It will look like: https://motor-pricing.nicemeadow-abc12345.uksouth.azurecontainerapps.io
import requests
response = requests.post(
"https://motor-pricing.nicemeadow-abc12345.uksouth.azurecontainerapps.io/quote",
json=[{"IDpol": 99001, "VehPower": 7, "DrivAge": 42, "Area": "C", "VehBrand": "B12"}],
)
print(response.json())
Troubleshooting¶
"Access denied" when pushing to ACR¶
Check that your Docker credentials are correct. If using admin credentials, make sure admin access is enabled on the ACR. If using a service principal, check it has the AcrPush role on the registry.
Container app not starting¶
Check the logs in the Azure Portal: Container App → Log stream. Common causes are missing dependencies or model files inside the image.
Health check failing¶
Azure Container Apps probes your health endpoint automatically. Make sure:
- Ingress target port matches your
haute.tomlportsetting (default 8080) - The
/healthendpoint returns a 200 status code
App URL not working¶
Check that ingress is enabled on the container app and that it's accepting external traffic (not just internal).
Infrastructure setup (one-time, done by IT)¶
As an analyst, you can skip this section entirely - send this page to whoever manages your Azure subscription and they'll set things up for you.
1. Create an Azure Container Registry (click to expand)
ACR is where Docker images are stored. Create one in the Azure Portal:
- Go to the Azure Portal
- Search for Container registries → click Create
- Fill in:
- Registry name: e.g.
pricingregistry(must be globally unique) - Resource group: create one or use existing (e.g.
rg-pricing) - Location: e.g.
UK South - SKU:
Basicis fine for dev/test
- Registry name: e.g.
- Click Review + Create → Create
Note the Login server - it looks like: pricingregistry.azurecr.io
2. Create a Container Apps Environment (click to expand)
The environment is the shared network space where container apps run:
- In the Azure Portal, search for Container Apps Environments → click Create
- Fill in:
- Environment name: e.g.
pricing-env - Resource group: same as above
- Location: same as above
- Environment name: e.g.
- Click Review + Create → Create
3. Create a Container App (click to expand)
- Search for Container Apps → click Create
- Fill in:
- Container app name: e.g.
motor-pricing - Resource group: same as above
- Container Apps Environment: select the one you just created
- Container app name: e.g.
- Under Container:
- Image source: Azure Container Registry
- Registry: select your ACR
- Image: you can use a placeholder for now - Haute will update it on deploy
- Under Ingress:
- Ingress: Enabled
- Ingress traffic: Accepting traffic from anywhere (or limited, depending on your security needs)
- Target port:
8080(match theportinhaute.toml)
- Click Review + Create → Create
4. Create a Service Principal for deployments (click to expand)
A service principal is like a special account that CI uses to push images and update the app:
az ad sp create-for-rbac \
--name "haute-deploy" \
--role contributor \
--scopes /subscriptions/<your-subscription-id>/resourceGroups/rg-pricing
This outputs:
{
"appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"displayName": "haute-deploy",
"password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
Give these values to whoever is setting up CI secrets.