start_date and the First Run

Concepts covered: Data intervals

The first boundary

start_date is the first boundary the schedule may use. The first window opens there and the first run is created when that window closes, 1 full period later. Most people read start_date as the moment the DAG first runs, deploy at 09:00 with a start_date of midnight that morning, and then watch an empty grid for 15 hours wondering what they broke.

Take daily_sales, a midnight cron with start_date 4 November 2026 00:00 UTC, deployed and unpaused at 09:00 on 4 November. Its first window is 4 November 00:00 to 5 November 00:00. That window is still open at 09:00, at 15:00 and at 23:59. At 00:00 on 5 November it closes, and the first run appears, named scheduled__2026-11-04T00:00:00+00:00. Nothing was wrong during the wait; the DAG had no complete day to count.

import pendulum
from airflow.sdk import dag, task
from airflow.timetables.interval import (
    CronDataIntervalTimetable,
)
DAILY = CronDataIntervalTimetable(
    "0 0 * * *", timezone="UTC")

@dag(
    dag_id="daily_sales",
    start_date=pendulum.datetime(
        2026, 11, 4, tz="UTC"),
    schedule=DAILY,
)
def daily_sales():
    @task
    def summarize():
        ...
    summarize()

daily_sales()
Unpaused at 09:00 on 4 November, the DAG shows no run until its first window closes at midnight.

When start_date does not land on one of the cron's instants, the first window opens at the first instant at or after it. The same start_date of midnight with 0 6 * * * gives a first window of 4 November 06:00 to 5 November 06:00, and a first run at 06:00 on 5 November. A start_date set in the future behaves the same way: 10 November with a midnight cron yields a first run at 00:00 on 11 November.

start_dateScheduleFirst windowFirst run created
2026-11-04 00:00`0 0 * * *`4 Nov 00:00 to 5 Nov 00:005 Nov 00:00
2026-11-04 00:00`0 6 * * *`4 Nov 06:00 to 5 Nov 06:005 Nov 06:00
2026-11-04 00:00`0 0 * * 0`8 Nov 00:00 to 15 Nov 00:0015 Nov 00:00
2026-11-10 00:00`0 0 * * *`10 Nov 00:00 to 11 Nov 00:0011 Nov 00:00

The weekly row is the one that surprises most. A start_date on Wednesday 4 November with a Sunday schedule sits 4 days before the first instant, so the first window opens on Sunday 8 November and its run arrives on Sunday 15 November, 11 days after deployment. The future row is the cleanest way to launch on a chosen day: set 10 November, unpause whenever you like before then, and the first run arrives at midnight on the 11th with no older windows to think about.

Paused on arrival

A DAG that appears for the first time arrives paused unless the deployment is set otherwise, and while a DAG is paused Airflow creates no runs for it, however many windows close. Unpause it before its first window closes and the first run appears exactly when that window ends, as in the animation. Unpause it after one or more windows have closed and the next section decides what happens to them. Either way, the unpause itself creates nothing on the spot when no window has closed.

A start_date that moves

Airflow re-reads every DAG file on a timer while it runs, so a start_date of datetime.now() is a different moment at every read. The first window opens at the latest read, and before it can close the file is read again and the window opens later still. It never closes, and the DAG never runs, with nothing in the logs to say why. Fix the date to a literal, pendulum.datetime(2026, 11, 4, tz="UTC"), and the first boundary stays put from one read to the next.

Do
  • Give every DAG a fixed, time zone aware start_date with pendulum.datetime.
  • Expect the first run 1 full period after start_date, at the first boundary after it.
  • Unpause a new DAG before its first window closes if you want that window's run on time.
Avoid
  • Use datetime.now() or any value that changes between reads as start_date.
  • Read start_date as the clock time the first run begins.
  • Set start_date to a cron instant and expect a run at that same instant.

About this lesson

This section is part of the Scheduling: Beginner lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.