# Fix CronJobs that pile up or never run

> One CronJob starts a new Job before the last one ends, another has never run. Read the schedules and fix both with concurrencyPolicy.

Level: Intermediate · Topic: Workloads · About 10 minutes
Try it in the browser: https://k8wiz.com/playground/fix-cronjobs-that-pile-up-or-never-run

The playground is a simulated Kubernetes cluster that runs in the browser. kubectl output and errors follow the real ones.

## The situation

`report` should build a report every minute. Each run takes about two and a half minutes, so runs overlap and Jobs pile up, each one holding a Pod.

`cleanup` should remove old exports every 2 minutes. It was created a while ago and has never run once.

Fix both without changing how often `report` runs. `sim ff 120` moves the clock forward if you don't want to wait.

## Objectives

1. See report's Jobs pile up (`kubectl get cronjobs,jobs`)
2. report runs one Job at a time (Old runs can finish, but no new one starts while one is running)
3. cleanup runs on its schedule (A run the CronJob starts itself, not one made with `kubectl create job --from`)
4. report still runs every minute

## Hints

1. `kubectl get cronjob report` shows ACTIVE above 1. `kubectl describe cronjob report` shows its `Concurrency Policy`: `Allow` lets runs overlap.
2. `concurrencyPolicy: Forbid` skips a run while the last one is still going (the event says `JobAlreadyActive`). `Replace` stops the old run and starts a new one.
3. Read cleanup's schedule field by field: minute, hour, day of month, month, day of week. `30 2` means the 30th of February, which never comes.
4. kubectl patch cronjob cleanup -p '{"spec":{"schedule":"*/2 * * * *"}}'

## Solution

```bash
kubectl get cronjobs,jobs
```
```bash
kubectl patch cronjob report -p '{"spec":{"concurrencyPolicy":"Forbid"}}'
```
```bash
kubectl patch cronjob cleanup -p '{"spec":{"schedule":"*/2 * * * *"}}'
```
```bash
sleep 150
```

## What it teaches

- `concurrencyPolicy` decides what happens when a run is due and the last one hasn't finished: `Allow` (the default) overlaps them, `Forbid` skips the new one, `Replace` stops the old one.
- The API server only checks that a schedule parses. A schedule that can never match, like the 30th of February, is accepted and simply never fires.
- Schedules use the controller's time zone (UTC) unless you set `spec.timeZone`, such as `Europe/Paris`.
- `activeDeadlineSeconds` on the Job template stops a run that hangs, so a stuck Job can't block a `Forbid` CronJob forever.
