Data Engineering  /  Airflow

๐ŸŒฌ๏ธ Apache Airflow Guide 3 of 4 18 guides ยท updated 2026

Workflow orchestration with Airflow โ€” DAGs, operators, scheduling, executors, and the production practices that keep pipelines reliable.

Installing & Running Airflow with Docker Compose

The fastest reliable way to get a full Airflow environment running locally โ€” scheduler, webserver, metadata database, and a worker, all wired together โ€” is the official Docker Compose file the Airflow project publishes for every release. This avoids the dependency headaches of a pip install apache-airflow (which has notoriously strict, version-pinned requirements) for local development and evaluation.


Step 1: Fetch the Compose File

Terminal window
curl -LfO 'https://airflow.apache.org/docs/apache-airflow/2.9.0/docker-compose.yaml'

Always pin the version in the URL to match the Airflow release you intend to run โ€” pulling latest gives inconsistent behavior across machines and over time as new versions ship.


Step 2: Set Required Environment Variables

Terminal window
mkdir -p ./dags ./logs ./plugins ./config
echo -e "AIRFLOW_UID=$(id -u)" > .env

On Linux, AIRFLOW_UID matters because the containers write files (logs, in particular) as that UID on your hostโ€™s bind-mounted volumes โ€” skip this and youโ€™ll get permission-denied errors the first time a task tries to write a log file. macOS and Windows Docker Desktop users are less affected due to how their filesystem virtualization handles permissions, but setting it is still the documented default.


Step 3: Initialize the Database

Terminal window
docker compose up airflow-init

This one-time step runs database migrations against the metadata database and creates the default airflow/airflow admin user. It should complete and exit cleanly โ€” if it doesnโ€™t, the compose fileโ€™s depends_on health checks for Postgres havenโ€™t been given enough time to pass, which is the most common first-run failure.


Step 4: Start Everything

Terminal window
docker compose up -d

This brings up the webserver, scheduler, triggerer, and a Celery worker (the default Compose file uses CeleryExecutor plus Redis as the broker), all backed by a Postgres container. Give it 30โ€“60 seconds on first boot for every serviceโ€™s health check to pass.

Terminal window
docker compose ps

Every service should show healthy, not just running โ€” a container thatโ€™s running but failing its health check is still initializing (or stuck).


Step 5: Access the UI

Navigate to http://localhost:8080 and log in with airflow / airflow (the default credentials created by airflow-init โ€” change these before anything touches real data). You should see the example DAGs Airflow ships with, which are a good way to confirm the scheduler is actually picking things up before you write your own.


Adding Your Own DAGs

Any .py file placed in the ./dags directory you created in Step 2 is automatically picked up by the scheduler within its parse interval (a few minutes by default) โ€” no restart required.

./dags/hello_world.py
from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime
with DAG("hello_world", start_date=datetime(2026, 1, 1), schedule="@daily", catchup=False) as dag:
BashOperator(task_id="say_hello", bash_command="echo Hello from Airflow")

If it doesnโ€™t appear in the UI within a few minutes, check docker compose logs airflow-scheduler for parse errors โ€” a syntax error or a missing import in your DAG file will show up there, and often in the UIโ€™s โ€œDAG Import Errorsโ€ banner too.


Common Mistakes

Forgetting AIRFLOW_UID on Linux and hitting permission-denied errors on log writes โ€” set it before the first docker compose up, not after.

Using the default Compose setup as a production reference. Itโ€™s explicitly documented as intended for local development/testing โ€” production deployments need external, properly-backed-up Postgres, secrets management, and typically run on Kubernetes rather than plain Docker Compose (see Deploying Airflow on Kubernetes).

Not tearing down volumes between experiments. docker compose down alone leaves the Postgres volume intact (which is usually what you want), but if you need a truly clean slate โ€” e.g., after intentionally corrupting local state while testing โ€” use docker compose down --volumes.

Frequently Asked Questions

Can I run just the scheduler and skip Celery/Redis for simple local testing? Yes โ€” override the executor to LocalExecutor in a custom compose override file and drop the worker/redis services; this is lighter weight for pure DAG-authoring iteration.

How much memory does this need? The official docs recommend at least 4GB allocated to Docker, and note that lower memory settings can cause containers to be killed mid-startup โ€” a common cause of mysterious webserver crashes on first run.

Do I need to rebuild the image to add a Python package? Yes โ€” the default image doesnโ€™t include arbitrary PyPI packages; you extend it with a small Dockerfile (FROM apache/airflow:2.9.0 + RUN pip install ...) and point Compose at your built image instead of pulling the stock one.

Summary

The official Docker Compose file is the fastest path from zero to a working multi-component Airflow environment, and it mirrors the real architecture (scheduler, webserver, executor, metadata database) closely enough to be a legitimate learning environment โ€” not just a toy. Get AIRFLOW_UID right, wait for health checks, and treat it as a development tool rather than a production deployment pattern.