Container Apps deployment failures: ImagePullBackOff, failing probes, the wrong target port and identity 403s
Azure Container Apps deployment failures come from a short list: ImagePullBackOff and registry sign-in, a container that crashes on start, health probes that fail, an ingress target port that doesn't match, empty secrets, and a managed identity without the right role. Here is the symptom table, the fix for each, and the six CLI commands that find the cause.
Apps & integrationFix a problem8 min read
A container that runs perfectly on your laptop fails to deploy to Azure Container Apps, and the portal says "Degraded" or "Failed" without saying why. The good news is that Container Apps deployment failures come from a short list of causes, and each leaves a distinct trace in the revision state or the system logs. This guide is Microsoft's symptom table in plain English, the fix for each row, and the six commands that get you from "it's broken" to the cause in a few minutes.
Six commands that find the cause
Run these in order; each one tells you whether to stop or go on.
# 1. App state: provisioningState, provisioningError, latestRevisionName
az containerapp show -g <rg> -n <app> -o json
# 2. Revisions. Empty list = image pull or template problem. Otherwise continue.
az containerapp revision list -g <rg> -n <app> -o table
# 3. One revision: healthState, runningState, runningStateDetails, provisioningError
az containerapp revision show -g <rg> -n <app> --revision <revision> -o json
# 4. Platform events: image pulls, probe results, identity and secret errors
az containerapp logs show -g <rg> -n <app> --type system --tail 50
# 5. Your app's stdout and stderr: stack traces, config errors
az containerapp logs show -g <rg> -n <app> --tail 100
# 6. Identity, if anything above mentioned 401 or 403
az containerapp identity show -g <rg> -n <app>
In the portal the same lives under Diagnose and solve problems (automated detectors for image pull, probes, Key Vault and ingress), Observe > Log Stream (System logs and Console logs) and Configure > More > Revisions & Replicas. For anything deeper, Log Analytics has three tables: ContainerAppSystemLogs (platform), ContainerAppConsoleLogs (your app, with the replica name) and ContainerAppHTTPLogs (ingress, once you enable it on the environment).
No revision is created
The platform failed before your code ran, almost always at the image pull. The system logs say which:
| The log says | Fix |
|---|---|
manifest unknown or tag not found | The tag doesn't exist in the registry. Check with az acr repository show-tags; a CI build that pushed latest but deployed a version tag is the usual story |
UNAUTHORIZED: authentication required | The app has no credentials for the registry. Best fix: az containerapp registry set --server <acr>.azurecr.io --identity system, with the AcrPull role on the app's identity. Or enable the registry's admin user and store its password as a secret |
UNAUTHORIZED with a managed identity configured | The identity lacks AcrPull: az role assignment create --role AcrPull --assignee <principal> --scope <acr id> |
name unknown or a DNS failure | The image name's host is wrong; it must be <registry>.azurecr.io/<repo>:<tag> |
| A timeout reaching the registry | In a custom virtual network, outbound access is blocked. Allow the AzureContainerRegistry service tag or the registry's FQDNs |
The revision exists but is not healthy
It crashes on start
CrashLoopBackOff, or a container that exits at once. Read the console logs first:
| You see | Fix |
|---|---|
| A stack trace | Your bug; fix it. If the logs are empty, the crash happens before output is flushed: log earlier, and disable buffering (PYTHONUNBUFFERED=1 for Python) |
exec format error or executable file not found | The command and args on the container don't match the image, or the image was built for another CPU architecture |
KeyError, undefined, a null reference at start | A required environment variable isn't set; see the secrets section below |
OOMKilled in the system logs | Raise the limits: az containerapp update --cpu 1.0 --memory 2.0Gi |
| The container starts and then exits with no error | The image's entrypoint runs a command that finishes. A web app's CMD must start the server, not just build it |
The probes fail
The revision is Degraded with 0 ready replicas, and the system logs show Unhealthy events. When ingress is on and you define no probes, Container Apps adds its own: HTTP GET / on the ingress target port, startup with a failure threshold of 48, liveness and readiness with a threshold of 3 and a one-second timeout.
| Condition | Fix |
|---|---|
| Connection refused | The probe's port isn't the one your app listens on. Make the probe port, the app's port and the ingress targetPort the same number, and listen on 0.0.0.0, not 127.0.0.1 |
/ returns 401, a redirect, or 404 | The default HTTP probe needs a 200–399 from /. Add an unauthenticated /health endpoint and point the probes at it |
| The app takes a while to start (Java, big frameworks) | Raise initialDelaySeconds on liveness and readiness, and use a startup probe with a higher failureThreshold |
| A background worker with no HTTP listener restarts for ever | It has ingress enabled, so it gets probed. Disable ingress, or use internal ingress and TCP probes on a port it does open |
| An MCP server or an API that only accepts POST | The default GET probe fails. Give it a GET /health and probe that |
An init container blocks it
The revision stays in Provisioning. Init containers must run a finite command and exit 0; one that waits for a dependency that never answers blocks the main container for good. Add retries, don't assume services are up at init time, and note that in consumption-only and dedicated workload-profile environments only the main container can use managed identity (newer API versions let identitySettings scope an identity to Init, Main, None or All). An init container that reads a Key Vault-referenced secret may see it empty; use a direct Container Apps secret for init-time values.
The app starts but configuration is missing
Secrets and environment variables
| Condition | Fix |
|---|---|
| A variable is empty at runtime | The secretRef must match the secret's name exactly; the match is case-sensitive |
| "Unknown secret" on deployment | Define it first: az containerapp secret set --secrets name=value |
| You changed a secret and nothing happened | Secrets are app-scoped but variables are revision-scoped, and updating a secret doesn't make a revision. Restart the revision or deploy a new one. Unversioned Key Vault references update within about 30 minutes and restart active revisions on their own |
Managed identity not configured on a Key Vault reference | az containerapp identity assign --system-assigned, then give it Key Vault Secrets User on the vault |
Authorization failed on Key Vault | The role is missing, or the vault's firewall blocks the environment's outbound IPs. Add the role (allow up to 10 minutes to apply) or a private endpoint |
| The reference never resolves | Check the URI shape: https://<vault>.vault.azure.net/secrets/<name> with an optional /<version>; and that the secret is enabled |
Managed identity 401s and 403s
| Condition | Fix |
|---|---|
ManagedIdentityCredential errors | No identity is assigned to the app |
| 403 with a user-assigned identity | Your code uses the system identity by default. Pass the client ID: DefaultAzureCredential(managed_identity_client_id="<client id>") |
| 403 with the identity assigned | It has no role on the target. Assign the data-plane role the service needs (for example Storage Blob Data Contributor on a storage account) |
| 403 persists after you fixed the role or a group membership | The identity's authorisation is cached per resource for around 24 hours, and you can't force a refresh. Wait, and don't rely on group changes for time-sensitive access |
Use the Azure Identity SDK's DefaultAzureCredential rather than calling the identity endpoint yourself; it behaves the same locally and in Azure.
The app runs but requests fail
| Condition | Fix |
|---|---|
| No URL to reach the app | Ingress is off: az containerapp ingress enable --type external --target-port 8080 --transport auto |
| 502 Bad Gateway | The ingress targetPort isn't the port the container listens on. Check with az containerapp ingress show --query targetPort, and keep the PORT variable in step |
| The old version still answers | The traffic split points at an old revision: az containerapp ingress traffic set --revision-weight latest=100 |
| 403 Forbidden | IP security restrictions on the ingress |
| Connection errors | The transport doesn't match the protocol: HTTP, TCP, or http2 for gRPC |
| Slow first request after a quiet spell | Scale to zero; set --min-replicas 1, or lengthen probe timeouts to cover the cold start |
For a failing request, the ResponseCodeDetails field in ContainerAppHTTPLogs says whether the error came from your container (via_upstream) or from the platform (route_not_found, upstream_per_try_timeout), which settles most arguments about whose fault it is.
Sources
- Troubleshoot common deployment failures in Azure Container Apps, Microsoft Learn
- Troubleshoot a container app: health probes, Microsoft Learn
- Troubleshoot start failures in Azure Container Apps, Microsoft Learn
- Troubleshoot MCP servers on Azure Container Apps, Microsoft Learn (probes on POST-only endpoints, port mismatch, cold starts)
- Comparing Container Apps with other Azure container options, Microsoft Learn
Did this fix it?
Comments
No comments yet. Share what worked for you.
Sign in to comment. Comments show under a display name you choose, never your email.