Home DevOps Apache Airflow Basics: What Breaks When Cron Runs It
Beginner 3 min · August 1, 2026
Airflow Introduction

Apache Airflow Basics: What Breaks When Cron Runs It

Airflow is cron with a brain: tracked runs, retries, dependencies, and idempotent tasks.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Production
production tested
August 01, 2026
last updated
1,750
articles · all by Naren
Before you start⏱ 15-20 min read
  • Comfortable with the Linux command line and crontab.
  • Python fundamentals: functions, imports, decorators.
  • No Airflow experience needed — this is the series entry point.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Apache Airflow is a workflow engine that schedules and tracks DAGs of tasks — work cron fires blindly and forgets.
  • The 4 components: webserver, scheduler, executor, and the metadata DB they all talk through.
  • Every execution becomes a DAG Run with task states; failures retry visibly, never silently.
  • One slow file stalls everything: a 12-second DAG import delays every schedule in the deployment.
  • Idempotent tasks are the first law — reruns, retries, and backfills must not double-apply work.
  • Use Airflow for dependency-heavy graphs; cron stays fine for one command with no state.
✦ Definition~90s read
What is Airflow Introduction?

Apache Airflow is an open-source workflow engine that schedules and tracks DAGs of tasks, persisting every run, retry, and task state in a metadata database.

Imagine a night-shift guard who checks the same door every hour but never writes anything down.
Plain-English First

Imagine a night-shift guard who checks the same door every hour but never writes anything down. If he misses a round, he has no idea — and neither does anyone else. Cron is that guard: it fires a command on schedule and keeps zero memory of what happened. Airflow is the same guard with a notebook: every round is recorded, failures get retried, and you can page through last week's entries to see exactly what ran and when. That notebook — the metadata database — is what turns scheduling from a guess into an auditable system.

Cron is the most dangerous scheduler in your stack, and it's not close. It fires commands on schedule and forgets them instantly — no state, no retries, no dependencies, no idea what already ran. That amnesia cost a billing team their customers' trust when a missed window silently re-ran the monthly invoicing job and double-billed everyone.

A workflow engine exists to remember. Apache Airflow schedules directed acyclic graphs (DAGs) of tasks, tracks every run in a metadata database, retries failures, and makes each execution auditable. Four components — webserver, scheduler, executor, metadata DB — cooperate through that one database, which is why losing it feels like losing the whole system.

This article builds the mental model from the failure up: what cron can't do, what a DAG is, how the components talk, and why idempotency is the first law of every pipeline you'll write. Then it gets honest about when Airflow is the answer, and when it's a sledgehammer for a nail.

Cron never learns from its mistakes. You're about to use a tool that does.

1. What a Workflow Engine Does That Cron Can't

Cron fires commands on a schedule. That's the whole contract: minute, hour, day, command. It has no memory of what ran, no notion of success beyond the exit code, and no way to express "run this only after that finished."

A workflow engine adds the three things cron never had: state, dependencies, and retries. Every execution becomes a tracked run with an ID. Every task records a transition from queued to running to success or failure. When a task fails, the engine retries it — with backoff, without a human paging at 3 AM.

The billing incident was a state problem. Cron re-ran a job over data it had already processed because nothing recorded that it had. Airflow's metadata DB is that memory.

billing.cronBASH
1
2
3
4
5
# /etc/crontab — the billing job that double-billed
0 2 1 * * root /opt/billing/run_monthly_billing.sh

# After cron fires it, the system remembers one thing: an exit code.
# No run ID. No state. No retry. No dependency. No audit trail.
📊 Production Insight
Cron gives you one exit code and amnesia.
State is the whole game.
Record every run before you can retry it.
🎯 Key Takeaway
Cron fires commands; Airflow tracks work.
No state means no retries, no dependencies, no audit.
State is the difference — use a tool that remembers.
Cron vs a Workflow Engine: State Is the Game Cron vs a Workflow Engine: State Is the Game Billing incident: amnesia plus a missed window ASPECT CRON AIRFLOW State of past runs None — amnesia DAG runs + states in DB Success signal Exit code only Task states, audited Retries None Built-in with backoff Dependencies No ordering Explicit edges Failure visibility Syslog, if read UI + per-task logs Missed window re-ran · double billing Fix: dedupe key per customer-month THECODEFORGE.IO
thecodeforge.io
Airflow Introduction

2. The DAG: Tasks, Edges, and Acyclicity

A DAG is a directed acyclic graph: nodes are tasks, edges are dependencies. "Acyclic" is the non-negotiable part — no task may depend on itself through a cycle, because the scheduler couldn't decide where to start. Airflow parses your file and refuses cycles at import time.

Tasks are units of work: pull an API, write a file, run SQL, send email. Edges express order: extract >> transform >> load reads as "extract, then transform, then load." The graph is the pipeline's documentation — the Grid view shows every run's graph with states colored per task.

A cron job is one point in time. A DAG is a structure: branching, joining, retrying, and failing in ways that stay visible.

Every task needs a task_id and an owner — a fresh install defaults the owner to airflow, and Airflow raises an error if either is missing. Every DAG run also carries a data interval: the slice of data (and time) the run operates on, which is what makes reruns and backfills addressable. And use timezone-aware datetimes (pendulum) for start_date — the Python stdlib timezone has known limitations in Airflow.

dags/monthly_billing.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from datetime import datetime

from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.operators.python import PythonOperator


def generate_invoice(customer_id: str) -> dict:
    return {"customer_id": customer_id, "amount_usd": 49.99}


with DAG(
    dag_id="monthly_billing",
    schedule="@monthly",
    start_date=datetime(2026, 1, 1),
    catchup=False,
) as dag:
    extract_customers = BashOperator(
        task_id="extract_customers",
        bash_command="python /opt/billing/extract_customers.py",
    )
    generate_invoice_task = PythonOperator(
        task_id="generate_invoice",
        python_callable=generate_invoice,
        op_kwargs={"customer_id": "cust_1042"},
    )
    extract_customers >> generate_invoice_task
📊 Production Insight
Draw the graph before writing the code.
Cycles fail at import — catch them in review.
One task, one unit of work, one exit code.
🎯 Key Takeaway
Tasks are nodes, dependencies are edges, cycles are banned.
The graph is the design document.
If you can't draw it acyclic, you can't schedule it.

3. The Four Components and the Metadata DB

Airflow ships four processes. The webserver renders the UI and the REST API. The scheduler decides what should run and when. The executor — a class configured per deployment — claims ready tasks and runs them. The metadata database persists everything: DAG definitions, DAG runs, task states, XComs, connections.

The trick is that no component talks to another directly. The scheduler writes "this task is queued" into the DB; the executor reads it, runs the work, and writes back the outcome; the webserver reads the same rows for the UI. The DB is the single source of truth — which is why losing it looks like losing the whole system.

SQLite ships by default and is fine for learning. Production runs Postgres; every real deployment I've seen graduates to it within weeks.

Mental Model
The Shared Mailbox
Think of the metadata DB as the shared mailbox.
  • The scheduler posts runs and states; the executor collects tasks and posts results.
  • The webserver only reads — it's a window into the mailbox.
  • Any component can be restarted; the mailbox keeps the truth.
📊 Production Insight
Back up the metadata DB before you tune anything.
Losing it erases run history.
Use Postgres for anything beyond local learning.
🎯 Key Takeaway
Webserver, scheduler, executor, metadata DB.
All four share one source of truth.
The DB is the system; the rest are clients.
Four Components, One Shared Mailbox Four Components, One Shared Mailbox No component talks to another directly Scheduler Decides what runs, and when Webserver UI + REST API Metadata DB DAG runs · task states XComs · connections Executor Runs ready task instances Losing the DB loses the whole system SQLite for learning · Postgres in prod THECODEFORGE.IO
thecodeforge.io
Airflow Introduction

4. Idempotency as the First Law

Run the same DAG twice and the world should look identical. That's idempotency, and it's the property that makes retries, backfills, and catchup safe. A retry is a re-execution of a task — if that task appends to a table, you now have double rows.

Design for it: upsert by natural key, delete-then-insert per partition, or skip rows already processed. The billing DAG's fix was a dedupe key — an invoice row only existed once per customer-month.

Airflow will re-run your tasks. Assume it. Build every task as if it will execute twice, because it will.

⚠ Retries Assume Idempotency
Airflow retries failed tasks by default. If a task half-completed — rows written, then the process died — the retry re-executes from scratch. Only idempotent tasks survive that.
📊 Production Insight
Assume every task executes at least twice.
Dedupe keys cost nothing; duplicate invoices cost customers.
Write tasks that are safe to rerun.
🎯 Key Takeaway
Idempotent means rerun-safe.
Retries and backfills will re-execute tasks.
Make every task safe to run twice.

5. Airflow's History: Airbnb 2014 to Apache 3.x

Airflow started at Airbnb in 2014 as an internal tool for the company's growing data pipelines. It hit the Apache Incubator in 2016 and became a top-level Apache project in 2019. The 1.x era made the scheduler, the DAG authoring model, and the UI.

2.0 (2020) rewrote the scheduler, added the TaskFlow API and the Grid view. 3.x is the current major — new operator module paths like airflow.operators.bash, assets replacing datasets, the triggerer, and object storage support.

Why does history matter? You'll meet 1.x-era DAGs in production codebases: default_args dicts, schedule_interval, imports from airflow.operators.bash_operator. The core model hasn't changed since 2014 — DAGs, tasks, states, a scheduler, a DB. That stability is why the platform still runs a huge share of the world's batch pipelines.

📊 Production Insight
Old DAGs use 1.x idioms; new ones use 3.x.
Read both fluently on codebases older than a year.
Write new code in the current style.
🎯 Key Takeaway
2014 Airbnb script → 2016 Apache → 3.x today.
The model survived three majors unchanged.
Learn the core model; the idioms will keep changing.
Airflow's History: One Model, Three Majors Airflow's History: One Model, Three Majors 2014 Airbnb script → 2016 Apache → 3.x today 2014 Airbnb 2016 Apache Incubator 2019 Top-level project 1.x era Scheduler · DAGs · UI 2.0 · 2020 TaskFlow · Grid view 3.x today Triggerer · assets Core model unchanged since 2014 Read 1.x DAGs, write 3.x style THECODEFORGE.IO
thecodeforge.io
Airflow Introduction

6. When Airflow Is — and Isn't — the Right Tool

Airflow wins when work has dependencies, needs retries, and must be visible: batch ETL, data platform orchestration, pipeline fan-in and fan-out. It loses when you need millisecond real-time triggers, long-running services, or a single nightly script that rarely fails.

If your problem is "one script, runs nightly, fails rarely" — cron is fine and honest. If it's "20 jobs, 5 depend on each other, failures must page someone" — that's Airflow's floor, not its ceiling.

The honest test: can you draw it as a DAG with tasks? If yes, Airflow is probably the right answer. If the graph would be one node, you're paying for a platform to run a shell script.

📊 Production Insight
One script, nightly, no deps — keep cron.
Anything drawable as a DAG belongs in Airflow.
Match the tool to the graph, not the hype.
🎯 Key Takeaway
Batch, dependent, visible work — Airflow's home.
Single scripts stay in cron.
Match the tool to the graph.
● Production incidentPOST-MORTEMseverity: high

The Cron Job That Double-Billed Every Customer

Symptom
Every customer on the email billing list received two identical invoices for the same month, and support tickets started piling up within hours.
Assumption
The team assumed cron would either run the job or miss it entirely — never silently re-run it over data that was already processed.
Root cause
Cron has no run state, no retry, and no dependency tracking. A midnight restart skipped the billing window, and the next scheduled fire re-ran the identical job over data nothing had marked as processed.
Fix
Replaced the cron entry with an Airflow DAG: tracked DAG runs, per-task retries, a dedupe key per customer-month, and idempotent invoice tasks that skip customers already billed.
Key lesson
  • Cron has no state — it cannot know what already ran.
  • A missed window can silently re-run against old data.
  • DAG runs give every execution an ID you can audit.
  • Every DAG run carries a data interval — the slice of data it operates on — which is what makes reruns and backfills addressable.
  • Make each task idempotent so retries never double-apply.
Production debug guideSymptom to Action4 entries
Symptom · 01
A DAG run is missing entirely
Fix
Check the schedule and data interval; a run fires after start_date plus one interval, not on start_date itself.
Symptom · 02
Tasks show failed but the log is empty
Fix
Open the task instance log in the Grid view; the executor may have died before capturing output.
Symptom · 03
Scheduling stopped across all DAGs
Fix
Restart the scheduler and watch heartbeats plus parse errors; one broken import stalls every file.
Symptom · 04
A DAG ran twice for the same window
Fix
Check for duplicate manual triggers or a cleared-and-rerun run; idempotent tasks absorb the rerun.
Cron vs Script Loop vs Airflow
AspectCronScript loopAirflow
State of past runsNone — cron forgets everythingNone in the process; logs only if you write themDAG runs and task states in the metadata DB
RetriesNone — failure just stopsWhatever you hand-rollBuilt-in retries with backoff
Dependency trackingSingle command per entrySequential by code orderExplicit edges between tasks
Failure visibilitySyslog, if you read itYour logs, your problemUI, per-task logs, alerting
IdempotencyNot enforcedNot enforcedEnforced by design; reruns are safe
ScaleDozens of entriesOne process, one machineExecutors spread across workers
⚙ Quick Reference
2 commands from this guide
FileCommand / CodePurpose
billing.cron0 2 1 * * root /opt/billing/run_monthly_billing.sh1. What a Workflow Engine Does That Cron Can't
dagsmonthly_billing.pyfrom datetime import datetime2. The DAG

Key takeaways

1
Cron has no memory; Airflow's metadata DB remembers every run.
2
A DAG is tasks plus edges, and cycles are rejected at parse time.
3
The four components talk only through the metadata database.
4
Idempotent tasks make retries, backfills, and catchup safe.
5
Use Airflow for graphs with dependencies; keep single commands in cron.

Common mistakes to avoid

4 patterns
×

Treating Airflow as cron with a fancier UI

Symptom
One giant task that does everything, and failures that say nothing
Fix
Split work into atomic tasks with explicit dependencies; the graph becomes the failure map.
×

Skipping idempotency design

Symptom
Backfills and retries create duplicate rows or double-send emails
Fix
Upsert by natural key or write-then-rename so every task is safe to re-run.
×

Ignoring the metadata DB

Symptom
Run history vanishes or the webserver 500s after a restart
Fix
Back it up, monitor it, and move from SQLite to Postgres before production.
×

Over-provisioning from day one

Symptom
A Kubernetes executor and 40 DAGs before the first DAG ever ran
Fix
Start with LocalExecutor, learn task states, then scale the architecture.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What's the difference between cron and Airflow?
Q02SENIOR
What are the four components of an Airflow deployment and how do they in...
Q03SENIOR
Why is idempotency the first law of Airflow pipelines, and how do you en...
Q01 of 03JUNIOR

What's the difference between cron and Airflow?

ANSWER
Cron fires commands on a schedule and forgets them — no state, no retries, no dependencies. Airflow tracks each execution as a DAG run in a metadata DB, supports dependencies between tasks, retries failures with backoff, and gives every run an audit trail.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Is Airflow a drop-in cron replacement?
02
What exactly is a DAG?
03
Can Airflow handle real-time or streaming workloads?
04
Where does Airflow store its state?
05
Is Airflow still relevant in 2026?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
August 01, 2026
last updated
1,750
articles · all by Naren
🔥

That's Airflow. Mark it forged?

3 min read · try the examples if you haven't

Previous
Jenkins Build Triggers
1 / 37 · Airflow
Next
Airflow Getting Started