Home / Azure

AzureTroubleshootingIntermediate

Azure Container Apps revision won't start: image pulls, crash loops, probes and ingress

Diagnose a Container Apps revision stuck in Provisioning, Degraded or Failed, or that runs but returns errors, and roll traffic back to the last good revision.

Deepak Bhardwaj8 min read

Applies toAzure Container Apps (Consumption and workload profiles environments), Azure CLI 2.x with the containerapp extension

You pushed a new image, the deployment "succeeded", and the app is down. The new revision shows Provisioning for ten minutes, then Degraded with Deployment Progress Deadline Exceeded. 0/1 replicas ready. Or the revision is Running but every request returns 404 or times out. This guide covers how to get traffic back to a working revision first, then find which of the four usual causes broke the new one: image pull, a crashing container, failing health probes, or ingress settings.

First: get traffic back to a working revision

Find the last revision that worked before you start debugging.

APP=ca-orders-api; RG=rg-orders-prod

az extension add --name containerapp --upgrade

az containerapp revision list -n $APP -g $RG --all \
  --query "[].{name:name, created:properties.createdTime, active:properties.active, traffic:properties.trafficWeight, health:properties.healthState, running:properties.runningState, prov:properties.provisioningState}" \
  -o table

az containerapp show -n $APP -g $RG \
  --query "{mode:properties.configuration.activeRevisionsMode, latest:properties.latestRevisionName, latestReady:properties.latestReadyRevisionName}"

If latestRevisionName and latestReadyRevisionName are different, the new revision never became ready. The difference between multiple and single revision mode matters here:

  • Multiple revision mode: activate the old revision and send all traffic to it.

    az containerapp revision activate -n $APP -g $RG --revision ca-orders-api--v41
    az containerapp ingress traffic set -n $APP -g $RG --revision-weight ca-orders-api--v41=100
    
  • Single revision mode: the platform normally keeps the old revision serving until the new one is ready. If it doesn't, deploy the previous image tag again:

    az containerapp update -n $APP -g $RG --image acrorders.azurecr.io/orders-api:1.41.0
    

Warning

Redeploying with a mutable tag such as latest can pull the broken image again. Roll back to an immutable tag or digest (orders-api@sha256:...).

Where to look

Symptom Most likely cause Go to
Deployment fails right away, or revision is Failed with an image error Registry auth, network path to the registry, or wrong tag Image pull
Revision Degraded, replicas restart repeatedly App exits on start: missing env var or secret, bad config, out of memory Crash loop
Provisioning/Processing for minutes, then Degraded with "0/1 replicas ready" Probes fail: wrong port, path, or the app starts too slowly Health probes
Revision Running and Healthy, requests fail with 404, 403 or timeout Ingress type, target port, IP restrictions, internal environment Ingress
Revision healthy, but responses come from old code Traffic still split to an older revision, or the tag was reused Traffic split

Read the logs for the bad revision

System logs show what the platform did (pulls, probe results, restarts). Console logs show your container's stdout/stderr.

REV=ca-orders-api--v42

az containerapp logs show -n $APP -g $RG --revision $REV --type system --tail 100 --format text

az containerapp replica list -n $APP -g $RG --revision $REV -o table

az containerapp logs show -n $APP -g $RG --revision $REV \
  --replica <replica-name> --container orders-api --type console --tail 100 --format text

If no replica exists (the revision scaled to zero or never started), use Log Analytics. With the environment's logs sent to Log Analytics directly, the tables are ContainerAppSystemLogs_CL and ContainerAppConsoleLogs_CL. With diagnostic settings, they are ContainerAppSystemLogs and ContainerAppConsoleLogs, and the column names don't have the _s suffix. Check which one your environment uses.

ContainerAppSystemLogs_CL
| where TimeGenerated > ago(1h)
| where ContainerAppName_s == "ca-orders-api" and RevisionName_s == "ca-orders-api--v42"
| project TimeGenerated, Reason_s, Log_s
| order by TimeGenerated desc
ContainerAppConsoleLogs_CL
| where TimeGenerated > ago(1h)
| where RevisionName_s == "ca-orders-api--v42"
| where Log_s has_any ("error", "exception", "fatal", "denied")
| project TimeGenerated, ContainerName_s, Log_s

Cause 1: image pull fails

Look for pull, unauthorized or manifest errors in the system log.

# Which registry credentials does the app use?
az containerapp registry list -n $APP -g $RG -o table

# Does the tag exist?
az acr repository show-tags -n acrorders --repository orders-api --orderby time_desc --top 5 -o table

If you use a managed identity, check that the identity has AcrPull on the registry:

PRINCIPAL=$(az containerapp show -n $APP -g $RG --query identity.principalId -o tsv)
ACR_ID=$(az acr show -n acrorders --query id -o tsv)

az role assignment list --assignee $PRINCIPAL --scope $ACR_ID -o table

# Fix: grant the role, then point the app at the registry with that identity
az role assignment create --assignee $PRINCIPAL --role AcrPull --scope $ACR_ID
az containerapp registry set -n $APP -g $RG --server acrorders.azurecr.io --identity system

In a VNet-integrated environment, also check:

  • Does a firewall or route table (UDR) block egress to the registry?
  • Does custom DNS resolve acrorders.azurecr.io (and its private endpoint, if it has one)?
  • Does the custom DNS forward unresolved queries to 168.63.129.16?

Cause 2: the container crashes on start

Console logs usually show the reason, such as a stack trace, KeyError, a connection refused to a database, or nothing at all, which often means an out-of-memory kill. Compare the new revision's template with the last good one:

az containerapp revision show -n $APP -g $RG --revision ca-orders-api--v41 --query properties.template > good.json
az containerapp revision show -n $APP -g $RG --revision $REV --query properties.template > bad.json
diff good.json bad.json

Common findings:

  • A new environment variable references a secret that isn't defined on the app.
  • A Key Vault secret reference the identity can't read.
  • CPU/memory lowered below what the runtime needs.
  • A changed command or args.

To check the environment from inside a running replica:

az containerapp exec -n $APP -g $RG --revision ca-orders-api--v41 --command "sh"

Cause 3: health probes fail

If you define no probes and ingress is enabled, Container Apps adds TCP startup and readiness probes on the ingress target port. If the app listens on a different port, or takes longer to start than the probes allow, the revision never becomes ready.

az containerapp ingress show -n $APP -g $RG --query "{targetPort:targetPort, external:external, transport:transport}"
az containerapp show -n $APP -g $RG --query "properties.template.containers[0].probes"

Probe settings aren't exposed as az containerapp update flags. Export the app to YAML, edit the probes, and apply the file:

az containerapp show -n $APP -g $RG -o yaml > app.yaml
# edit template.containers[0].probes: port, path, initialDelaySeconds, failureThreshold
az containerapp update -n $APP -g $RG --yaml app.yaml
probes:
  - type: Startup
    httpGet: { path: /healthz, port: 8080 }
    periodSeconds: 5
    failureThreshold: 30      # allows ~150 s to start
  - type: Readiness
    httpGet: { path: /ready, port: 8080 }
    periodSeconds: 10

Tip

Don't make the readiness probe check downstream dependencies such as the database. If the database has a problem, every replica goes unready together and you lose the whole app instead of degrading.

Cause 4: revision healthy, requests still fail

Response Check
404 Container App is stopped or does not exist from inside the VNet On an internal environment, ingress set to "Limited to Container Apps Environment". Set --type external (on an internal environment this doesn't make it public)
403 IP security restrictions, or client certificate mode set to Require
Timeout / connection reset Target port doesn't match the port the app listens on, HTTP vs TCP transport, NSG or firewall in front of the environment
Old responses Traffic weights in multiple mode, or a reused image tag
az containerapp ingress update -n $APP -g $RG --target-port 8080
az containerapp ingress access-restriction list -n $APP -g $RG -o table
az containerapp ingress traffic show -n $APP -g $RG -o table

Prevent it next time

  • Deploy immutable tags or digests. Never deploy latest to production.
  • Use multiple revision mode for production apps. Deploy the new revision with 0% traffic, check it through its revision URL or a label, then shift traffic.
  • Define explicit startup and readiness probes on the real port, with a startup budget that matches your slowest observed start.
  • Alert on system logs for the app (pull failures, probe failures, restarts) instead of waiting for users to report it.
  • Use a managed identity with AcrPull for registry access. Don't use admin credentials that can be rotated out from under you.