How to Schedule an Airflow DAG (Extract, Transform, Load)

Wrapping a Python ETL script in an Airflow DAG gives you retries, dependency tracking, and visibility into every run - none of which cron can offer. The pattern is three PythonOperator tasks linked with >> and a daily schedule. Here's how to build it.

Data Engineerairflowdagorchestration

From cron to a DAG

A bare python3 etl.py cron job runs blindly: if it fails at 2 AM nobody knows until morning, there is no retry, and there is no record of which step broke. Airflow replaces that with a DAG (Directed Acyclic Graph) - a set of tasks with explicit dependencies, so Airflow knows to run transform only after extract succeeds, and to retry on failure before alerting.

The DAG file

Create dags/daily_etl.py. The modern idiom is a with DAG(...) context manager:

import csv, json
from datetime import datetime, timedelta
from airflow import DAG

try:
    from airflow.providers.standard.operators.python import PythonOperator
except ImportError:                              # older Airflow
    from airflow.operators.python import PythonOperator

def extract():
    rows = []
    with open("/tmp/raw_orders.csv") as f:
        for row in csv.DictReader(f):
            rows.append(row)
    with open("/tmp/raw_orders.json", "w") as f:
        json.dump(rows, f)
    print(f"extracted {len(rows)} rows")

def transform():
    with open("/tmp/raw_orders.json") as f:
        rows = json.load(f)
    for r in rows:
        r["total"] = float(r["price"]) * int(r["quantity"])
    with open("/tmp/transformed_orders.json", "w") as f:
        json.dump(rows, f)

def load():
    with open("/tmp/transformed_orders.json") as f:
        rows = json.load(f)
    total = sum(r["total"] for r in rows)
    with open("/tmp/load_summary.txt", "w") as f:
        f.write(f"rows={len(rows)} total_revenue={total:.2f}\n")

default_args = {
    "owner": "data-team",
    "retries": 2,
    "retry_delay": timedelta(seconds=10),
}

with DAG(
    dag_id="daily_etl",
    schedule="@daily",
    start_date=datetime(2024, 1, 1),
    catchup=False,
    default_args=default_args,
) as dag:
    t_extract   = PythonOperator(task_id="extract",   python_callable=extract)
    t_transform = PythonOperator(task_id="transform", python_callable=transform)
    t_load      = PythonOperator(task_id="load",      python_callable=load)

    t_extract >> t_transform >> t_load

Key parameters to understand

Testing without a scheduler

airflow dags test runs the DAG in-process - no scheduler, no worker, no database writes - which makes it ideal for local/CI verification:

export AIRFLOW_HOME=/tmp/airflow
export AIRFLOW__CORE__DAGS_FOLDER=/workspace/dags
export AIRFLOW__CORE__LOAD_EXAMPLES=False

airflow dags test daily_etl 2024-01-01

All three tasks run sequentially and their output files appear under /tmp/. If a task fails, the traceback shows immediately - no need to dig through the UI.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What is an Airflow DAG and why use it instead of cron?

A DAG (Directed Acyclic Graph) is a set of tasks with defined dependencies and a schedule. Unlike cron, Airflow retries failed tasks, records every run's status, visualizes the dependency graph, and alerts on failure - so you know exactly which step broke and why.

How do I set task dependencies in Airflow?

Use the >> operator between task objects: t_extract >> t_transform >> t_load. This tells Airflow to run transform only after extract succeeds, and load only after transform succeeds.

How do I test an Airflow DAG without running the full scheduler?

Run airflow dags test <dag_id> <execution_date>. It executes all tasks in-process in dependency order - no scheduler, worker, or triggerer needed. This is the standard way to verify a DAG before deploying it.

Keep learning

Make an ETL Pipeline IdempotentData projectSpeed Up a Slow SQL Report QueryData projectMake a CSV Importer Resilient to Bad DataData projectData roadmapStep by step to hiredData interview questionsSTAR answersAll Data projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →