Automation
Azure Container Apps Jobs: diagnose a KEDA scale rule before rerunning workers
A production runbook for qualifying an Azure Container Apps Job that no longer consumes correctly with KEDA scale rule, backlog, managed identity, secrets, logs, idempotency, validation and rollback before rerunning workers.
An Azure Container Apps Job that stops consuming a queue can look like a simple worker problem. The tempting response is to rerun the job, increase parallelism or purge blocked messages. That is risky when the real issue is the KEDA rule, an identity that can no longer read the queue, an expired secret, a saturated workload profile, a broken image or a non-idempotent handler.
The use case is an invoice-worker-prod job triggered by queue backlog. Since a deployment, the backlog keeps growing, a few executions fail, and the team wants to rerun workers to catch up. Before replaying anything, it needs to decide whether the safe action is a KEDA rule fix, secret restoration, image rollback, lower parallelism, bounded replay or a deliberate pause in consumption.
Treat the job as a consumption contract
Start by writing what the job is expected to consume and what it must not break. A KEDA rule is not only a threshold. It links an event source, an identity, metadata, execution limits and application code that must tolerate retries.
job:
name: invoice-worker-prod
environment: aca-env-prod
resource_group: rg-app-prod
trigger: event_driven
workload_profile: consumption-prod
source:
type: storage_queue
account: stprodops
queue: invoices-to-process
expected_backlog_behavior: decreases_during_business_hours
poison_queue: invoices-to-process-poison
scale_rule:
name: invoices-queue
min_executions: 0
max_executions: 20
polling_interval_seconds: 30
message_threshold: 25
runtime_contract:
idempotency_key: invoice_id
retry_safe_until: before_external_payment_capture
managed_identity: aca-invoice-worker-prod
secrets:
- queue_connection_or_identity_binding
- downstream_api_token_reference
blocked_until_proven:
- backlog_shape_known
- keda_rule_current
- identity_can_read_queue
- latest_image_healthy
- replay_scope_bounded
- rollback_path_ready This contract prevents the queue from becoming a plain counter. If processing is no longer idempotent after an external step, a broad replay can create a larger incident than the original backlog.
Read backlog shape before rerun
The first diagnostic is backlog shape. Continuous growth may mean KEDA no longer triggers. A few old messages may point to poison messages. Retry waves may come from a downstream outage. An empty queue with stale delay alerts may only be an observability lag.
Backlog to qualify
Visible message count
Age of the oldest message
Number of messages dequeued several times
Messages moved to the poison queue
Time of the last successful consumption
Correlation with the last deployment
Possible interpretation
Backlog grows with no executions: check KEDA, identity, trigger metadata
Many failed executions: check image, code, secrets, downstream system
A few repeated messages: isolate poison messages before global replay
Intermittent backlog: check workload profile, quotas and cold start The rerun decision depends on that reading. Do not replay an entire queue when the real symptom is concentrated on three invalid messages or on a downstream API that is still unavailable.
Compare declared and effective KEDA configuration
The useful configuration is what Container Apps sees now. Check the job, trigger type, scale rules, referenced secrets and execution limits before changing code or increasing maxExecutions.
RG="rg-app-prod"
JOB="invoice-worker-prod"
az containerapp job show --resource-group "$RG" --name "$JOB" --query "{trigger:properties.configuration.triggerType, replicaTimeout:properties.configuration.replicaTimeout, replicaRetryLimit:properties.configuration.replicaRetryLimit, parallelism:properties.configuration.parallelism, replicaCompletionCount:properties.configuration.replicaCompletionCount, scaleRules:properties.configuration.eventTriggerConfig.scale.rules, registries:properties.configuration.registries}" --output json
az containerapp job execution list --resource-group "$RG" --name "$JOB" --output table Look for simple drift: queue name changed, threshold too high, renamed secret, removed identity, execution limit too low, timeout too short or a workload profile different from the expected one.
Separate trigger failure from runtime failure
A job may not start because KEDA cannot read the source. It may also start correctly and then fail inside the container. Those two states require different fixes.
Trigger problem
No execution while backlog grows
Authentication errors on the event source
Missing scale rule or inconsistent metadata
Secret or identity binding not found
Runtime problem
Executions created and then Failed
Application logs show business or downstream exception
Image pull or startup probe failure
Timeout before processing completes
Retries process the same message again
Decision
Fix KEDA or identity when the job never starts
Fix image, code, secret or downstream when the job starts then fails
Do not increase scale-out until the problem class is clear This separation protects production from an attractive but unsafe move: increasing concurrency while every worker fails for the same reason.
Read logs within a bounded window
Logs should answer three questions: did the scaler trigger, did the container start, and was the message processed or rejected? Adapt the tables to the Log Analytics setup actually enabled in the environment.
let StartTime = datetime(2026-07-30T06:00:00Z);
let EndTime = datetime(2026-07-30T09:00:00Z);
let JobName = "invoice-worker-prod";
ContainerAppSystemLogs_CL
| where TimeGenerated between (StartTime .. EndTime)
| where ContainerAppName_s == JobName
| project TimeGenerated, Reason_s, Log_s, ReplicaName_s
| union (
ContainerAppConsoleLogs_CL
| where TimeGenerated between (StartTime .. EndTime)
| where ContainerAppName_s == JobName
| project TimeGenerated, Reason_s="console", Log_s, ReplicaName_s
)
| order by TimeGenerated desc If logs show nothing, that does not prove the job is healthy. Check Diagnostic Settings, target workspace, ingestion delay and the real job or execution name first.
Validate identity, secrets and source access
A KEDA rule can depend on a secret or managed identity. An RBAC change, secret rotation or Key Vault change can prevent backlog reads even when application code has not changed.
Identity and access
Expected managed identity attached to the job
Minimal role on the queue or namespace
No recent deny assignment or policy change
Secret reference still resolves
Key Vault reachable from the Container Apps environment when used
Expiration or recent rotation documented
Useful evidence
401 or 403 errors in system or application logs
Last success before rotation
Comparison with a healthy job on the same source
Exact RBAC scope instead of broad subscription role Do not replace a managed identity with a durable connection string just to move fast. If a temporary secret is required for recovery, it needs an expiry, an owner and a removal plan.
Choose replay, correction or pause
Once evidence is collected, make the decision explicit. A manual replay is acceptable only when processing is idempotent, scope is bounded and the cause is fixed or cleanly bypassed.
Fix the KEDA rule
Backlog present, no execution, scaler metadata or secret drift
Roll back image or configuration
Recent executions fail after an application or IaC deployment
Reduce parallelism
Downstream degraded, too many workers, retries amplify the incident
Isolate poison messages
Small set of repeated messages, reproducible functional errors
Rerun manually
Cause fixed, idempotency proven, window and volume bounded, validation ready
Deliberate pause
Processing is not idempotent or downstream is still unstable The correct action can be to stop consuming for a few minutes. A controlled pause with monitored backlog is better than a replay that writes twice to a downstream system.
Validate and prepare rollback
Closure must prove that consumption resumed without hiding the cause. Keep an application-level validation, not only a decreasing backlog.
Final validation
KEDA rule observes the right source
Recent executions finish as Succeeded
Backlog decreases at the expected rate
No new poison message appears
Logs show the idempotency key or business correlation
Downstream confirms expected processing
Rollback
Return to the previous job image or revision
Restore the previous scale rule from IaC
Restore initial threshold and parallelism
Revoke any temporary secret
Put isolated messages back only after the fix
Document messages that were not replayed If an emergency change was applied manually, it remains provisional until it is either captured in IaC or removed explicitly.
Conclusion
A blocked Azure Container Apps Job is not just a worker to rerun. It is a consumption chain: event source, KEDA rule, identity, secrets, image, execution limits, logs, idempotency and downstream system.
The useful decision is bounded: fix the scaler if the job does not start, roll back the image if executions fail after deployment, reduce concurrency if the downstream system is suffering, isolate invalid messages, or rerun only after idempotency is proven. The runbook turns an urgent backlog into controlled recovery.