LowCodeStacks™
Sign in

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

Checked against Microsoft Learn · 5 sourcesPosted by the Maker DeskHow we write guides

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.

bash
# 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 saysFix
manifest unknown or tag not foundThe 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 requiredThe 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 configuredThe identity lacks AcrPull: az role assignment create --role AcrPull --assignee <principal> --scope <acr id>
name unknown or a DNS failureThe image name's host is wrong; it must be <registry>.azurecr.io/<repo>:<tag>
A timeout reaching the registryIn 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 seeFix
A stack traceYour 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 foundThe 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 startA required environment variable isn't set; see the secrets section below
OOMKilled in the system logsRaise the limits: az containerapp update --cpu 1.0 --memory 2.0Gi
The container starts and then exits with no errorThe 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.

ConditionFix
Connection refusedThe 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 404The 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 everIt 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 POSTThe 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

ConditionFix
A variable is empty at runtimeThe secretRef must match the secret's name exactly; the match is case-sensitive
"Unknown secret" on deploymentDefine it first: az containerapp secret set --secrets name=value
You changed a secret and nothing happenedSecrets 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 referenceaz containerapp identity assign --system-assigned, then give it Key Vault Secrets User on the vault
Authorization failed on Key VaultThe 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 resolvesCheck 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

ConditionFix
ManagedIdentityCredential errorsNo identity is assigned to the app
403 with a user-assigned identityYour code uses the system identity by default. Pass the client ID: DefaultAzureCredential(managed_identity_client_id="<client id>")
403 with the identity assignedIt 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 membershipThe 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

ConditionFix
No URL to reach the appIngress is off: az containerapp ingress enable --type external --target-port 8080 --transport auto
502 Bad GatewayThe 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 answersThe traffic split points at an old revision: az containerapp ingress traffic set --revision-weight latest=100
403 ForbiddenIP security restrictions on the ingress
Connection errorsThe transport doesn't match the protocol: HTTP, TCP, or http2 for gRPC
Slow first request after a quiet spellScale 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

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.