What is a Container App Job?

A Container App Job is a feature of Azure Container Apps for running containerized work that has a beginning and an end. You give it an image, tell it when to run, and Azure handles everything else - scheduling, scaling, retries, and the underlying compute. When the container exits, the job execution completes, and you stop paying for it.

Jobs live inside a Container Apps environment - the same environment that hosts long-running Container Apps. They share networking, managed identity, Dapr components, secrets, and log destinations with the apps next to them, so an API and the nightly job that maintains its data sit in the same operational surface and follow the same deployment story.

An Azure Container Apps job runs an instance of a containerized application as a finite execution.- Microsoft Learn

The sibling resource

Container Apps has two top-level resource types - Apps and Jobs - that look identical from a distance. Same image, same environment, same configuration shape. The difference is the contract: an App is a service that stays up; a Job is a task that runs to completion. The rest of this article is about Jobs specifically, so it assumes you have at least seen the App side.

If you are still deciding between the two for a particular workload, that decision has its own piece: Container App vs Container App Job walks through the dividing line, the billing implications, and a four-question decision tree. The rest of this article assumes you have landed on Job.

Three trigger types

Every job declares exactly one trigger type at creation. The trigger determines when an execution starts; the rest of the configuration (image, resources, timeout, retries) is shared.

MANUAL

On demand

Started by you - via the CLI, the Azure portal, an Azure DevOps pipeline, GitHub Actions, an Logic App, or the management REST API. One execution per start command.

SCHEDULE

By cron

Started automatically on a cron expression. The five-field cron syntax you'd find on any UNIX system. Time zone is UTC; no surprises.

EVENT

By signal

Started by a KEDA scaler watching a queue, topic, stream, or other external source. The platform polls; when work shows up, a replica spins up to handle it.

Manual is the most flexible - you control every execution from outside the platform. Schedule is the right choice for periodic maintenance, reports, and reconciliation tasks where the cadence is fixed. Event is what lets you build queue-driven workers without a long-running consumer process; the queue itself is the scale signal.

Anatomy of a job

Behind the CLI is a typed Azure resource - the same one you'd author in Bicep, Terraform, ARM, or YAML. Here is roughly what the schedule job above looks like as a resource definition:

YAML nightly-report.yaml
properties: environmentId: /subscriptions/.../managedEnvironments/my-env configuration: triggerType: Schedule replicaTimeout: 1800 # max seconds per replica replicaRetryLimit: 3 # retries on non-zero exit scheduleTriggerConfig: cronExpression: "0 2 * * *" parallelism: 1 # replicas per scheduled run replicaCompletionCount: 1 # successes required template: containers: - name: report image: myreg.azurecr.io/report:v3 resources: cpu: 0.5 memory: "1Gi" env: - name: REPORT_DATE value: "yesterday"

Three things to notice. First, the template block is the same shape you use for a Container App - same container schema, same env vars, same secrets, same managed identity. Second, the configuration block is where Apps and Jobs diverge: jobs replace the ingress and scale rules of an app with a trigger and an execution policy. Third, resources are per-replica - if you set CPU to 0.5 and run two replicas in parallel, you pay for 1 vCPU-second for every second the pair runs.

Parallelism, retries, timeouts

Every execution of a job is governed by four numbers worth understanding before they bite:

  • replicaTimeout - the wall-clock ceiling on a single replica, in seconds. If the container is still running when the timer fires, the platform terminates it and marks the replica failed. There is no "just a little longer" here; pick a number that's well above the realistic worst case.
  • replicaRetryLimit - how many times the platform retries a failed replica before giving up. A non-zero exit code or a timeout counts as a failure. Retries restart the container fresh; they do not resume from where it stopped.
  • parallelism - how many replicas run concurrently inside one execution. For a scheduled or manual job this is fixed; for an event-driven job it's the upper bound the scaler can grow into.
  • replicaCompletionCount - how many replicas must exit successfully for the execution itself to be considered successful. Useful when you want a fan-out job where the execution succeeds as soon as N workers finish, even if a few stragglers are still running.

The combination matters. A job with parallelism: 5 and replicaCompletionCount: 5 is a strict fan-out - all five must complete. With replicaCompletionCount: 1, it's a competing-workers pattern - the first one to succeed wins, and the rest can be canceled. With parallelism: 1 and a high replicaRetryLimit, it's a stubborn one-shot task that the platform will keep trying until it works or runs out of patience.

Event-driven jobs and KEDA

The event trigger is the most distinctive of the three. Container Apps embeds KEDA - the same scaler framework Container Apps uses to scale services - and points it at a queue, a topic, or any other supported external source. The platform polls the scaler on a configurable interval; for every unit of pending work it sees, it starts a replica of your job.

YAML queue-worker.yaml
configuration: triggerType: Event replicaTimeout: 1800 replicaRetryLimit: 3 eventTriggerConfig: parallelism: 4 # up to 4 workers at once replicaCompletionCount: 1 pollingInterval: 30 # seconds between scaler checks minExecutions: 0 # scale to zero when idle maxExecutions: 10 # cap concurrent executions scale: rules: - name: sb-rule type: azure-servicebus metadata: queueName: work-items messageCount: "1" auth: - secretRef: sb-connection triggerParameter: connection

The supported scalers cover most of the places work tends to land: Azure Service Bus queues and topics, Azure Storage Queue, Azure Event Hubs, Apache Kafka, RabbitMQ, Redis lists and streams, generic HTTP scalers, and more. Each one knows how to translate its "amount of pending work" signal into a replica count the platform can act on.

Two pieces of the model are easy to miss. The platform scales the number of executions, and each execution can itself have parallelism replicas - so an event-driven job can fan out twice: once via the scaler, again via the parallelism setting inside each execution. And because the job runs to completion, you do not need an outer "while true: poll" loop in your code - the container is started, processes the work, and exits. The platform starts another one when there is more work to do.

Where it sits in Azure

Container App Jobs occupy a specific point in Azure's compute lineup, between the very serverless and the very orchestrated. Knowing what's nearby helps you tell when something else is the better fit.

  • Azure Functions are for short-lived, per-event code. They start fast, scale to many instances, and bill at the granularity of a single execution. They cap at a few minutes on the Consumption plan, and they're function-shaped - not container-shaped. Pick Functions for sub-second to low-minute work that fits a function programming model. For the deeper take, see Azure Function vs Container App Job.
  • Azure Container Instances (ACI) are single containers with no orchestration. No scheduling, no event scaling, no retry policy. ACI is the right primitive when you want a one-off container and nothing more, or when something else (an Azure DevOps agent, an AKS virtual node) is wrapping it for you.
  • Container Apps Jobs are the serverless option for containerized run-to-completion work - scheduling, retries, KEDA-based scaling, environment-level networking and identity, no cluster to operate. Pick jobs when the work is container-shaped and runs from "a few minutes" to "a few hours".
  • Azure Kubernetes Service (AKS) Jobs and CronJobs give you full Kubernetes - every knob, every plugin, every networking option, every responsibility. Pick AKS when you already operate a cluster, when you need primitives Container Apps does not expose, or when the rest of the workload is on AKS and the job belongs there too.

The pricing model reinforces the position. Container App Jobs bill per vCPU-second and GiB-second of replica runtime, and the same monthly free grant that Container Apps offers applies. A job that runs for 90 seconds, four times a day, costs almost nothing to operate compared to a worker service sitting idle 99% of the time waiting for the same work.

When to reach for jobs

Container App Jobs earn their place in stacks that have a service-shaped workload and a task-shaped one, and want to ship both with the same toolchain. Specifically:

  • A nightly report, ETL job, or backup that has to run on a fixed schedule and finish before morning.
  • A queue-driven worker that processes messages from Service Bus, Storage Queue, or Event Hubs - and should scale to zero when the queue is empty.
  • A one-off migration or data backfill kicked off from a release pipeline, parameterized by environment variables, retried automatically on failure.
  • A fan-out task that splits across N parallel replicas - generating per-tenant reports, processing chunks of a large dataset, exercising a test matrix.
  • An Aspire stack that already deploys its services to Container Apps and now needs a scheduled or queue-driven companion task in the same environment.

Reach for something else when the work is sub-minute and high-frequency (Functions fits better), when you need fine-grained control of the runtime that Kubernetes exposes and Container Apps does not (AKS is the answer), or when the task is genuinely a long-running service in disguise - if your job restarts itself in a loop, what you actually want is a Container App.

RUN

To completion

Containers start, do the work, and exit. The exit code is the result; success and failure are unambiguous.

TRIGGER

Three ways

Manual, cron schedule, or KEDA event source - pick the one that matches how the work actually arrives.

SCALE

To zero

No idle replicas. Event-driven jobs spin up when work shows up, then disappear when the queue drains.

SHARE

The environment

Same Container Apps environment, networking, identity, and secrets as the services running next to it.