Job in Kubernetes

Job in Kubernetes

How to run a task once, run it many times in parallel, or run it every night, and be sure it really finishes.

Most of the programs we talk about in Kubernetes are meant to run forever. A website, an API, and a chat service should all stay up day and night. But a huge amount of useful work looks completely different. Resize a million photos. Send this month’s invoices. Copy last night’s data into a backup. Train a machine-learning model. Each of these tasks has a clear start and a clear end, and when the work is done, the program should simply stop and go away.

Kubernetes has a special tool for exactly this kind of work, called a Job. A Job starts the work, watches it, retries it when something breaks, and records whether it finally succeeded. In this guide you will learn what a Job is, how it behaves from the first second to the last, how to run many copies in parallel, how to run Jobs on a schedule, and how to avoid the mistakes that catch most teams.

1What Is a Job?

Let us start with the plain meaning of a Job and the words we need to describe it.

The short answer

A Job is a Kubernetes object that runs one or more pods until a chosen number of them finish successfully. A pod is the smallest unit Kubernetes runs; it wraps one or more containers, which are lightly packaged boxes holding a program and everything it needs. Kubernetes runs pods on nodes, the machines that make up the cluster. A Job sits above the pods and takes responsibility for them: if a pod fails, the Job starts another. When enough pods have succeeded, the Job marks itself complete and stops creating new ones.

Analogy

A website is like a permanent employee who comes to work every day and never goes home. A Job is like a courier hired to deliver one parcel. The courier goes out, tries to deliver, and if the door is locked, tries again. Once the parcel is delivered, the courier signs the receipt and the job is finished. Nobody expects the courier to stay around afterwards.

6
default number of retries before a Job gives up
6 min
longest wait between retries
3
finished CronJob runs kept by default

What Jobs are good for

Data

Migrations and imports

Change a database structure, load a file into a table, or move data between systems, once, with a clear result.

Reports

Batch processing

Crunch through a large pile of records, such as invoices, logs, or transactions, and then stop.

Safety

Backups

Copy data to safe storage on a regular basis and confirm that the copy succeeded.

AI

Training and preparation

Prepare data sets and train models, often across many pods at the same time.

Media

Conversion work

Resize images, transcode video, or generate thumbnails, one item or one batch at a time.

Care

Cleanup and maintenance

Remove old files, rebuild search indexes, or refresh caches when needed.

i
Good to know

The word “job” is used loosely in everyday speech. In this guide, a capital-J Job always means the Kubernetes object, not a step in some pipeline tool or a scheduled task on an old server.

2Job vs Deployment vs Bare Pod

Choosing the right tool starts with one question: is this work supposed to end?

WorkloadMeant forDoes it end?Typical example
JobA task that must finish successfullyYes, when enough pods succeedDatabase migration
CronJobA Job that repeats on a scheduleEach run ends; the schedule continuesNightly backup
DeploymentA service that must stay upNo, pods are replaced foreverWeb API
StatefulSetA service that needs stable names and storageNoDatabase cluster
DaemonSetOne helper on every nodeNoLog collector
Bare podQuick experimentsYes, but nobody retries itTrying out an image

Why not use a Deployment?

A Deployment believes that a finished program is a broken program. It keeps its pods running with a restart rule called Always, so when your task finishes and exits cleanly, the Deployment happily starts it again, and again, without end. That is perfect for a web server and wrong for a nightly report.

Why not use a bare pod?

A lone pod does run your task, but nobody is responsible for it. If its node crashes halfway through, the pod is simply lost. No one retries it, and no one records whether the work was completed. A Job adds exactly the missing pieces: retries, counting, and a final verdict.

!
Common trap

Some teams run a Deployment with a program that sleeps forever and waits for work. That is not a Job. It costs money all day, it needs its own scaling logic, and it can never report “done”. If the work has an end, model it as a Job.

3Anatomy of a Job

A Job is described by a small document with a handful of settings. Learn these settings and you can read almost any Job.

Like other Kubernetes objects, a Job is written in a simple structured text format called YAML and submitted to the cluster. The description has two main parts. The first is the pod template, a recipe for the pods the Job will create: which container image to run, what command to give it, how much CPU and memory it needs, and where to find its settings. The second part is a set of rules that control how many pods run, how many must succeed, and what to do when things go wrong.

Rule

completions

How many pods must finish successfully for the whole Job to count as done. If you say nothing, the answer is one.

Rule

parallelism

How many pods may run at the same moment. The default is one, so pods run one after another.

Rule

backoffLimit

How many failures are tolerated before the Job is declared failed. The default is six.

Rule

activeDeadlineSeconds

A time limit for the entire Job. When it runs out, everything is stopped and the Job fails, no matter how many retries remain.

Rule

ttlSecondsAfterFinished

How long to keep a finished Job before Kubernetes deletes it automatically. If unset, it stays until someone removes it.

Rule

restartPolicy

Set inside the pod template. For a Job it must be OnFailure or Never. Always is not allowed.

flowchart TB
    CJ["CronJob (optional)"] -->|"creates on schedule"| JOB["Job"]
    USER["Person or pipeline"] -->|"creates directly"| JOB
    JOB -->|"creates and tracks"| P1["Pod 1"]
    JOB --> P2["Pod 2"]
    JOB --> P3["Pod 3"]
    P1 --> C1["Container runs the task"]
    P2 --> C2["Container runs the task"]
    P3 --> C3["Container runs the task"]
    JOB -->|"records"| RES["Result: succeeded and failed counts"]
    
Figure 1: A Job owns its pods, and a CronJob can own the Job

The diagram shows the chain of ownership. A person, a pipeline, or a CronJob creates the Job. The Job creates and tracks pods. Each pod runs the container that does the real work. The Job keeps a running count of how many pods succeeded and how many failed, and this count decides when it is finished. Because the Job owns its pods, deleting the Job also deletes them.

Two details worth remembering

First, Kubernetes creates the Job’s labels and selector automatically, so that the Job can tell its own pods apart from everyone else’s. You should almost never edit them by hand. Second, the pod template of a Job cannot be changed after the Job is created. If you need a different image or command, create a new Job. This rule protects you from a Job whose pods are half old and half new.

4The Job Lifecycle, Step by Step

Let us follow one Job from the moment it is created until it disappears.

sequenceDiagram
    participant U as User
    participant A as kube-apiserver
    participant J as Job controller
    participant S as kube-scheduler
    participant K as kubelet on a node
    participant T as TTL controller
    U->>A: Create Job
    A->>J: New Job noticed
    J->>A: Create Pod
    S->>A: Bind Pod to a node
    K->>A: Notice Pod on my node
    K->>K: Run the container
    K->>A: Report Pod Failed
    J->>J: Count the failure and wait for the backoff delay
    J->>A: Create replacement Pod
    K->>A: Report Pod Succeeded
    J->>A: Mark Job Complete
    T->>A: Delete Job after the TTL expires
    
Figure 2: A Job that fails once, retries, and then succeeds
1

The Job is created

You submit it to the API server, the front door of the cluster. The Job is saved in the cluster’s database, etcd.

2

The Job controller notices

A controller is a small program that compares what you asked for with what exists. The Job controller sees a Job with no pods and creates the first ones, up to the allowed parallelism.

3

Pods get homes and start

The scheduler picks a node for each pod. The node’s kubelet, its local manager, starts the containers.

4

The work runs

The container does its task and exits. An exit code of zero means success. Any other number means failure.

5

The controller counts

Each success moves the Job closer to the goal. Each failure is counted against the retry limit, and a replacement pod is created after a short, growing delay.

6

The Job finishes

Once enough pods have succeeded, the Job becomes Complete. If the retry limit or time limit is reached first, it becomes Failed.

7

Cleanup

Finished pods are kept so that you can read their logs. When the time-to-live expires, the Job and its pods are deleted.

i
Note

Finished pods do not use CPU or memory, but they still exist as records in the cluster. That is why cleanup matters when you run thousands of Jobs.

5Job States and Conditions

A Job reports how it is doing through a few counters and a short list of conditions.

stateDiagram-v2
    [*] --> Active: Job created
    Active --> Suspended: suspend set to true
    Suspended --> Active: suspend set to false
    Active --> Complete: Enough pods succeeded
    Active --> Failed: Retry limit or deadline reached
    Complete --> [*]
    Failed --> [*]
    
Figure 3: The main states in the life of a Job

An active Job is still working: pods are running or being created. A suspended Job is paused; no pods run until you resume it. A complete Job reached its goal. A failed Job gave up. Complete and Failed are final: a finished Job never wakes up again.

Field or conditionWhat it tells you
activeHow many pods are running or waiting to run right now
succeededHow many pods have finished successfully so far
failedHow many pods have failed so far
Complete conditionThe Job reached its target number of successes
Failed conditionThe Job gave up. The reason is BackoffLimitExceeded when it used up its retries, or DeadlineExceeded when it ran out of time
Suspended conditionThe Job is paused and not running any pods
startTime and completionTimeWhen the Job began and when it finished, so you can measure how long it took
Analogy

Think of the suspend setting as a pause button on a music player. The song, meaning the Job, is still in the queue and nothing is lost, but no sound plays. Press play, and the Job starts creating pods. Press pause while pods are running, and those pods are stopped politely.

i
Useful trick

You can create a Job already suspended. Queueing tools use this to hold a Job back until enough cluster capacity is free, and then release it.

6Retries, Backoff, and Failure Handling

Real tasks fail. A network hiccup, a busy database, or a node crash should not ruin a night of work. Retrying is the heart of a Job.

Two ways a failure can be retried

The restart policy in the pod template decides how a failure is handled.

SettingWhat happens on failureGood for
OnFailureThe failed container is restarted inside the same pod, on the same nodeTasks that can safely continue in the same place, and where you want fewer pods created
NeverThe pod stays as a failed record and the Job creates a brand-new podTasks where you want the failed pod kept for inspection, or need fresh nodes and clean state

The backoff limit and the growing delay

The backoff limit is the number of failures the Job will tolerate, six by default. Once that many failures have been counted, the Job is marked Failed. Between retries, the Job controller waits a little longer each time: about 10 seconds, then 20, then 40, and so on, up to a ceiling of six minutes. This is called exponential backoff. It gives a struggling system, such as an overloaded database, time to recover instead of hammering it with instant retries.

Analogy

Imagine a delivery driver who finds nobody home. The first retry comes after a short wait, the next after a longer one, and so on. After six missed attempts, the company stops trying and sends you a notice instead.

A time limit for the whole Job

A Job that retries forever can hide a real problem. Set activeDeadlineSeconds to cap the total time. When the deadline arrives, every running pod is stopped and the Job fails with the reason DeadlineExceeded. The deadline wins over the backoff limit, so even if retries remain, the Job stops.

Smarter rules for failures

Not all failures deserve the same reaction. If a node is drained for maintenance and your pod is evicted, that is not your program’s fault, yet by default it still counts against the retry limit. A pod failure policy lets you write rules based on the container’s exit code or on why the pod was removed. For example, you can say: “if the exit code is 42, which my program uses for a bad input file, fail the whole Job immediately because retrying is pointless” and “if the pod was evicted by the cluster, ignore that failure and do not count it”. Pod failure policies work with the restart policy Never.

!
Design rule

Retries mean your task may run more than once for the same piece of work. Design it to be safe when repeated. This idea, called idempotency, is covered later in this guide.

7Parallel Jobs and Completion Patterns

One pod at a time is fine for small tasks. Large tasks need many workers. Jobs support several patterns, chosen by two numbers.

The two numbers are completions, the total successes needed, and parallelism, the number of pods allowed to run together. Depending on how you set them, a Job behaves in one of four ways.

PatternSettingsHow it worksExample
Single runcompletions 1, parallelism 1One pod does the whole task; a replacement is created only on failureDatabase migration
Fixed count, parallelcompletions N, parallelism PThe Job runs P pods at a time until N have succeededProcess 50 separate files
Work queueparallelism P, completions unsetPods pull tasks from a shared queue; when one succeeds, the rest wind downWorkers reading a message queue
Indexedcompletions N, indexed modeEach pod receives its own number from 0 to N minus 1 and handles that sliceSplit a big data set into 100 slices
Analogy

Picture a supermarket with ten checkout counters. A fixed-count Job is like ten cashiers working through a known list of 50 trolleys. A work-queue Job is like cashiers who serve whoever is next in the line, until the line is empty. An indexed Job is like exam papers numbered 0 to 99, where each teacher is handed one number and marks only that paper.

How indexed Jobs work

In indexed completion mode, Kubernetes gives every pod a unique number, available to the program as an environment variable called JOB_COMPLETION_INDEX. Your program reads its number and decides which slice of the data to handle. This avoids needing a queue, because the split is decided in advance. If pod 17 fails, only slice 17 is retried. Newer Kubernetes versions can also count retries separately for each index, so one stubborn slice does not use up the retry budget of the whole Job.

Use case: resizing a million photos

A photo service stores images in cloud storage. An indexed Job with 100 completions and a parallelism of 20 starts 20 pods at a time. Pod number 7 reads the seventh slice of ten thousand file names, resizes those photos, and exits. If a pod crashes, only its slice is redone. Twenty workers shrink a job that would take hours on one machine down to a fraction of the time.

i
Tip

Parallelism is a ceiling, not a promise. If the cluster is short of CPU or memory, some pods wait in Pending. The Job simply takes longer; it does not fail.

8Running Jobs at Scale

A single Job is simple. Hundreds of Jobs and thousands of pods need a little more planning.

Tell the cluster what you need

Every pod in a Job should declare its resource requests, the minimum CPU and memory it needs, and limits, the maximum it may use. The scheduler uses requests to decide where a pod fits. Without them, it may pile too many pods onto one node, and the node may run out of memory and start killing pods. Batch work is often hungry, so honest numbers are especially important.

Hold Jobs back until there is room

If you submit 500 heavy Jobs to a small cluster, most pods will sit in Pending while a few run. That is wasteful and noisy. The better approach is a queue. Kubernetes lets you create Jobs in the suspended state, and projects such as Kueue, which is part of the Kubernetes community, watch the cluster’s free capacity and unsuspend Jobs in a fair order. Another project, Volcano, adds batch-oriented scheduling features such as scheduling a whole group of pods at once, which matters when a training job needs all its workers to start together or not at all.

Capacity

Autoscale the nodes

A node autoscaler adds machines when pods cannot be scheduled, then removes them when the work is done. You pay only while Jobs run.

Cost

Use cheaper spot machines

Batch work that can be retried is a good match for spot or preemptible machines, which cost less but may be taken away at short notice.

Priority

Give Jobs a priority class

Make sure a low-priority nightly report never crowds out the pods that serve customers.

Tidiness

Clean up automatically

Set a time-to-live so finished Jobs do not pile up and slow down the cluster’s database.

!
Watch the control plane

Each pod is a record in etcd, and the control plane must track it. Tens of thousands of finished pods can slow the whole cluster. Prefer one Job with many completions over thousands of separate tiny Jobs, and always clean up.

Analogy

A queueing system is like a doorman at a busy restaurant. Instead of letting everyone squeeze in and blocking the aisles, the doorman lets in parties as tables free up. Everyone is served, and the kitchen is never overwhelmed.

9CronJob: Running Jobs on a Schedule

Many tasks repeat: every night, every hour, every first day of the month. That is the job of a CronJob.

A CronJob is a Kubernetes object that creates a new Job whenever its schedule says so. It contains the same Job recipe you already know, plus a schedule. The name comes from cron, the classic scheduler on Unix computers.

Reading a schedule

A schedule is written as five fields separated by spaces: minute, hour, day of the month, month, and day of the week. A star means “every”.

ScheduleMeaning
*/5 * * * *Every five minutes
0 * * * *At the start of every hour
30 2 * * *Every day at 2:30 in the morning
0 9 * * 1Every Monday at 9:00
0 0 1 * *At midnight on the first day of each month

By default, the schedule follows the time zone of the cluster’s control plane, which is often UTC. Newer versions let you set a timeZone field such as a named region, so “9:00 in the morning” means the same thing for your team everywhere and daylight saving changes are handled for you. The shortest interval is one minute.

What if the last run is still going?

Suppose a report takes 70 minutes but is scheduled every hour. When the next hour arrives, the previous Job is still running. The concurrency policy decides what happens.

flowchart TB
    T["Scheduled time arrives"] --> Q{"Is the previous Job still running?"}
    Q -->|"No"| N["Create a new Job"]
    Q -->|"Yes, policy Allow"| N
    Q -->|"Yes, policy Forbid"| SK["Skip this run"]
    Q -->|"Yes, policy Replace"| R["Stop the old Job then create a new one"]
    R --> N
    N --> H["Old finished Jobs trimmed by history limits"]
    
Figure 4: How a CronJob reacts at each scheduled time
Default

Allow

Start the new Job even if the old one is running. Overlapping runs are possible.

Safe

Forbid

Skip the new run if the old one is not finished. Good when two runs would clash.

Fresh

Replace

Cancel the old run and start a new one. Good when only the newest result matters.

Late start

startingDeadlineSeconds

How late a run may start after its scheduled time. If it cannot start in time, that run is skipped.

History

History limits

By default a CronJob keeps the last three successful and the last one failed Job, so you can read logs but not fill the cluster.

Pause

suspend

Stops future runs without deleting the CronJob. Runs already started carry on.

!
Important

A CronJob promises “at least once” behaviour, not “exactly once”. In rare situations a run may be created twice, or missed. Write your task so that running it twice does no harm. Also note that a CronJob’s name may not be longer than 52 characters, because Kubernetes adds a suffix when it creates each Job.

Analogy

A CronJob is like a repeating reminder on your phone. Each time the alarm rings, a new to-do item (a Job) is created and handled. The reminder itself keeps ringing on schedule, and the old to-do items are tidied away after a while.

10How Jobs Work Inside the Cluster

Behind every Job there is a small team of controllers quietly doing their rounds.

All the smarts of Jobs live in the kube-controller-manager, a program in the control plane that hosts many controllers. Each controller runs a simple, endless loop known as reconciliation: look at the desired state, look at the actual state, and act to close the gap.

ComponentIts role in a Job
API serverReceives the Job, validates it, and stores it
Job controllerCreates pods, counts successes and failures, applies retry and deadline rules, and sets the final condition
CronJob controllerCreates Jobs at the scheduled times and trims old ones
SchedulerChooses a node for each pod
KubeletRuns the containers on the node and reports how they ended
TTL controllerDeletes finished Jobs after their time-to-live
Garbage collectorRemoves pods whose owner Job has been deleted

Keeping an honest count

How does the Job controller know exactly how many pods succeeded, even if some pods are deleted or the controller itself restarts? It attaches a small marker called a finalizer to each of its pods. A finalizer tells the cluster “do not fully delete this pod until I have recorded its result”. Only after the Job controller has added the pod to its count does it remove the marker. This prevents a success from being counted twice, or a failure from being forgotten.

Analogy

Imagine a teacher collecting homework. Before a student may throw away their draft, the teacher must first tick their name on the class list. The tick guarantees that no homework is lost or counted twice, even if the teacher takes a break in the middle.

i
Note

Because the control plane, not your pod, keeps the score, a Job survives a lot of turbulence: nodes can die, the controller can restart, and the counts stay correct.

11Data, Storage, and Idempotency

A Job’s pods are temporary. The results of the work must go somewhere that lasts.

Where do the results go?

When a pod ends, everything written inside its own filesystem is lost when the pod is removed. So a Job should send its output to a place outside the pod.

Best for big data

Object storage

Services like Amazon S3 or Google Cloud Storage suit files, exports, and reports. Any pod on any node can reach them.

Structured data

Databases

Write records to a database or publish messages to a queue for other systems to pick up.

Shared disks

Persistent volumes

A volume that outlives pods. Parallel pods can share one only if the storage type supports several writers.

Scratch space

Temporary volumes

Good for working files while the pod runs, or for sharing files between an init container and the main container. They vanish with the pod.

Settings and credentials should come in through ConfigMaps and Secrets rather than being baked into the image. And do not forget logs: they disappear when finished pods are deleted, so a log collector should copy them elsewhere.

Idempotency: safe to repeat

A task is idempotent if doing it twice has the same effect as doing it once. Retries, node failures, and the “at least once” nature of CronJobs mean your task will sometimes run again over work that is already partly or fully done.

Analogy

Pressing the “on” button of a light switch twice leaves the light on. That is idempotent. Pressing a “toggle” switch twice turns the light off again. That is not. Design your Jobs like the on button.

Safe to repeat

  • Write a result file under a name based on the input, replacing any old copy.
  • Insert or update a record using a unique key, so duplicates are impossible.
  • Record progress and skip items already done.
  • Use a temporary name while writing and rename only when finished.

Dangerous to repeat

  • Adding a fresh row every run with no unique key.
  • Sending an email or payment each time without checking if it was sent.
  • Appending to a shared file with no checks.
  • Incrementing a counter as the only record of progress.

12Advantages, Disadvantages, and Trade-offs

Jobs are excellent at what they do, and they are not meant to do everything.

Advantages

  • Built-in retries: Failures are handled automatically, with sensible delays.
  • Clear verdict: Complete or Failed, with counts and timestamps.
  • Parallel by design: Scale from one worker to hundreds with two numbers.
  • Pay for what you use: Pods exist only while the work runs.
  • Same tools as the rest: Images, secrets, quotas, and monitoring all apply.

Disadvantages

  • Simple by nature: A Job does not understand steps that depend on each other.
  • Immutable template: Changing the recipe means creating a new Job.
  • Cleanup is your job: Old Jobs and pods pile up if you forget the time-to-live.
  • Start-up cost: Each pod needs scheduling and an image pull, so very short tasks carry overhead.
  • At-least-once running: Tasks must be safe to repeat.

Choosing between related tools

You needBest choiceWhy
One task with retriesJobSimple and built in
The same task on a timetableCronJobSchedule plus Job in one object
Steps with dependencies, branching, or approvalsA workflow engine such as Argo Workflows or Apache AirflowThey model graphs of tasks and run each step as a pod
A worker that handles messages foreverDeploymentIt should never end, and a queue system feeds it
Thousands of tiny tasks per secondA queue with long-lived workersCreating a pod per task is too slow and costly
i
Rule of thumb

If a task takes less than a few seconds, the time to start a pod may exceed the time to do the work. Group many small items into one Job run, or use long-lived workers.

13Security for Jobs

Jobs often touch valuable data and run with real credentials, so they deserve the same care as any service.

Permissions

Control who can create Jobs

Anyone who may create a Job can run any container in that namespace. Grant this power carefully using role-based access control.

Identity

A narrow service account

Give each Job a service account with only the permissions it needs, such as read access to one storage bucket, and nothing more.

Secrets

Keep credentials out of images

Pass passwords and keys through Secrets or a secrets manager, never in the command line or image.

Containers

Least privilege

Run as a non-root user, drop unneeded Linux powers, and block privilege escalation.

Supply chain

Trusted images

Use images from your own registry, pinned to a version or a digest, and scan them for known flaws.

Blast radius

Quotas and network rules

Use resource quotas to cap how many pods a namespace can create, and network policies to limit where Jobs can connect.

!
Runaway danger

A CronJob with a bug can create Jobs faster than they finish and quietly fill a cluster. A namespace resource quota, a Forbid concurrency policy, and a deadline on each Job are the three safety belts against this.

Short life is a security advantage. Because Job pods exist only for the time needed, credentials they hold live in the cluster only briefly. Combine that with a time-to-live, and few traces are left behind once the work is done.

14Monitoring, Logging, and Metrics

A Job that fails at 3 a.m. is only a problem if nobody notices. Good monitoring turns silent failures into clear alerts.

What to watchWhere it comes fromWhy it matters
Failed Jobskube-state-metrics: job failed and job status failedA Job that exhausted its retries needs a human
Successful podskube-state-metrics: job status succeededShows progress of a long parallel Job
Last schedule time of a CronJobkube-state-metrics: cronjob last schedule timeDetects a schedule that silently stopped running
Job durationStart and completion timesA run that suddenly takes twice as long often signals trouble
Pod restarts and exit codesPod statusReveals crashes, memory kills, and bad inputs
Application logsLog collector on each nodeExplain why a run failed, even after its pods are gone

Alerts that help

  • Job failed: page someone, or open a ticket, as soon as a Job enters the Failed state.
  • Missed schedule: alert when a CronJob has not started a run for longer than expected.
  • Too slow: alert when a Job runs far beyond its usual duration, before the deadline kills it.
  • Too many leftovers: alert when finished Jobs or pods grow past a sensible number.

Finally, remember that logs from finished pods vanish with the pods. Ship logs to a central store as they are produced. It is also useful to have the task print a short summary at the end, such as items processed, items skipped, and time taken, so that a quick look at the logs tells the story.

i
Tip

For very important CronJobs, add a “dead man’s switch”: the task reports success to a monitoring service, and that service raises an alarm if no report arrives on time. This catches the case where the CronJob itself never ran.

15Troubleshooting Jobs

When a Job misbehaves, the symptom usually points straight at the cause.

SymptomLikely causeWhere to look
Job exists but no pods appearThe Job is suspended, a namespace quota is full, or the template is invalidJob conditions and events
Pods stay PendingNot enough CPU or memory, untolerated taints, or waiting for a new nodePod events
ImagePullBackOffWrong image name or tag, or missing registry credentialsPod events
Pods fail again and againThe application has an error, a bad setting, or cannot reach a dependencyLogs of the failed pods, exit codes
Failed with BackoffLimitExceededThe retry limit was used upLogs of each failed pod; look for a repeating cause
Failed with DeadlineExceededThe work took longer than the time limitJob duration history; raise the limit or speed up the task
Job Complete but results missingThe program exited with code 0 despite an error, or wrote to a temporary disk that was lostApplication logs and output location
CronJob never runsIt is suspended, the schedule is wrong, a time zone is misunderstood, or the start deadline was missedCronJob status and last schedule time
Hundreds of old pods remainNo time-to-live and no history limitsJob spec and CronJob spec
Work seems done twiceA retry or a duplicate CronJob run repeated itTask design; add idempotency

A calm routine

1

Describe the Job

Read its conditions, counters, and events. They often name the reason directly.

2

List its pods

Use the Job’s name label to find them and see which are Pending, Running, Failed, or Completed.

3

Read the logs and exit code

Look at a failed pod first. Its last lines and exit code usually explain the failure.

4

Change and re-create

Since the template cannot be edited, fix the recipe and create a new Job. Delete the old one once you have what you need from it.

i
Tip

Setting the restart policy to Never keeps each failed pod as a record, so you can read the logs of every attempt. With OnFailure the same pod is reused and earlier attempts are harder to inspect.

16Design Patterns and Anti-patterns

Teams that run batch work well keep repeating the same good habits. Trouble also has repeating shapes.

Patterns that work

Pattern

Idempotent tasks

Every run is safe to repeat, so retries and duplicate runs are harmless.

Pattern

Indexed slicing

Split big data into numbered slices, one per pod, so only the failed slice is retried.

Pattern

Init container for setup

Download inputs or wait for a dependency in an init container, so the main container focuses on the work.

Pattern

Queue in front, Jobs behind

Submit work to a queueing system that releases Jobs as capacity allows.

Pattern

Deadline plus limit plus TTL

Every Job gets a time limit, a retry limit, and an automatic cleanup time.

Anti-patterns to avoid

ANTI-PATTERN 01Avoid
What people do

They write tasks that are not safe to repeat, such as inserting rows without a unique key or sending emails without checking.

Why it hurts

Retries and duplicate CronJob runs then create double charges, duplicate emails, and messy data.

Do this instead

Use unique keys, record what was already done, and write results under predictable names so a second run simply overwrites the first.

ANTI-PATTERN 02Avoid
What people do

They never set a time-to-live or history limit, so finished Jobs and pods remain for months.

Why it hurts

The cluster’s database fills with thousands of dead records, listing commands slow down, and the control plane becomes sluggish.

Do this instead

Set ttlSecondsAfterFinished on Jobs and sensible history limits on CronJobs. Keep logs in a central store instead.

ANTI-PATTERN 03Avoid
What people do

They use a Job as a long-running service, or run a Deployment that sleeps and waits for work.

Why it hurts

A Job has a deadline and a retry limit and is expected to end. A sleeping Deployment wastes resources and cannot report completion.

Do this instead

Use a Job for work that ends, and a Deployment fed by a queue for work that never ends.

ANTI-PATTERN 04Avoid
What people do

They allow many overlapping CronJob runs, with no limits, for a task that takes longer than its interval.

Why it hurts

Runs pile up, compete for the same data, and can exhaust the cluster.

Do this instead

Use the Forbid policy, or lengthen the interval, and always add a deadline.

17Best Practices and Common Mistakes

A short checklist that suits nearly every Job, followed by the slips seen most often.

Best practices

1

Exit honestly

Return zero only on real success and a non-zero code on failure. The Job can only judge by the exit code.

2

Set every safety limit

Pick a retry limit, a time limit, and a time-to-live on every Job.

3

Request the right resources

Measure a real run and set CPU and memory requests and limits from it.

4

Handle shutdown signals

Pods may be stopped early by a drain or a deadline. Save progress on SIGTERM so the next attempt can resume.

5

Use exact image versions

Pin images to a fixed tag or digest, so retries and reruns behave identically.

6

Alert on results, not only on infrastructure

Watch for failed Jobs and missed CronJob runs, not just node health.

Common mistakes

Mistake

Restart policy Always

Kubernetes rejects it for Jobs. Choose OnFailure or Never.

Mistake

Editing a running Job’s template

The template cannot change. Create a new Job with the fixed recipe.

Mistake

Saving results inside the pod

Files on the pod’s own disk disappear. Save to storage that lasts.

Mistake

Guessing the time zone

A schedule may follow UTC. Set the time zone explicitly to avoid runs at surprising hours.

18Real-World and Industry Examples

Batch work on Kubernetes shows up in many well-known places.

Apache Airflow and Airbnb

Apache Airflow, the workflow tool created at Airbnb, offers a Kubernetes executor that runs each task of a workflow in its own pod. Every task gets a clean environment, its own resources, and a clear success or failure, which is the same idea as a Job applied to a chain of steps.

GitLab CI pipelines

GitLab Runner can use Kubernetes to run each pipeline job in a fresh pod. When a build finishes, the pod disappears, so thousands of builds a day use cluster capacity only while they are running.

CERN and scientific computing

CERN, the European physics laboratory, has publicly discussed running batch-style data-processing workloads on Kubernetes. Physics experiments produce huge data sets that must be split into many independent slices and processed in parallel, which is exactly the pattern that indexed Jobs describe.

Large-scale AI training

Organisations such as OpenAI have described using Kubernetes for very large machine-learning training on thousands of nodes. Training is batch work with a start and an end, and it benefits from queueing, gang-style scheduling, and retry rules, the same features covered in this guide.

Scenario: a monthly invoice run

Picture a telecom company that must produce five million invoices on the first of each month. A CronJob starts an indexed Job with 200 slices at 1 a.m. Each pod produces about 25,000 invoices, writes them to object storage under names derived from the customer number, and exits. A node fails at 2 a.m., so one slice is retried, and because the file names are predictable, the retry safely replaces the partial output. By 3 a.m. the Job is Complete, and the finished pods are removed an hour later by the time-to-live.

19Frequently Asked Questions

Q1What is the difference between a Job and a CronJob?
A Job runs a task once, with retries, until it succeeds or fails. A CronJob creates a new Job on a schedule, so the same task repeats over time.
Q2Why can I not use the restart policy Always?
Always means “restart whenever the program stops”, which would make the task run forever. A Job needs the program to be allowed to finish, so only OnFailure and Never are accepted.
Q3Does a Job delete its pods when it finishes?
No. Finished pods stay so that you can read their logs. Set a time-to-live on the Job, or delete it yourself, to clean them up.
Q4How do I run a finished Job again?
A finished Job cannot be restarted. Create a new Job from the same recipe, with a new name. A CronJob does this for you on each schedule.
Q5How many pods run at the same time?
As many as the parallelism setting allows, one by default. The number can be lower if the cluster lacks free resources or if fewer completions remain.
Q6Can I change a Job after creating it?
The pod template is fixed. A few controls, such as parallelism and the suspend switch, can be changed while the Job exists. For anything else, create a new Job.
Q7What happens if the node running my Job’s pod dies?
The pod is counted as a failure. The Job controller waits for the backoff delay and creates a replacement pod, which the scheduler places on a healthy node.
Q8Does a Job guarantee my task runs exactly once?
No. Kubernetes aims for at least once. Retries, node failures, and rare CronJob duplicates mean a task can run more than once, so design it to be safe to repeat.
Q9How can I see the logs of a finished Job?
Find the Job’s pods with its name label and read their logs, as long as the pods still exist. To keep logs after cleanup, collect them into a central logging system.
Q10How do I stop a Job that takes too long?
Set activeDeadlineSeconds. When the time is up, running pods are stopped and the Job is marked Failed with the reason DeadlineExceeded.

20Summary and Key Takeaways

Key Takeaways

  • A Job runs work that must end. It creates pods, retries failures, and stops when enough pods have succeeded.
  • Choose it over a Deployment or bare pod whenever a task has a finish line and you need a clear verdict of Complete or Failed.
  • Two numbers shape parallel work: completions say how many successes are needed, and parallelism says how many pods run together. Indexed mode gives each pod its own slice.
  • Failure handling is built in. Use the restart policy, backoff limit, deadline, and pod failure policy to decide how hard to try and when to give up.
  • A CronJob is a schedule that creates Jobs. Set the time zone, the concurrency policy, and history limits, and expect at-least-once behaviour.
  • Make tasks idempotent and store results outside the pod, because retries and reruns are normal and pod disks are temporary.
  • Set limits and clean up. Give every Job a deadline, a retry limit, a time-to-live, honest resource requests, and alerts for failures and missed runs.