Airflow DAGs
A DAG (Directed Acyclic Graph) is the core building block of Apache Airflow. Every workflow you create in Airflow is a DAG. This topic explains what a DAG is, how to write one, and how Airflow uses it.
What Does "DAG" Mean in Plain English?
Break the term into three words:
- Directed: Tasks run in a specific direction — from start to finish. Task B cannot start before Task A finishes.
- Acyclic: There are no loops. A task cannot circle back and trigger an earlier task. The workflow always moves forward.
- Graph: The workflow is a network of connected tasks drawn as a diagram.
The Recipe Analogy
Think of baking a cake. You must follow steps in order:
[Mix Batter] → [Pour Into Pan] → [Bake in Oven] → [Apply Frosting]
You cannot apply frosting before baking. You cannot bake before mixing. The recipe is a DAG — directed (in order), acyclic (no going back), and a graph (connected steps).
Your First DAG in Python
Create a file called my_first_dag.py inside the ~/airflow/dags/ folder:
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
def greet():
print("Hello from Task 1!")
def farewell():
print("Goodbye from Task 2!")
with DAG(
dag_id="my_first_dag",
start_date=datetime(2024, 1, 1),
schedule="@daily",
catchup=False,
) as dag:
task1 = PythonOperator(
task_id="say_hello",
python_callable=greet,
)
task2 = PythonOperator(
task_id="say_goodbye",
python_callable=farewell,
)
task1 >> task2
Breaking Down the Code Line by Line
The Imports
from airflow import DAG from airflow.operators.python import PythonOperator from datetime import datetime
You import the DAG class to define the workflow, PythonOperator to run Python functions as tasks, and datetime to set the start date.
The Python Functions
def greet():
print("Hello from Task 1!")
def farewell():
print("Goodbye from Task 2!")
These are the actual jobs your tasks will perform. They are plain Python functions.
The DAG Definition Block
with DAG(
dag_id="my_first_dag",
start_date=datetime(2024, 1, 1),
schedule="@daily",
catchup=False,
) as dag:
| Parameter | Purpose |
|---|---|
| dag_id | Unique name for the DAG shown in the UI |
| start_date | The first date the DAG becomes eligible to run |
| schedule | How often the DAG runs (@daily, @hourly, cron string, etc.) |
| catchup | False = do not run missed past schedules; True = run them all |
Setting Task Order With >>
task1 >> task2
The >> operator sets dependency. It means "task1 must finish before task2 starts." You can read it as "task1 flows into task2."
DAG with Branching Tasks
Not all DAGs run in a straight line. Some workflows branch based on conditions:
[check_stock]
/ \
[reorder_items] [send_ok_report]
If stock is low, the left branch runs. If stock is fine, the right branch runs. In code:
check_stock >> [reorder_items, send_ok_report]
You can also write:
check_stock >> reorder_items check_stock >> send_ok_report
Both lines produce the same branched structure.
Key DAG Parameters You Will Use Often
default_args
Use default_args to apply the same settings to every task in the DAG without repeating them:
default_args = {
"owner": "data_team",
"retries": 2,
"retry_delay": timedelta(minutes=5),
"email_on_failure": True,
"email": ["team@example.com"],
}
with DAG(dag_id="sales_dag", default_args=default_args, ...) as dag:
...
catchup
If you create a DAG today with a start_date one month ago and catchup=True, Airflow will try to run the DAG for every missed day over the past month. Set catchup=False for most use cases to avoid this.
tags
Tags let you filter DAGs in the UI. Useful when you have dozens of DAGs:
with DAG(dag_id="sales_dag", tags=["sales", "daily"], ...) as dag:
How Airflow Reads Your DAG File
~/airflow/dags/
│
▼
Airflow Scheduler scans this folder every 30 seconds
│
▼
Finds my_first_dag.py
│
▼
Parses (reads) the Python file
│
▼
Registers "my_first_dag" in the database
│
▼
Shows it in the UI
Airflow does not run your Python file when the DAG triggers. It parses the file at load time to understand the structure, then executes each task function individually when the schedule fires.
Common DAG Mistakes and Fixes
| Mistake | Symptom | Fix |
|---|---|---|
| Import error in DAG file | DAG shows "Import Error" in UI | Check your Python imports and syntax |
| Duplicate dag_id | Only one DAG appears | Each DAG needs a unique dag_id |
| catchup=True on old start_date | Hundreds of runs queue up instantly | Set catchup=False or set start_date to today |
| Code runs at module level | Slow DAG loading, UI lags | Move heavy code inside task functions, not at the top level |
