---
title: "Airflow catchup: what it does and how to control it | Metacenta"
description: "Airflow catchup creates a run for every missed interval. What happens when you unpause a DAG, the default in Airflow 2 and 3, and how to backfill instead."
url: "https://metacenta.com/rules/dag-catchup-unbounded"
updated: "2026-10-14"
tokens: 1052
---

Orchestration & reliability · rule dag-catchup-unbounded

# Airflow catchup: what it does and how to control it

Airflow catchup decides whether the scheduler creates runs for past intervals. With `catchup=True` it creates a run for each interval that hasn't run. With `catchup=False` it creates a run for the latest interval only.

A Metacenta review checks this under the rule **A deploy does not trigger a backfill**. Everything below applies whether or not you ever commission one.

## How catchup works

Take a daily DAG with a `start_date` of 1 January, first switched on at the end of the month. With catchup on, the scheduler creates a run for each completed day. Today isn't included, because its interval hasn't finished.

Catchup also applies when you pause a DAG and unpause it later. Every interval missed during the pause gets a run.

## The default in Airflow 2 and Airflow 3

A DAG that doesn't set `catchup` takes the `catchup_by_default` setting from the `[scheduler]` config. It's `True` in Airflow 2 and `False` in Airflow 3, so an unset DAG changes behaviour when you upgrade. Set `catchup` on each DAG to avoid depending on either default.

## How to load history with catchup off

A backfill runs a date range you choose, so `catchup=False` doesn't stop you loading history. The command differs by version.

```bash
# Airflow 3
airflow backfill create --dag-id daily_orders \
    --from-date 2026-01-01 \
    --to-date 2026-01-31 \
    --max-active-runs 3

# Airflow 2
airflow dags backfill --start-date 2026-01-01 --end-date 2026-01-31 daily_orders
```

See [Airflow's catchup and backfill docs](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) for the other options.

## How max_active_runs limits catchup

`max_active_runs` caps how many runs of a DAG execute at once. It doesn't reduce how many get created, so a long catchup still works through every interval, just more slowly.

## What this rule checks

This rule flags scheduled Airflow DAGs that set `catchup=True`, or leave it unset before Airflow 3. `catchup=False` passes, and so does a DAG with `schedule=None` or no schedule we can read. Dagster and Prefect have no such default, so they are not judged.

## Why it matters

Catchup replays every interval between the start date and now. Pause a DAG for a fortnight, or deploy one with an old start date, and resuming it queues hundreds of runs at once. The warehouse bill and the load spike arrive together.

## How to fix it

**Stop a redeploy from replaying the schedule.** Set `catchup=False` unless the backfill is wanted. Where it is wanted, bound it with `max_active_runs`.

Before:

```python
with DAG("daily_orders", schedule="@daily", start_date=datetime(2024, 1, 1)):
    ...
```

After:

```python
with DAG(
    "daily_orders",
    schedule="@daily",
    start_date=datetime(2024, 1, 1),
    catchup=False,
):
    ...
```

## When it is fine to leave

A DAG meant to backfill, such as one loading partitions by date, is working as intended. An unset `catchup` is on in Airflow 2 and off in Airflow 3, so on Airflow 3 it passes. A deployment that changes the default in its own config is not visible to us; tell us, and the finding comes out.

## What we need to check it

The Airflow DAG files you name to us one at a time. We read your Airflow version from `requirements.txt` or `pyproject.toml` if you send them, or from an `airflow.sdk` import. `catchup=True` written out is high confidence; unset on Airflow 2 is medium, and low when we cannot tell the version.

[Ask about a review](https://metacenta.com/enquire?src=rule-dag-catchup-unbounded&via=md)

## Related pages

- [Airflow DAG owner: how to set it and what it does](https://metacenta.com/rules/dag-unowned)
- [Airflow DAGs with no retries](https://metacenta.com/rules/dag-no-retries)
- [Airflow DAGs with no failure alert](https://metacenta.com/rules/dag-no-failure-alert)
- [Passwords and API keys hardcoded in DAG files](https://metacenta.com/rules/dag-credential-literal)
- [A sample review, as a client reads it](https://metacenta.com/sample)
- [All published rules](https://metacenta.com/rules)
- [How a review is assessed](https://metacenta.com/standard)
