
- Data & Analytics
Apache Airflow Guide
Apache Airflow Guide
If you have ever had a data pipeline fail silently at 3 AM, a scheduled job run in the wrong order, or a team spend hours manually triggering tasks that should have run automatically — you have already felt the problem Apache Airflow was built to solve.
Airflow is the de facto standard for workflow orchestration. Over 80,000 organisations run it. It sees more than 30 million downloads every month. And in April 2025, it released its most significant update since version 2.0 in 2020.
This guide covers Apache Airflow 3.x — the latest major version — from installation through production-grade pipelines, updated for 2026.
What is Apache Airflow?
Apache Airflow is an open-source platform for programmatically authoring, scheduling, and monitoring workflows. Originally developed at Airbnb in 2014, it allows you to define workflows as Python code — represented as Directed Acyclic Graphs (DAGs), where nodes are tasks and edges define the order of execution.
Data engineers use it for ETL pipelines. Machine learning engineers use it for model training and inference workflows. DevOps teams use it for infrastructure automation.
What Changed in Airflow 3.x
Airflow 3.0 was released in April 2025. This is not a collection of feature additions — it changes the UI, the security model, how tasks communicate with the scheduler, and how DAG versions are tracked.
Important: Airflow 2.x reaches end-of-life in April 2026. If you are still on 2.x, migration planning should be underway now.
Rebuilt in React
The old Flask/Jinja UI is gone. The new React frontend loads faster, handles thousands of DAGs, and adds DAG pinning, 17 UI languages, and redesigned Grid and Gantt views.
Reproducible Runs
Every DAG run is tied to the exact version of code that triggered it. Updating a DAG no longer disrupts jobs already in flight.
Event-Driven Scheduling
Datasets from 2.x are renamed Assets. DAGs can now trigger automatically when an asset is updated — no workarounds needed.
Task Execution API
Tasks no longer access the metadata database directly. All state transitions go through a dedicated API — preventing buggy code from corrupting your installation.
Native Workflow Support
Model training, inference pipelines, and GenAI workflows are now first-class citizens — not bolted-on afterthoughts.
Unified airflow.sdk
DAG authoring now uses a single SDK surface. Imports that previously required scattered module paths are consolidated into one clean entry point.
Core Concepts
Directed Acyclic Graphs
A DAG is your entire workflow — schedule, retry policy, task order, and dependencies. In Airflow 3.x, every run references the exact code version that triggered it.
Units of Work
Tasks are individual work units created using Operators. Common ones include PythonOperator, BashOperator, EmailOperator, and Sensors for waiting on external events.
Data Dependencies
Assets represent data that DAGs produce or consume. When an upstream DAG updates an asset, downstream DAGs trigger automatically — no polling required.
How Tasks Run
SequentialExecutor for testing. LocalExecutor for single-machine parallelism. CeleryExecutor and KubernetesExecutor for distributed production workloads.
Installing Apache Airflow 3.x
Always install using the official constraint file. Without it, pip may resolve incompatible dependency versions that break Airflow silently.
# Set Airflow home
export AIRFLOW_HOME=~/airflow
AIRFLOW_VERSION=3.2.2
PYTHON_VERSION="$(python --version | cut -d ' ' -f 2 | cut -d '.' -f 1-2)"
CONSTRAINT_URL="https://raw.githubusercontent.com/apache/airflow/constraints-${AIRFLOW_VERSION}/constraints-${PYTHON_VERSION}.txt"
pip install "apache-airflow==${AIRFLOW_VERSION}" \
--constraint "${CONSTRAINT_URL}"
# Initialise the database
airflow db migrate
# Create admin user
airflow users create \
--username admin \
--firstname Admin \
--lastname User \
--role Admin \
--email admin@example.com
# Start services in separate terminals
airflow webserver --port 8080
airflow scheduler
Airflow 3.x change: airflow db init is replaced by airflow db migrate. The new React UI is at http://localhost:8080.
Your First DAG in Airflow 3.x
Create a Python file in your DAGs folder. Airflow 3.x uses SDK-first imports and a cleaner context manager syntax.
from datetime import datetime, timedelta
from airflow.sdk import DAG # New unified import in 3.x
from airflow.operators.bash import BashOperator
from airflow.operators.python import PythonOperator
default_args = {
'owner': 'data-team',
'retries': 2,
'retry_delay': timedelta(minutes=5),
}
with DAG(
dag_id='hello_airflow',
default_args=default_args,
schedule=timedelta(days=1), # schedule_interval deprecated in 3.x
start_date=datetime(2026, 1, 1),
catchup=False,
tags=['tutorial'],
) as dag:
print_date = BashOperator(
task_id='print_date',
bash_command='date',
)
def greet():
print("Hello from Airflow 3.x!")
greet_task = PythonOperator(
task_id='greet_user',
python_callable=greet,
)
print_date >> greet_task
The TaskFlow API — using @dag and @task decorators — is the recommended approach in Airflow 3.x. It handles XCom automatically and produces much cleaner code.
from datetime import datetime
from airflow.sdk import dag, task
@dag(schedule="@daily", start_date=datetime(2026, 1, 1), catchup=False)
def my_pipeline():
@task
def extract():
return {"records": 100, "status": "ok"}
@task
def transform(data: dict):
data["processed"] = True
return data
@task
def load(data: dict):
print(f"Loading {data['records']} records")
load(transform(extract()))
my_pipeline()
Managing Task Dependencies
Airflow uses Python's bitshift operators to define execution order — readable, expressive, and handles fan-out and fan-in cleanly.
# Linear: A then B then C
task_a >> task_b >> task_c
# Fan-out: A triggers B, C, D simultaneously
task_a >> [task_b, task_c, task_d]
# Fan-in: B, C, D must complete before E
[task_b, task_c, task_d] >> task_e
# Complex chains
start >> [parallel_1, parallel_2] >> combine >> end
Building a Production ETL Pipeline
A complete production-ready pipeline using the TaskFlow API. Data passes between tasks naturally — no manual XCom boilerplate required.
from datetime import datetime, timedelta
from airflow.sdk import dag, task
import pandas as pd, requests, json
@dag(
schedule='0 3 * * *',
start_date=datetime(2026, 1, 1),
catchup=False,
default_args={
'owner': 'data-engineering',
'retries': 3,
'retry_delay': timedelta(minutes=5),
},
)
def etl_pipeline():
@task
def extract(**context):
url = f'https://api.example.com/data?date={context["ds"]}'
data = requests.get(url, timeout=30).json()
path = f'/tmp/raw_{context["ds"]}.json'
with open(path, 'w') as f:
json.dump(data, f)
return path
@task
def transform(file_path: str, **context):
df = pd.read_json(file_path)
df['processed_date'] = pd.to_datetime(context['ds'])
df['value_doubled'] = df['value'] * 2
out = f'/tmp/transformed_{context["ds"]}.csv'
df.to_csv(out, index=False)
return out
@task
def load(file_path: str):
import sqlite3
df = pd.read_csv(file_path)
conn = sqlite3.connect('/tmp/warehouse.db')
df.to_sql('analytics', conn, if_exists='append', index=False)
conn.close()
load(transform(extract()))
etl_pipeline()
Advanced Patterns
Dynamic Task Mapping
@task
def process_partition(partition_id: str):
print(f"Processing {partition_id}")
# Tasks created dynamically at runtime
process_partition.expand(
partition_id=['north', 'south', 'east', 'west']
)
Asset-Driven Scheduling (new in 3.x)
from airflow.sdk import Asset
orders_asset = Asset("s3://my-bucket/orders/")
# Producer — publishes the asset
@dag(schedule="@daily")
def produce_orders():
@task(outlets=[orders_asset])
def write_orders():
pass
write_orders()
# Consumer — triggers when asset updates
@dag(schedule=[orders_asset])
def consume_orders():
@task
def process():
print("New orders — processing now")
process()
Airflow 2.x vs 3.x — Key Differences
| Feature | Airflow 2.x | Airflow 3.x |
|---|---|---|
| Imports | Multiple scattered paths | airflow.sdk |
| Schedule param | schedule_interval | schedule |
| DB init command | airflow db init | airflow db migrate |
| UI framework | Flask / Jinja | React |
| DAG versioning | Not available | Built-in |
| Event scheduling | Datasets (limited) | Assets (full support) |
| Task DB access | Direct (security risk) | Via Task Execution API |
| AI/ML workflows | Via plugins only | Native |
| End of life | April 2026 | Active development |
Best Practices for Production
Design for Idempotency
Every task should produce the same result whether it runs once or ten times. Delete before inserting — never append blindly.
@task
def idempotent_load(**context):
import sqlite3
date = context['ds']
conn = sqlite3.connect('/tmp/db.db')
conn.execute(
"DELETE FROM metrics WHERE date = ?", (date,)
)
df = pd.read_csv(f'/tmp/data_{date}.csv')
df.to_sql('metrics', conn, if_exists='append', index=False)
conn.commit()
conn.close()
Never Hardcode Credentials
from airflow.models import Variable
# Store via CLI: airflow variables set api_key "value"
api_key = Variable.get("api_key")
# Configure DB connections via Admin > Connections in the UI
# Never hardcode credentials directly in DAG files
Essential CLI Commands
airflow dags list
airflow dags trigger etl_pipeline
airflow tasks test etl_pipeline extract 2026-01-15
airflow dags pause etl_pipeline
airflow dags unpause etl_pipeline
airflow dags backfill etl_pipeline -s 2026-01-01 -e 2026-01-31
Frequently Asked Questions
What is the latest version of Apache Airflow?
The latest stable version is Apache Airflow 3.2.2, released on 29 May 2026. The 3.x line is the current active major version and includes a new React UI, DAG versioning, asset-driven scheduling, and native AI and ML workflow support.
Should I upgrade from Airflow 2.x to 3.x?
Yes. Airflow 2.x reaches end-of-life in April 2026. If you need event-driven scheduling, DAG versioning, improved security, or a modern UI, migrate now. Test thoroughly on a staging environment before cutting over to production.
What replaced Datasets in Airflow 3.x?
Datasets from Airflow 2.x are renamed to Assets in Airflow 3.x. Assets support richer event-driven scheduling — downstream DAGs trigger automatically when an upstream DAG produces or updates an asset, without polling or workarounds.
What is the TaskFlow API in Apache Airflow?
The TaskFlow API lets you write DAGs using Python decorators — @dag and @task — instead of manually instantiating Operator classes. It handles XCom data passing automatically between tasks and produces significantly cleaner, more readable code. It is the recommended approach in Airflow 3.x.
What is the difference between schedule and schedule_interval?
schedule_interval was the parameter used in Airflow 2.x. In Airflow 3.x, it is replaced by schedule. The new parameter accepts cron expressions, timedelta objects, preset strings like @daily, or a list of Assets for event-driven triggering.
What does airflow db migrate do?
airflow db migrate is the Airflow 3.x replacement for airflow db init. It initialises or upgrades the Airflow metadata database schema. Run it after installing Airflow for the first time, and again whenever you upgrade to a new version.
Can I run Airflow 3.x with Python 3.8?
No. Airflow 3.x requires Python 3.9 or higher. Python version support has been modernised in the 3.x line, dropping older versions to reduce the dependency surface and improve stability.
Need Help Implementing Airflow in Your Organisation?
Auriga IT helps engineering teams architect, deploy, and scale data pipelines using Apache Airflow and modern data engineering tools.
Whether you are starting fresh or migrating from 2.x, we can help you get it right.
Talk to Our TeamRelated content
Auriga: Leveling Up for Enterprise Growth!
Auriga’s journey began in 2010 crafting products for India’s [...]






