DAG Versioning in Airflow 3.0

Airflow 3.0 introduces DAG Versioning: each DAG run is associated with the code version as it was at scheduling time. No more surprises where a running DAG is affected by a production code deployment.

How It Works

Airflow automatically captures a DAG snapshot at scheduling time. If you deploy a new DAG version while a run is in progress, remaining tasks continue with the old version. New runs use the updated version.

python
from airflow.sdk import DAG, task
from datetime import datetime


# Version 1 of the DAG (deployed Monday)
with DAG(
    dag_id='etl_pipeline',
    schedule='@daily',
    start_date=datetime(2025, 4, 1),
) as dag:

    @task
    def extract():
        """Extract from source A."""
        return {'source': 'A', 'rows': 1000}

    @task
    def transform(data: dict):
        """Transform v1: simple cleanup."""
        data['valid_rows'] = int(data['rows'] * 0.95)
        return data

    @task
    def load(data: dict):
        print(f"Loading {data['valid_rows']} rows")

    load(transform(extract()))

# If you deploy v2 on Tuesday that changes transform(),
# Monday's run (in progress) keeps v1.
# Tuesday's run will use v2.

Viewing Versions

The Airflow 3.0 web UI shows the version associated with each DAG Run. You can also check it via the REST API or CLI.

bash
# List versions of a DAG
airflow dags list-versions --dag-id etl_pipeline

# See which version is associated with a DAG Run
airflow dags list-runs --dag-id etl_pipeline
# Shows: run_id | dag_version | state | start_date

Sources