| Feature | Value for the team |
|---|---|
| DAG as Python code | Git, PR, review, CI/CD. |
| Providers | Integrations with databases, clouds, and messaging systems. |
| UI and observability | Visibility into dependencies, statuses, and logs. |
| Extensibility | Custom hooks/operators for your platform. |
| Backfill | Reloading and recalculating historical windows. |
| Aspect | 2.x | 3.x |
|---|---|---|
| Task execution | Direct access to the database | Via Task SDK and API |
| Worker isolation | Limited | Explicit isolation |
| DAG versioning | Limited | DAG Versioning |
| Assets | No native model | Has data-aware scheduling |
from airflow.models.taskinstance import TaskInstance
# Anti-pattern for 3.x: directly reading internal models from task code
def bad_task(**ctx):
ti = TaskInstance.get_task_instance(
dag_id="demo",
task_id="step",
run_id=ctx["run_id"]
)
return ti.state
| Option | For whom | Pros | Limitations |
|---|---|---|---|
| Local Python (pip + LocalExecutor) |
Learning, quick experiments |
Up and running in minutes, minimal infrastructure |
Does not reflect real production |
| Docker Compose | Team dev environment, workshop, pre-prod |
Close to production topology, predictable |
Manual administration and upgrades |
| Kubernetes + Helm (self-managed) |
Mature platform/data team |
Scalability, flexibility, control |
High operational complexity |
| Managed Airflow (MWAA / Composer / Astronomer) |
Focus on the data product, not on ops |
Less routine, faster time-to-value |
Cost, provider limitations |
| Hybrid (control plane managed + compute self-hosted) |
Strict requirements for networking and security |
Balance of control and speed |
More complex architecture and governance |
| Platform | Advantage | Drawback |
|---|---|---|
| GCP Composer | Deep integration with GCP | Vendor lock-in |
| AWS MWAA | Managed service in AWS | Limited configuration flexibility |
| Astronomer | Strong DX and tooling | Additional cost |
curl -LfO 'https://airflow.apache.org/docs/apache-airflow/stable/docker-compose.yaml'
mkdir -p ./dags ./logs ./plugins ./config
# AIRFLOW_UID — for correct permissions on mounted folders
# FERNET_KEY — encrypts sensitive fields in the metadata DB
echo -e "AIRFLOW_UID=$(id -u)\nFERNET_KEY=$(openssl rand -base64 32 | tr '+/' '-_')" > .env
docker compose up airflow-init # one-time initialization
docker compose up
docker-compose.yaml.dags, logs, plugins..env file with AIRFLOW_UID and keys.docker compose up airflow-init.docker compose up -d.export AIRFLOW_UID=$(id -u)
export AIRFLOW_GID=0
export FERNET_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
docker compose up airflow-init
AIRFLOW_UID is needed for correct permissions on the volume.FERNET_KEY encrypts sensitive fields in the metadata DB.| Component | Purpose |
|---|---|
| Metadata DB | Stores the state of DAGs, DagRuns, TaskInstances, connections, variables, and execution history. |
| API Server | A single access point to Airflow state and operations for the UI, SDK, and internal services. |
| Scheduler | Schedules runs, evaluates dependencies, and decides which tasks to start next. |
| Worker | Runs task instances in the execution environment and sends results/statuses back. |
| Triggerer | Handles deferrable tasks and asynchronous waits without blocking worker slots. |
| Redis | Broker/queue for delivering tasks and control messages (for example, in a Celery topology). |
| DAG Processor | Parses DAG files, validates DAG code, and serializes definitions for the scheduler/UI. |
| Web UI | Visualization of DAGs and runs, viewing logs, and manual operations (trigger, clear, pause/unpause). |
| Symptom | Cause | Solution |
|---|---|---|
| UI is blank | init was skipped | Run airflow-init again |
| DAG is not visible | Import error / path issue | Check the scheduler and dag-processor logs |
| No runs | DAG paused | Unpause in the UI or CLI |
| Screen | When to open it | What to look for |
|---|---|---|
| Grid | Production incident | Problematic run, task status, and failure pattern by date |
| Graph | The chain breaks | Broken upstream, incorrect trigger rule, excess dependencies |
| Gantt | SLA degradation | Long tasks, executor queue, and time bottleneck |
task_id. An analogy to an object/instance of a step.queued, running, success, failed.| Airflow entity | What it is | Analogy |
|---|---|---|
| DAG | Graph of work and dependencies | Recipe/blueprint of the process |
| DagRun | One DAG run over time | One run of the recipe (for example, for a day) |
| Operator | Step type | Class in OOP |
| Task | A concrete step in the DAG | Object (instance of a class) |
| TaskInstance | Task in a specific run | State of the object over time |
from airflow.operators.bash import BashOperator
say_hello = BashOperator(
task_id="say_hello",
bash_command="echo Hello",
)
say_hello here is a Task created from the Operator template.| Operator | Package | Purpose |
|---|---|---|
| PythonOperator | core | Python functions |
| BashOperator | core | Shell commands |
| EmptyOperator | core | Helper graph nodes |
| TriggerDagRunOperator | core | Launch another DAG |
| PostgresOperator | postgres | SQL in Postgres |
from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime
with DAG(
dag_id="bash_dependencies_demo",
start_date=datetime(2026, 1, 1),
schedule="@daily",
catchup=False,
) as dag:
start = BashOperator(task_id="start", bash_command="date")
task_a = BashOperator(task_id="task_a", bash_command="echo 'Processing A'; sleep 2")
task_b = BashOperator(task_id="task_b", bash_command="echo 'Processing B'; sleep 3")
finish = BashOperator(task_id="finish", bash_command="echo 'Done'")
start >> [task_a, task_b] >> finish
start >> [task_a, task_b] >> finish
finish.start >> task_a >> task_b >> finish
task_b starts only after task_a succeeds.
DagRun, and tracks each task in that run as a separate TaskInstance.| Status | Meaning |
|---|---|
| queued | Waiting for an execution slot |
| running | Currently running on a worker |
| success | Completed successfully |
| failed | Completed with an error |
| skipped | Skipped by DAG logic |
| upstream_failed | Was not started due to upstream failure |
from airflow.providers.postgres.hooks.postgres import PostgresHook
def read_users():
hook = PostgresHook(postgres_conn_id="analytics_pg")
rows = hook.get_records("SELECT id, email FROM users LIMIT 100")
return rows
from airflow.providers.standard.sensors.filesystem import FileSensor
wait_for_file = FileSensor(
task_id="wait_for_drop",
filepath="/opt/airflow/data/input.csv",
poke_interval=30,
timeout=1800,
deferrable=True,
)
from airflow.models import Variable
@task
def send_report():
channel = Variable.get("alerts_channel", default_var="#data-alerts")
return channel
@task and passing it to another happens via XCom.@task
def extract():
return {"cnt": 42}
ti.xcom_push(key="cnt", value=42)
ti.xcom_pull(task_ids="extract", key="cnt")
@task
def extract():
return {"raw_value": 42}
@task
def transform(data: dict):
return data["raw_value"] * 2
transform(extract())
| Component | Role | What it does |
|---|---|---|
| DAG Processor | Parsing | Reads DAG files and prepares the graph for execution. |
| Scheduler | Scheduling | Decides when to run task instances and in what order. |
| Executor | Dispatcher | Routes tasks to the appropriate execution environment. |
| Worker | Executor | Actually executes the task code. |
| Metadata DB | State | Stores statuses, logs, metadata, and run history. |
| API Server | API | Handles web endpoints, the UI, and orchestration control. |
| Triggerer | Waiting | Supports deferrable tasks and asynchronous waits. |
| Role | Executor | Worker |
|---|---|---|
| Purpose | Task routing | Actual execution |
| Analogy | Taxi dispatcher | Driver |
| Where it lives | Orchestration service | Execution environment |
| Executor | Principle | Backend | When to use it |
|---|---|---|---|
| Sequential | One task at a time | None | Local experiments |
| Local | Parallel execution on a single server | None | Small installations |
| Celery | Task queue | Redis/RabbitMQ | Classic production |
| Kubernetes | Pod per task | K8s API | Cloud elasticity |