Airflow Summit 2026 is coming August 31 - September 2 in Austin, TX. Register now to secure your spot!

Building the image

Before you dive-deeply in the way how the Airflow Image is built, let us first explain why you might need to build the custom container image and we show a few typical ways you can do it.

Quick start scenarios of image extending

The most common scenarios where you want to build your own image are adding a new apt package, adding a new PyPI dependency (either individually or via requirements.txt) and embedding dags into the image.

Example Dockerfiles for those scenarios are below, and you can read further for more complex cases which might involve either extending or customizing the image. You will find more information about more complex scenarios below, but if your goal is to quickly extend the Airflow image with new provider, package, etc. then here is a quick start for you.

Adding new apt package

The following example adds vim to the Airflow image. When adding packages via apt you should switch to the root user when running the apt commands, but do not forget to switch back to the airflow user after installation is complete.

/opt/airflow/docker-stack-docs/docker-examples/extending/add-apt-packages/Dockerfile

FROM apache/airflow:3.3.0
USER root
RUN apt-get update \
  && apt-get install -y --no-install-recommends \
         vim \
  && apt-get autoremove -yqq --purge \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*
USER airflow

Adding new PyPI packages individually

The following example adds lxml python package from PyPI to the image. When adding packages via pip you need to use the airflow user rather than root. Attempts to install pip packages as root will fail with an appropriate error message.

Note

In the example below, we also add apache-airflow package to be installed - in the very same version that the image version you used it from. This is not strictly necessary, but it is a good practice to always install the same version of apache-airflow as the one you are using. This way you can be sure that the version you are using is the same as the one you are extending. In some cases where your new packages have conflicting dependencies, pip might decide to downgrade or upgrade apache-airflow for you, so adding it explicitly is a good practice - this way if you have conflicting requirements, you will get an error message with conflict information, rather than a surprise downgrade or upgrade of airflow. If you upgrade Airflow base image, you should also update the version to match the new version of airflow.

Note

Creating custom images means that you need to maintain also a level of automation as you need to re-create the images when either the packages you want to install or Airflow is upgraded. Please do not forget about keeping these scripts. Also keep in mind, that in cases when you run pure Python tasks, you can use the Python Virtualenv functions which will dynamically source and install python dependencies during runtime. With Airflow 2.8.0 Virtualenvs can also be cached.

/opt/airflow/docker-stack-docs/docker-examples/extending/add-pypi-packages/Dockerfile

FROM apache/airflow:3.3.0
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" lxml

Adding packages from requirements.txt

The following example adds few python packages from requirements.txt from PyPI to the image. Note that similarly when adding individual packages, you need to use the airflow user rather than root. Attempts to install pip packages as root will fail with an appropriate error message.

Note

In the example below, we also add apache-airflow package to be installed - in the very same version that the image version you used it from. This is not strictly necessary, but it is a good practice to always install the same version of apache-airflow as the one you are using. This way you can be sure that the version you are using is the same as the one you are extending. In some cases where your new packages have conflicting dependencies, pip might decide to downgrade or upgrade apache-airflow for you, so adding it explicitly is a good practice - this way if you have conflicting requirements, you will get an error message with conflict information, rather than a surprise downgrade or upgrade of airflow. If you upgrade Airflow base image, you should also update the version to match the new version of airflow.

/opt/airflow/docker-stack-docs/docker-examples/extending/add-requirement-packages/Dockerfile

FROM apache/airflow:3.3.0
COPY requirements.txt /
RUN pip install --no-cache-dir "apache-airflow==${AIRFLOW_VERSION}" -r /requirements.txt

/opt/airflow/docker-stack-docs/docker-examples/extending/add-requirement-packages/requirements.txt

lxml
beautifulsoup4

Embedding dags

The following example adds test_dag.py to your image in the /opt/airflow/dags folder.

/opt/airflow/docker-stack-docs/docker-examples/extending/embedding-dags/Dockerfile

FROM apache/airflow:3.3.0

COPY --chown=airflow:root test_dag.py /opt/airflow/dags

docker-examples/extending/embedding-dags/test_dag.py

import datetime

import pendulum

from airflow.models.dag import DAG
from airflow.providers.standard.operators.empty import EmptyOperator

now = pendulum.now(tz="UTC")
now_to_the_hour = (now - datetime.timedelta(0, 0, 0, 0, 0, 3)).replace(minute=0, secon