The schedule Parameter
The schedule parameter (renamed from schedule_interval in Airflow 2.4+, though the old name still works) tells Airflow how often to create new DAG runs. It accepts a cron string like '0 6 * * *', a datetime.timedelta for interval-based scheduling, a cron preset like @daily, @hourly, or @weekly, None for a DAG that only runs when manually triggered or externally triggered via the API, and, since Airflow 2.4, a list of Dataset objects for data-aware scheduling that fires a DAG when upstream tasks in other DAGs update those datasets.
Cricket analogy: A cron schedule is like a franchise league's fixed match calendar, every Wednesday and Saturday at 7:30 PM, while dataset-based scheduling is more like a knockout match only being scheduled once both semifinal results (the datasets) are confirmed.
Cron Expression Syntax
A standard cron expression has five space-separated fields: minute (0-59), hour (0-23), day of month (1-31), month (1-12), and day of week (0-6, where 0 is Sunday). The expression '0 6 * * 1-5' means 'at 06:00, every day of the month, every month, but only Monday through Friday', a common pattern for business-day-only pipelines. Airflow uses croniter internally to calculate the next run time from a cron string, and you can combine ranges (1-5), lists (1,15), and steps (*/15 for every 15 minutes) within any field.
Cricket analogy: '0 6 * * 1-5' is like a franchise scheduling optional net practice at 6 AM only on weekday mornings, skipping the fixed weekend match days entirely from that particular routine.
from airflow import DAG
import pendulum
# Runs every weekday at 6 AM IST
with DAG(
dag_id="weekday_morning_report",
schedule="0 6 * * 1-5",
start_date=pendulum.datetime(2026, 1, 1, tz="Asia/Kolkata"),
catchup=False,
max_active_runs=1,
) as dag:
...
# Runs every 15 minutes
with DAG(
dag_id="near_realtime_sync",
schedule="*/15 * * * *",
start_date=pendulum.datetime(2026, 1, 1, tz="UTC"),
catchup=False,
) as dag:
...Logical Date, Data Interval, and Catchup
One of the most confusing parts of Airflow scheduling is that a DAG run scheduled for 2026-07-10 with a daily schedule doesn't actually trigger on July 10th; it triggers at the end of that interval, meaning just after midnight on July 11th, because Airflow assumes the interval [2026-07-10, 2026-07-11) must fully elapse before there's complete data for it. The logical_date (formerly execution_date) represents the start of that data interval, not the moment the run was actually kicked off, which is why templated variables like {{ ds }} refer to the interval being processed rather than 'today'.
Cricket analogy: It's like a monthly wicket-count report for June only being compiled and published in early July, once the full month of matches has actually finished, even though the report is labeled 'June'.
catchup (default True) determines whether Airflow automatically creates DAG runs for every interval between start_date and now the first time a DAG is turned on; setting catchup=False skips straight to only running the most recent interval going forward. max_active_runs caps how many DAG runs can execute concurrently, which matters a lot when catchup is enabled and dozens of missed intervals need to run without overwhelming the environment. Manual backfills for specific date ranges can also be triggered explicitly via airflow dags backfill, independent of the catchup setting.
Cricket analogy: catchup=True is like a broadcaster deciding to re-air every missed match highlight reel from a tournament's opening week once they finally get broadcast rights, rather than only showing highlights from today's match onward.
Always use timezone-aware start_date values (via pendulum.datetime(..., tz='Asia/Kolkata') rather than naive Python datetime) since Airflow internally stores and compares everything in UTC, and naive datetimes can cause subtle off-by-one-hour scheduling bugs, especially around daylight saving transitions.
Leaving catchup=True (the default) with a start_date set far in the past can cause a 'backfill storm': the moment the DAG is unpaused, Airflow immediately queues every missed interval since start_date, which can flood the scheduler and workers. Explicitly set catchup=False unless you specifically need historical backfilling.
- The schedule parameter accepts cron strings, timedeltas, presets like @daily, None, or a list of Datasets for data-aware scheduling.
- A cron expression has five fields: minute, hour, day of month, month, and day of week, supporting ranges, lists, and steps.
- A DAG run triggers at the end of its data interval, not at the start; logical_date represents the interval's start, not the trigger time.
- catchup=True (the default) creates DAG runs for every missed interval since start_date the first time a DAG is unpaused.
- max_active_runs limits concurrent DAG runs, which is important when catchup produces many queued intervals at once.
- airflow dags backfill lets you manually trigger runs for a specific date range independent of the catchup setting.
- Always use timezone-aware start_date values to avoid subtle scheduling bugs around UTC conversion and daylight saving time.
Practice what you learned
1. What does the cron expression '0 6 * * 1-5' mean?
2. Why does a daily DAG run for logical_date 2026-07-10 actually trigger just after midnight on 2026-07-11?
3. What risk does leaving catchup=True with a start_date far in the past create?
4. What does max_active_runs control?
Was this page helpful?
You May Also Like
DAG Definition Basics
Learn how Apache Airflow DAGs are structured as Python files, parsed by the scheduler, and how to define one correctly with dag_id, schedule, and default_args.
Task Dependencies
Learn how to wire tasks together with bitshift operators and the chain() helper, and how trigger rules control execution when upstream tasks fail or skip.
The TaskFlow API
Learn how Airflow's @dag and @task decorators let you write DAGs as plain Python functions, with automatic XCom passing and dynamic task mapping.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics
ProgrammingPowerShell Study Notes
Programming · 30 topics