From f74278c9a9f47e93e545a8355137188833c624c3 Mon Sep 17 00:00:00 2001
From: Jarek Potiuk <jarek@potiuk.com>
Date: Sat, 20 Mar 2021 04:03:51 +0100
Subject: [PATCH] Much easier to use and better documented Docker image

Previously you had to specify AIRFLOW_VERSION_REFERENCE and
AIRFLOW_CONSTRAINTS_REFERENCE to point to the right version
of Airflow. Now those values are auto-detected if not specified
(but you can still override them)

This change allowed to simplify and restructure the Dockerfile
documentation - following the recent change in separating out
the docker-stack, production image building documentation has
been improved to reflect those simplifications. It should be
much easier to grasp by the novice users now - very clear
distinction and separation is made between the two types of
building your own images - customizing or extending - and it
is now much easier to follow examples and find out how to
build your own image. The criteria on which approach to
choose were put first and forefront.

Examples have been reviewed, fixed and put in a logical
sequence. From the most basic ones to the most advanced,
with clear indication where the basic aproach ends and where
the "power-user" one starts. The examples were also separated
out to separate files and included from there - also the
example Docker images and build commands are executable
and tested automatically in CI, so they are guaranteed
to work.

Finally The build arguments were split into sections - from most
basic to most advanced and each section links to appropriate
example section, showing how to use those parameters.

Fixes: #14848
Fixes: #14255
---
 .github/workflows/ci.yml                      |  24 +
 Dockerfile                                    |  59 +-
 Dockerfile.ci                                 |  15 +-
 IMAGES.rst                                    |   5 +-
 breeze                                        |   5 -
 docs/docker-stack/build-arg-ref.rst           | 267 ++++----
 docs/docker-stack/build.rst                   | 623 +++++++++++-------
 .../customizing/add-build-essential-custom.sh |  33 +
 .../customizing/custom-sources.sh             |  48 ++
 .../github-different-repository.sh            |  31 +
 .../customizing/github-master.sh              |  31 +
 .../customizing/github-v2-0-test.sh           |  31 +
 .../customizing/pypi-dev-runtime-deps.sh      |  34 +
 .../customizing/pypi-extras-and-deps.sh       |  32 +
 .../customizing/pypi-selected-version.sh      |  30 +
 .../customizing/stable-airflow.sh             |  28 +
 .../extending/add-apt-packages/Dockerfile     |  27 +
 .../add-build-essential-extend/Dockerfile     |  28 +
 .../extending/add-pypi-packages/Dockerfile    |  20 +
 .../extending/embedding-dags/Dockerfile       |  22 +
 .../extending/embedding-dags/test_dag.py      |  39 ++
 .../restricted/restricted_environments.sh     |  44 ++
 scripts/ci/images/ci_run_prod_image_test.sh   |  50 ++
 ...ci_test_examples_of_prod_image_building.sh |  91 +++
 scripts/ci/libraries/_build_images.sh         |   1 +
 .../ci/libraries/_docker_engine_resources.sh  |   9 +-
 scripts/ci/libraries/_initialization.sh       |   1 +
 scripts/ci/libraries/_parallel.sh             |  70 +-
 scripts/ci/testing/ci_run_airflow_testing.sh  |  59 +-
 scripts/docker/common.sh                      |  63 ++
 scripts/docker/compile_www_assets.sh          |   5 +-
 scripts/docker/install_airflow.sh             |  18 +-
 .../docker/install_airflow_from_branch_tip.sh |  13 +-
 .../install_from_docker_context_files.sh      |  26 +-
 34 files changed, 1368 insertions(+), 514 deletions(-)
 create mode 100755 docs/docker-stack/docker-examples/customizing/add-build-essential-custom.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/custom-sources.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/github-different-repository.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/github-master.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/github-v2-0-test.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/pypi-dev-runtime-deps.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/pypi-extras-and-deps.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/pypi-selected-version.sh
 create mode 100755 docs/docker-stack/docker-examples/customizing/stable-airflow.sh
 create mode 100644 docs/docker-stack/docker-examples/extending/add-apt-packages/Dockerfile
 create mode 100644 docs/docker-stack/docker-examples/extending/add-build-essential-extend/Dockerfile
 create mode 100644 docs/docker-stack/docker-examples/extending/add-pypi-packages/Dockerfile
 create mode 100644 docs/docker-stack/docker-examples/extending/embedding-dags/Dockerfile
 create mode 100644 docs/docker-stack/docker-examples/extending/embedding-dags/test_dag.py
 create mode 100755 docs/docker-stack/docker-examples/restricted/restricted_environments.sh
 create mode 100755 scripts/ci/images/ci_run_prod_image_test.sh
 create mode 100755 scripts/ci/images/ci_test_examples_of_prod_image_building.sh
 create mode 100755 scripts/docker/common.sh

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b47022d15022a..79eb7fb45828d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -216,6 +216,7 @@ jobs:
           fi
 
   test-openapi-client-generation:
+    timeout-minutes: 10
     name: "Test OpenAPI client generation"
     runs-on: ${{ fromJson(needs.build-info.outputs.runsOn) }}
     needs: [build-info]
@@ -229,6 +230,29 @@ jobs:
       - name: "Generate client codegen diff"
         run: ./scripts/ci/openapi/client_codegen_diff.sh
 
+  test-examples-of-prod-image-building:
+    timeout-minutes: 60
+    name: "Test examples of production image building"
+    runs-on: ${{ fromJson(needs.build-info.outputs.runsOn) }}
+    needs: [build-info]
+    if: needs.build-info.outputs.image-build == 'true'
+    steps:
+      - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 2
+          persist-credentials: false
+      - name: "Free space"
+        run: ./scripts/ci/tools/ci_free_space_on_ci.sh
+        if: |
+          needs.build-info.outputs.waitForImage == 'true'
+      - name: "Setup python"
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{needs.build-info.outputs.defaultPythonVersion}}
+      - name: "Test examples of PROD image building"
+        run: ./scripts/ci/images/ci_test_examples_of_prod_image_building.sh
+
   ci-images:
     timeout-minutes: 120
     name: "Wait for CI images"
diff --git a/Dockerfile b/Dockerfile
index 482e2254376ab..5f8e666b39726 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -33,7 +33,7 @@
 #                        all the build essentials. This makes the image
 #                        much smaller.
 #
-ARG AIRFLOW_VERSION="2.0.0.dev0"
+ARG AIRFLOW_VERSION="2.0.1"
 ARG AIRFLOW_EXTRAS="async,amazon,celery,cncf.kubernetes,docker,dask,elasticsearch,ftp,grpc,hashicorp,http,ldap,google,microsoft.azure,mysql,postgres,redis,sendgrid,sftp,slack,ssh,statsd,virtualenv"
 ARG ADDITIONAL_AIRFLOW_EXTRAS=""
 ARG ADDITIONAL_PYTHON_DEPS=""
@@ -45,7 +45,6 @@ ARG AIRFLOW_GID="50000"
 ARG CASS_DRIVER_BUILD_CONCURRENCY="8"
 
 ARG PYTHON_BASE_IMAGE="python:3.6-slim-buster"
-ARG PYTHON_MAJOR_MINOR_VERSION="3.6"
 
 ARG AIRFLOW_PIP_VERSION=20.2.4
 
@@ -61,9 +60,6 @@ SHELL ["/bin/bash", "-o", "pipefail", "-e", "-u", "-x", "-c"]
 ARG PYTHON_BASE_IMAGE
 ENV PYTHON_BASE_IMAGE=${PYTHON_BASE_IMAGE}
 
-ARG PYTHON_MAJOR_MINOR_VERSION
-ENV PYTHON_MAJOR_MINOR_VERSION=${PYTHON_MAJOR_MINOR_VERSION}
-
 # Make sure noninteractive debian install is used and language variables set
 ENV DEBIAN_FRONTEND=noninteractive LANGUAGE=C.UTF-8 LANG=C.UTF-8 LC_ALL=C.UTF-8 \
     LC_CTYPE=C.UTF-8 LC_MESSAGES=C.UTF-8
@@ -165,11 +161,16 @@ ENV AIRFLOW_EXTRAS=${AIRFLOW_EXTRAS}${ADDITIONAL_AIRFLOW_EXTRAS:+,}${ADDITIONAL_
 ARG CONSTRAINTS_GITHUB_REPOSITORY="apache/airflow"
 ENV CONSTRAINTS_GITHUB_REPOSITORY=${CONSTRAINTS_GITHUB_REPOSITORY}
 
-ARG AIRFLOW_CONSTRAINTS_REFERENCE="constraints-master"
 ARG AIRFLOW_CONSTRAINTS="constraints"
-ARG AIRFLOW_CONSTRAINTS_LOCATION="https://raw.githubusercontent.com/${CONSTRAINTS_GITHUB_REPOSITORY}/${AIRFLOW_CONSTRAINTS_REFERENCE}/${AIRFLOW_CONSTRAINTS}-${PYTHON_MAJOR_MINOR_VERSION}.txt"
+ENV AIRFLOW_CONSTRAINTS=${AIRFLOW_CONSTRAINTS}
+ARG AIRFLOW_CONSTRAINTS_REFERENCE=""
+ENV AIRFLOW_CONSTRAINTS_REFERENCE=${AIRFLOW_CONSTRAINTS_REFERENCE}
+ARG AIRFLOW_CONSTRAINTS_LOCATION=""
 ENV AIRFLOW_CONSTRAINTS_LOCATION=${AIRFLOW_CONSTRAINTS_LOCATION}
 
+ARG DEFAULT_CONSTRAINTS_BRANCH="constraints-master"
+ENV DEFAULT_CONSTRAINTS_BRANCH=${DEFAULT_CONSTRAINTS_BRANCH}
+
 ENV PATH=${PATH}:/root/.local/bin
 RUN mkdir -p /root/.local/bin
 
@@ -203,6 +204,26 @@ ENV AIRFLOW_PRE_CACHED_PIP_PACKAGES=${AIRFLOW_PRE_CACHED_PIP_PACKAGES}
 ARG INSTALL_PROVIDERS_FROM_SOURCES="false"
 ENV INSTALL_PROVIDERS_FROM_SOURCES=${INSTALL_PROVIDERS_FROM_SOURCES}
 
+# This is airflow version that is put in the label of the image build
+ARG AIRFLOW_VERSION
+ENV AIRFLOW_VERSION=${AIRFLOW_VERSION}
+
+# Determines the way airflow is installed. By default we install airflow from PyPI `apache-airflow` package
+# But it also can be `.` from local installation or GitHub URL pointing to specific branch or tag
+# Of Airflow. Note That for local source installation you need to have local sources of
+# Airflow checked out together with the Dockerfile and AIRFLOW_SOURCES_FROM and AIRFLOW_SOURCES_TO
+# set to "." and "/opt/airflow" respectively.
+ARG AIRFLOW_INSTALLATION_METHOD="apache-airflow"
+ENV AIRFLOW_INSTALLATION_METHOD=${AIRFLOW_INSTALLATION_METHOD}
+
+# By default latest released version of airflow is installed (when empty) but this value can be overridden
+# and we can install version according to specification (For example ==2.0.2 or <3.0.0).
+ARG AIRFLOW_VERSION_SPECIFICATION=""
+ENV AIRFLOW_VERSION_SPECIFICATION=${AIRFLOW_VERSION_SPECIFICATION}
+
+# Only copy common.sh to not invalidate cache on other script changes
+COPY scripts/docker/common.sh /scripts/docker/common.sh
+
 # Only copy install_airflow_from_branch_tip.sh to not invalidate cache on other script changes
 COPY scripts/docker/install_airflow_from_branch_tip.sh /scripts/docker/install_airflow_from_branch_tip.sh
 
@@ -235,27 +256,10 @@ COPY ${AIRFLOW_SOURCES_FROM} ${AIRFLOW_SOURCES_TO}
 ARG CASS_DRIVER_BUILD_CONCURRENCY
 ENV CASS_DRIVER_BUILD_CONCURRENCY=${CASS_DRIVER_BUILD_CONCURRENCY}
 
-# This is airflow version that is put in the label of the image build
-ARG AIRFLOW_VERSION
-ENV AIRFLOW_VERSION=${AIRFLOW_VERSION}
-
 # Add extra python dependencies
 ARG ADDITIONAL_PYTHON_DEPS=""
 ENV ADDITIONAL_PYTHON_DEPS=${ADDITIONAL_PYTHON_DEPS}
 
-# Determines the way airflow is installed. By default we install airflow from PyPI `apache-airflow` package
-# But it also can be `.` from local installation or GitHub URL pointing to specific branch or tag
-# Of Airflow. Note That for local source installation you need to have local sources of
-# Airflow checked out together with the Dockerfile and AIRFLOW_SOURCES_FROM and AIRFLOW_SOURCES_TO
-# set to "." and "/opt/airflow" respectively.
-ARG AIRFLOW_INSTALLATION_METHOD="apache-airflow"
-ENV AIRFLOW_INSTALLATION_METHOD=${AIRFLOW_INSTALLATION_METHOD}
-
-# By default latest released version of airflow is installed (when empty) but this value can be overridden
-# and we can install version according to specification (For example ==2.0.2 or <3.0.0).
-ARG AIRFLOW_VERSION_SPECIFICATION=""
-ENV AIRFLOW_VERSION_SPECIFICATION=${AIRFLOW_VERSION_SPECIFICATION}
-
 # We can set this value to true in case we want to install .whl .tar.gz packages placed in the
 # docker-context-files folder. This can be done for both - additional packages you want to install
 # and for airflow as well (you have to set INSTALL_FROM_PYPI to false in this case)
@@ -282,11 +286,10 @@ ARG CONTINUE_ON_PIP_CHECK_FAILURE="false"
 COPY scripts/docker/install*.sh /scripts/docker/
 
 # hadolint ignore=SC2086, SC2010
-RUN if [[ ${INSTALL_FROM_PYPI} == "true" ]]; then \
-        bash /scripts/docker/install_airflow.sh; \
-    fi; \
-    if [[ ${INSTALL_FROM_DOCKER_CONTEXT_FILES} == "true" ]]; then \
+RUN if [[ ${INSTALL_FROM_DOCKER_CONTEXT_FILES} == "true" ]]; then \
         bash /scripts/docker/install_from_docker_context_files.sh; \
+    elif [[ ${INSTALL_FROM_PYPI} == "true" ]]; then \
+        bash /scripts/docker/install_airflow.sh; \
     fi; \
     if [[ -n "${ADDITIONAL_PYTHON_DEPS}" ]]; then \
         bash /scripts/docker/install_additional_dependencies.sh; \
diff --git a/Dockerfile.ci b/Dockerfile.ci
index 24248e5c6939a..02a43254e1bac 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -26,9 +26,6 @@ ENV PYTHON_BASE_IMAGE=${PYTHON_BASE_IMAGE}
 ARG AIRFLOW_VERSION="2.0.0.dev0"
 ENV AIRFLOW_VERSION=$AIRFLOW_VERSION
 
-ARG PYTHON_MAJOR_MINOR_VERSION="3.6"
-ENV PYTHON_MAJOR_MINOR_VERSION=${PYTHON_MAJOR_MINOR_VERSION}
-
 # Print versions
 RUN echo "Base image: ${PYTHON_BASE_IMAGE}"
 RUN echo "Airflow version: ${AIRFLOW_VERSION}"
@@ -241,11 +238,16 @@ RUN echo "Installing with extras: ${AIRFLOW_EXTRAS}."
 ARG CONSTRAINTS_GITHUB_REPOSITORY="apache/airflow"
 ENV CONSTRAINTS_GITHUB_REPOSITORY=${CONSTRAINTS_GITHUB_REPOSITORY}
 
-ARG AIRFLOW_CONSTRAINTS_REFERENCE="constraints-${AIRFLOW_BRANCH}"
 ARG AIRFLOW_CONSTRAINTS="constraints"
-ARG AIRFLOW_CONSTRAINTS_LOCATION="https://raw.githubusercontent.com/${CONSTRAINTS_GITHUB_REPOSITORY}/${AIRFLOW_CONSTRAINTS_REFERENCE}/${AIRFLOW_CONSTRAINTS}-${PYTHON_MAJOR_MINOR_VERSION}.txt"
+ENV AIRFLOW_CONSTRAINTS=${AIRFLOW_CONSTRAINTS}
+ARG AIRFLOW_CONSTRAINTS_REFERENCE=""
+ENV AIRFLOW_CONSTRAINTS_REFERENCE=${AIRFLOW_CONSTRAINTS_REFERENCE}
+ARG AIRFLOW_CONSTRAINTS_LOCATION=""
 ENV AIRFLOW_CONSTRAINTS_LOCATION=${AIRFLOW_CONSTRAINTS_LOCATION}
 
+ARG DEFAULT_CONSTRAINTS_BRANCH="constraints-master"
+ENV DEFAULT_CONSTRAINTS_BRANCH=${DEFAULT_CONSTRAINTS_BRANCH}
+
 # By changing the CI build epoch we can force reinstalling Airflow and pip all dependencies
 # It can also be overwritten manually by setting the AIRFLOW_CI_BUILD_EPOCH environment variable.
 ARG AIRFLOW_CI_BUILD_EPOCH="3"
@@ -292,6 +294,9 @@ ENV PIP_PROGRESS_BAR=${PIP_PROGRESS_BAR}
 
 RUN pip install --no-cache-dir --upgrade "pip==${AIRFLOW_PIP_VERSION}"
 
+# Only copy common.sh to not invalidate further layers
+COPY scripts/docker/common.sh /scripts/docker/common.sh
+
 # Only copy install_airflow_from_branch_tip.sh to not invalidate cache on other script changes
 COPY scripts/docker/install_airflow_from_branch_tip.sh /scripts/docker/install_airflow_from_branch_tip.sh
 
diff --git a/IMAGES.rst b/IMAGES.rst
index 71c5d25827d17..c00067fa067e1 100644
--- a/IMAGES.rst
+++ b/IMAGES.rst
@@ -454,7 +454,6 @@ additional apt dev and runtime dependencies.
     --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
     --build-arg AIRFLOW_VERSION="2.0.0" \
     --build-arg AIRFLOW_VERSION_SPECIFICATION="==2.0.0" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-0" \
     --build-arg AIRFLOW_SOURCES_FROM="empty" \
     --build-arg AIRFLOW_SOURCES_TO="/empty" \
     --build-arg ADDITIONAL_AIRFLOW_EXTRAS="jdbc"
@@ -489,7 +488,6 @@ based on example in `this comment <https://github.com/apache/airflow/issues/8605
     --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
     --build-arg AIRFLOW_VERSION="2.0.0" \
     --build-arg AIRFLOW_VERSION_SPECIFICATION="==2.0.0" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-0" \
     --build-arg AIRFLOW_SOURCES_FROM="empty" \
     --build-arg AIRFLOW_SOURCES_TO="/empty" \
     --build-arg ADDITIONAL_AIRFLOW_EXTRAS="slack" \
@@ -567,7 +565,7 @@ The following build arguments (``--build-arg`` in docker build command) can be u
 |                                          |                                          | set to true. Default location from       |
 |                                          |                                          | GitHub is used in this case.             |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_CONSTRAINTS_REFERENCE``        | ``constraints-master``                   | reference (branch or tag) from GitHub    |
+| ``AIRFLOW_CONSTRAINTS_REFERENCE``        |                                          | reference (branch or tag) from GitHub    |
 |                                          |                                          | repository from which constraints are    |
 |                                          |                                          | used. By default it is set to            |
 |                                          |                                          | ``constraints-master`` but can be        |
@@ -575,6 +573,7 @@ The following build arguments (``--build-arg`` in docker build command) can be u
 |                                          |                                          | ``constraints-1-10`` for 1.10.* versions |
 |                                          |                                          | or it could point to specific version    |
 |                                          |                                          | for example ``constraints-2.0.0``        |
+|                                          |                                          | is empty, it is auto-detected            |
 +------------------------------------------+------------------------------------------+------------------------------------------+
 | ``INSTALL_PROVIDERS_FROM_SOURCES``       | ``true``                                 | If set to false and image is built from  |
 |                                          |                                          | sources, all provider packages are not   |
diff --git a/breeze b/breeze
index 94a8b86b27731..4df4937ec3b45 100755
--- a/breeze
+++ b/breeze
@@ -881,11 +881,6 @@ function breeze::parse_arguments() {
             INSTALL_AIRFLOW_VERSION="${2}"
             # Reference is mutually exclusive with version
             INSTALL_AIRFLOW_REFERENCE=""
-            # Skip mounting local sources when airflow is installed from remote
-            if [[ ${INSTALL_AIRFLOW_VERSION} =~ ^[0-9\.]*$ ]]; then
-                echo "Install providers from PyPI"
-                INSTALL_PROVIDERS_FROM_SOURCES="false"
-            fi
             echo "Installs version of Airflow: ${INSTALL_AIRFLOW_VERSION}"
             echo
             shift 2
diff --git a/docs/docker-stack/build-arg-ref.rst b/docs/docker-stack/build-arg-ref.rst
index 57d4da5639644..2ec04c87ebf52 100644
--- a/docs/docker-stack/build-arg-ref.rst
+++ b/docs/docker-stack/build-arg-ref.rst
@@ -18,99 +18,77 @@
 Image build arguments reference
 -------------------------------
 
-The following build arguments (``--build-arg`` in docker build command) can be used for production images:
+The following build arguments (``--build-arg`` in docker build command) can be used for production images.
+Those arguments are used when you want to customize the image. You can see some examples of it in
+:ref:`Building from PyPI packages<image-build-pypi>`.
+
+Basic arguments
+...............
+
+Those are the most common arguments that you use when you want to build a custom image.
 
 +------------------------------------------+------------------------------------------+------------------------------------------+
 | Build argument                           | Default value                            | Description                              |
 +==========================================+==========================================+==========================================+
 | ``PYTHON_BASE_IMAGE``                    | ``python:3.6-slim-buster``               | Base python image.                       |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``PYTHON_MAJOR_MINOR_VERSION``           | ``3.6``                                  | major/minor version of Python (should    |
-|                                          |                                          | match base image).                       |
-+------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_VERSION``                      | ``2.0.1.dev0``                           | version of Airflow.                      |
+| ``AIRFLOW_VERSION``                      | ``2.0.1``                                | version of Airflow.                      |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_REPO``                         | ``apache/airflow``                       | the repository from which PIP            |
-|                                          |                                          | dependencies are pre-installed.          |
+| ``AIRFLOW_EXTRAS``                       | (see Dockerfile)                         | Default extras with which airflow is     |
+|                                          |                                          | installed.                               |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_BRANCH``                       | ``master``                               | the branch from which PIP dependencies   |
-|                                          |                                          | are pre-installed initially.             |
+| ``ADDITIONAL_AIRFLOW_EXTRAS``            |                                          | Optional additional extras with which    |
+|                                          |                                          | airflow is installed.                    |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_CONSTRAINTS_LOCATION``         |                                          | If not empty, it will override the       |
-|                                          |                                          | source of the constraints with the       |
-|                                          |                                          | specified URL or file. Note that the     |
-|                                          |                                          | file has to be in docker context so      |
-|                                          |                                          | it's best to place such file in          |
-|                                          |                                          | one of the folders included in           |
-|                                          |                                          | ``.dockerignore`` file.                  |
+| ``AIRFLOW_HOME``                         | ``/opt/airflow``                         | Airflow’s HOME (that’s where logs and    |
+|                                          |                                          | SQLite databases are stored).            |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_CONSTRAINTS_REFERENCE``        | ``constraints-master``                   | Reference (branch or tag) from GitHub    |
-|                                          |                                          | where constraints file is taken from     |
-|                                          |                                          | It can be ``constraints-master`` but     |
-|                                          |                                          | also can be ``constraints-1-10`` for     |
-|                                          |                                          | 1.10.* installation. In case of building |
-|                                          |                                          | specific version you want to point it    |
-|                                          |                                          | to specific tag, for example             |
-|                                          |                                          | ``constraints-1.10.15``.                 |
+| ``AIRFLOW_USER_HOME_DIR``                | ``/home/airflow``                        | Home directory of the Airflow user.      |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``INSTALL_PROVIDERS_FROM_SOURCES``       | ``false``                                | If set to ``true`` and image is built    |
-|                                          |                                          | from sources, all provider packages are  |
-|                                          |                                          | installed from sources rather than from  |
-|                                          |                                          | packages. It has no effect when          |
-|                                          |                                          | installing from PyPI or GitHub repo.     |
+| ``AIRFLOW_PIP_VERSION``                  | ``20.2.4``                               | PIP version used.                        |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_EXTRAS``                       | (see Dockerfile)                         | Default extras with which airflow is     |
-|                                          |                                          | installed.                               |
+| ``PIP_PROGRESS_BAR``                     | ``on``                                   | Progress bar for PIP installation        |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``INSTALL_FROM_PYPI``                    | ``true``                                 | If set to true, Airflow is installed     |
-|                                          |                                          | from PyPI. if you want to install        |
-|                                          |                                          | Airflow from self-build package          |
-|                                          |                                          | you can set it to false, put package in  |
-|                                          |                                          | ``docker-context-files`` and set         |
-|                                          |                                          | ``INSTALL_FROM_DOCKER_CONTEXT_FILES`` to |
-|                                          |                                          | ``true``. For this you have to also keep |
-|                                          |                                          | ``AIRFLOW_PRE_CACHED_PIP_PACKAGES`` flag |
-|                                          |                                          | set to ``false``.                        |
+| ``AIRFLOW_UID``                          | ``50000``                                | Airflow user UID.                        |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_PRE_CACHED_PIP_PACKAGES``      | ``false``                                | Allows to pre-cache airflow PIP packages |
-|                                          |                                          | from the GitHub of Apache Airflow        |
-|                                          |                                          | This allows to optimize iterations for   |
-|                                          |                                          | Image builds and speeds up CI builds.    |
+| ``AIRFLOW_GID``                          | ``50000``                                | Airflow group GID. Note that most files  |
+|                                          |                                          | created on behalf of airflow user belong |
+|                                          |                                          | to the ``root`` group (0) to keep        |
+|                                          |                                          | OpenShift Guidelines compatibility.      |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``INSTALL_FROM_DOCKER_CONTEXT_FILES``    | ``false``                                | If set to true, Airflow, providers and   |
-|                                          |                                          | all dependencies are installed from      |
-|                                          |                                          | from locally built/downloaded            |
-|                                          |                                          | .whl and .tar.gz files placed in the     |
-|                                          |                                          | ``docker-context-files``. In certain     |
-|                                          |                                          | corporate environments, this is required |
-|                                          |                                          | to install airflow from such pre-vetted  |
-|                                          |                                          | packages rather than from PyPI. For this |
-|                                          |                                          | to work, also set ``INSTALL_FROM_PYPI``. |
-|                                          |                                          | Note that packages starting with         |
-|                                          |                                          | ``apache?airflow`` glob are treated      |
-|                                          |                                          | differently than other packages. All     |
-|                                          |                                          | ``apache?airflow`` packages are          |
-|                                          |                                          | installed with dependencies limited by   |
-|                                          |                                          | airflow constraints. All other packages  |
-|                                          |                                          | are installed without dependencies       |
-|                                          |                                          | 'as-is'. If you wish to install airflow  |
-|                                          |                                          | via 'pip download' with all dependencies |
-|                                          |                                          | downloaded, you have to rename the       |
-|                                          |                                          | apache airflow and provider packages to  |
-|                                          |                                          | not start with ``apache?airflow`` glob.  |
+| ``AIRFLOW_CONSTRAINTS_REFERENCE``        |                                          | Reference (branch or tag) from GitHub    |
+|                                          |                                          | where constraints file is taken from     |
+|                                          |                                          | It can be ``constraints-master`` but     |
+|                                          |                                          | can be ``constraints-1-10`` for 1.10.*   |
+|                                          |                                          | versions of ``constraints-2-0`` for      |
+|                                          |                                          | 2.0.* installation. In case of building  |
+|                                          |                                          | specific version you want to point it    |
+|                                          |                                          | to specific tag, for example             |
+|                                          |                                          | ``constraints-2.0.1``.                   |
+|                                          |                                          | Auto-detected if empty.                  |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``UPGRADE_TO_NEWER_DEPENDENCIES``        | ``false``                                | If set to true, the dependencies are     |
-|                                          |                                          | upgraded to newer versions matching      |
-|                                          |                                          | setup.py before installation.            |
+
+Image optimization options
+..........................
+
+The main advantage of Customization method of building Airflow image, is that it allows to build highly optimized image because
+the final image (RUNTIME) might not contain all the dependencies that are needed to build and install all other dependencies
+(DEV). Those arguments allow to control what is installed in the DEV image and what is installed in RUNTIME one, thus
+allowing to produce much more optimized images. See :ref:`Building optimized images<image-build-optimized>`.
+for examples of using those arguments.
+
 +------------------------------------------+------------------------------------------+------------------------------------------+
+| Build argument                           | Default value                            | Description                              |
++==========================================+==========================================+==========================================+
 | ``CONTINUE_ON_PIP_CHECK_FAILURE``        | ``false``                                | By default the image build fails if pip  |
 |                                          |                                          | check fails for it. This is good for     |
 |                                          |                                          | interactive building but on CI the       |
 |                                          |                                          | image should be built regardless - we    |
 |                                          |                                          | have a separate step to verify image.    |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``ADDITIONAL_AIRFLOW_EXTRAS``            |                                          | Optional additional extras with which    |
-|                                          |                                          | airflow is installed.                    |
+| ``UPGRADE_TO_NEWER_DEPENDENCIES``        | ``false``                                | If set to true, the dependencies are     |
+|                                          |                                          | upgraded to newer versions matching      |
+|                                          |                                          | setup.py before installation.            |
 +------------------------------------------+------------------------------------------+------------------------------------------+
 | ``ADDITIONAL_PYTHON_DEPS``               |                                          | Optional python packages to extend       |
 |                                          |                                          | the image with some extra dependencies.  |
@@ -149,18 +127,6 @@ The following build arguments (``--build-arg`` in docker build command) can be u
 | ``ADDITIONAL_RUNTIME_APT_ENV``           |                                          | Additional env variables defined         |
 |                                          |                                          | when installing runtime deps.            |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_HOME``                         | ``/opt/airflow``                         | Airflow’s HOME (that’s where logs and    |
-|                                          |                                          | SQLite databases are stored).            |
-+------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_UID``                          | ``50000``                                | Airflow user UID.                        |
-+------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_GID``                          | ``50000``                                | Airflow group GID. Note that most files  |
-|                                          |                                          | created on behalf of airflow user belong |
-|                                          |                                          | to the ``root`` group (0) to keep        |
-|                                          |                                          | OpenShift Guidelines compatibility.      |
-+------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_USER_HOME_DIR``                | ``/home/airflow``                        | Home directory of the Airflow user.      |
-+------------------------------------------+------------------------------------------+------------------------------------------+
 | ``CASS_DRIVER_BUILD_CONCURRENCY``        | ``8``                                    | Number of processors to use for          |
 |                                          |                                          | cassandra PIP install (speeds up         |
 |                                          |                                          | installing in case cassandra extra is    |
@@ -170,43 +136,106 @@ The following build arguments (``--build-arg`` in docker build command) can be u
 |                                          |                                          | The mysql extra is removed from extras   |
 |                                          |                                          | if the client is not installed.          |
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``AIRFLOW_PIP_VERSION``                  | ``20.2.4``                               | PIP version used.                        |
+
+Installing Airflow using different methods
+..........................................
+
+Those parameters are useful only if you want to install Airflow using different installation methods than the default
+(installing from PyPI packages).
+
+This is usually only useful if you have your own fork of Airflow and want to build the images locally from
+those sources - either locally or directly from GitHub sources. This way you do not need to release your
+Airflow and Providers via PyPI - they can be installed directly from sources or from GitHub repository.
+Another option of installation is to build Airflow from previously prepared binary Python packages which might
+be useful if you need to build Airflow in environments that require high levels of security.
+
+You can see some examples of those in:
+  * :ref:`Building from GitHub<image-build-github>`,
+  * :ref:`Using custom installation sources<image-build-custom>`,
+  * :ref:`Build images in security restricted environments<image-build-secure-environments>`
+
 +------------------------------------------+------------------------------------------+------------------------------------------+
-| ``PIP_PROGRESS_BAR``                     | ``on``                                   | Progress bar for PIP installation        |
+| Build argument                           | Default value                            | Description                              |
++==========================================+==========================================+==========================================+
+| ``AIRFLOW_INSTALLATION_METHOD``          | ``apache-airflow``                       | Installation method of Apache Airflow.   |
+|                                          |                                          | ``apache-airflow`` for installation from |
+|                                          |                                          | PyPI. It can be GitHub repository URL    |
+|                                          |                                          | including branch or tag to install from  |
+|                                          |                                          | that repository or "." to install from   |
+|                                          |                                          | local sources. Installing from sources   |
+|                                          |                                          | requires appropriate values of the       |
+|                                          |                                          | ``AIRFLOW_SOURCES_FROM`` and             |
+|                                          |                                          | ``AIRFLOW_SOURCES_TO`` variables (see    |
+|                                          |                                          | below)                                   |
++------------------------------------------+------------------------------------------+------------------------------------------+
+| ``AIRFLOW_SOURCES_FROM``                 | ``empty``                                | Sources of Airflow. Set it to "." when   |
+|                                          |                                          | you install Airflow from local sources   |
++------------------------------------------+------------------------------------------+------------------------------------------+
+| ``AIRFLOW_SOURCES_TO``                   | ``/empty``                               | Target for Airflow sources. Set to       |
+|                                          |                                          | "/opt/airflow" when you install Airflow  |
+|                                          |                                          | from local sources.                      |
++------------------------------------------+------------------------------------------+------------------------------------------+
+| ``AIRFLOW_VERSION_SPECIFICATION``        |                                          | Optional - might be used for using limit |
+|                                          |                                          | for Airflow version installation - for   |
+|                                          |                                          | example ``<2.0.2`` for automated builds. |
++------------------------------------------+------------------------------------------+------------------------------------------+
+| ``INSTALL_PROVIDERS_FROM_SOURCES``       | ``false``                                | If set to ``true`` and image is built    |
+|                                          |                                          | from sources, all provider packages are  |
+|                                          |                                          | installed from sources rather than from  |
+|                                          |                                          | packages. It has no effect when          |
+|                                          |                                          | installing from PyPI or GitHub repo.     |
++------------------------------------------+------------------------------------------+------------------------------------------+
+| ``AIRFLOW_CONSTRAINTS_LOCATION``         |                                          | If not empty, it will override the       |
+|                                          |                                          | source of the constraints with the       |
+|                                          |                                          | specified URL or file. Note that the     |
+|                                          |                                          | file has to be in docker context so      |
+|                                          |                                          | it's best to place such file in          |
+|                                          |                                          | one of the folders included in           |
+|                                          |                                          | ``.dockerignore`` file.                  |
 +------------------------------------------+------------------------------------------+------------------------------------------+
+| ``INSTALL_FROM_DOCKER_CONTEXT_FILES``    | ``false``                                | If set to true, Airflow, providers and   |
+|                                          |                                          | all dependencies are installed from      |
+|                                          |                                          | from locally built/downloaded            |
+|                                          |                                          | .whl and .tar.gz files placed in the     |
+|                                          |                                          | ``docker-context-files``. In certain     |
+|                                          |                                          | corporate environments, this is required |
+|                                          |                                          | to install airflow from such pre-vetted  |
+|                                          |                                          | packages rather than from PyPI. For this |
+|                                          |                                          | to work, also set ``INSTALL_FROM_PYPI``. |
+|                                          |                                          | Note that packages starting with         |
+|                                          |                                          | ``apache?airflow`` glob are treated      |
+|                                          |                                          | differently than other packages. All     |
+|                                          |                                          | ``apache?airflow`` packages are          |
+|                                          |                                          | installed with dependencies limited by   |
+|                                          |                                          | airflow constraints. All other packages  |
+|                                          |                                          | are installed without dependencies       |
+|                                          |                                          | 'as-is'. If you wish to install airflow  |
+|                                          |                                          | via 'pip download' with all dependencies |
+|                                          |                                          | downloaded, you have to rename the       |
+|                                          |                                          | apache airflow and provider packages to  |
+|                                          |                                          | not start with ``apache?airflow`` glob.  |
++------------------------------------------+------------------------------------------+------------------------------------------+
+
+Pre-caching PIP dependencies
+............................
+
+When image is build from PIP, by default pre-caching of PIP dependencies is used. This is in order to speed-up incremental
+builds during development. When pre-cached PIP dependencies are used and ``setup.py`` or ``setup.cfg`` changes, the
+PIP dependencies are already pre-installed, thus resulting in much faster image rebuild. This is purely an optimization
+of time needed to build the images and should be disabled if you want to install Airflow from
+docker context files.
 
-There are build arguments that determine the installation mechanism of Apache Airflow for the
-production image. There are three types of build:
-
-* From local sources (by default for example when you use ``docker build .``)
-* You can build the image from released PyPI airflow package (used to build the official Docker image)
-* You can build the image from any version in GitHub repository(this is used mostly for system testing).
-
-+-----------------------------------+------------------------+-----------------------------------------------------------------------------------+
-| Build argument                    | Default                | What to specify                                                                   |
-+===================================+========================+===================================================================================+
-| ``AIRFLOW_INSTALLATION_METHOD``   | ``apache-airflow``     | Should point to the installation method of Apache Airflow. It can be              |
-|                                   |                        | ``apache-airflow`` for installation from packages and URL to installation from    |
-|                                   |                        | GitHub repository tag or branch or "." to install from sources.                   |
-|                                   |                        | Note that installing from local sources requires appropriate values of the        |
-|                                   |                        | ``AIRFLOW_SOURCES_FROM`` and ``AIRFLOW_SOURCES_TO`` variables as described below. |
-|                                   |                        | Only used when ``INSTALL_FROM_PYPI`` is set to ``true``.                          |
-+-----------------------------------+------------------------+-----------------------------------------------------------------------------------+
-| ``AIRFLOW_VERSION_SPECIFICATION`` |                        | Optional - might be used for package installation of different Airflow version    |
-|                                   |                        | for example"==2.0.1". For consistency, you should also set``AIRFLOW_VERSION``     |
-|                                   |                        | to the same value AIRFLOW_VERSION is resolved as label in the image created.      |
-+-----------------------------------+------------------------+-----------------------------------------------------------------------------------+
-| ``AIRFLOW_CONSTRAINTS_REFERENCE`` | ``constraints-master`` | Reference (branch or tag) from GitHub where constraints file is taken from.       |
-|                                   |                        | It can be ``constraints-master`` but also can be``constraints-1-10`` for          |
-|                                   |                        | 1.10.*  installations. In case of building specific version                       |
-|                                   |                        | you want to point it to specific tag, for example ``constraints-2.0.1``           |
-+-----------------------------------+------------------------+-----------------------------------------------------------------------------------+
-| ``AIRFLOW_WWW``                   | ``www``                | In case of Airflow 2.0 it should be "www", in case of Airflow 1.10                |
-|                                   |                        | series it should be "www_rbac".                                                   |
-+-----------------------------------+------------------------+-----------------------------------------------------------------------------------+
-| ``AIRFLOW_SOURCES_FROM``          | ``empty``              | Sources of Airflow. Set it to "." when you install airflow from                   |
-|                                   |                        | local sources.                                                                    |
-+-----------------------------------+------------------------+-----------------------------------------------------------------------------------+
-| ``AIRFLOW_SOURCES_TO``            | ``/empty``             | Target for Airflow sources. Set to "/opt/airflow" when                            |
-|                                   |                        | you want to install airflow from local sources.                                   |
-+-----------------------------------+------------------------+-----------------------------------------------------------------------------------+
++------------------------------------------+------------------------------------------+------------------------------------------+
+| Build argument                           | Default value                            | Description                              |
++==========================================+==========================================+==========================================+
+| ``AIRFLOW_BRANCH``                       | ``master``                               | the branch from which PIP dependencies   |
+|                                          |                                          | are pre-installed initially.             |
++------------------------------------------+------------------------------------------+------------------------------------------+
+| ``AIRFLOW_REPO``                         | ``apache/airflow``                       | the repository from which PIP            |
+|                                          |                                          | dependencies are pre-installed.          |
++------------------------------------------+------------------------------------------+------------------------------------------+
+| ``AIRFLOW_PRE_CACHED_PIP_PACKAGES``      | ``false``                                | Allows to pre-cache airflow PIP packages |
+|                                          |                                          | from the GitHub of Apache Airflow        |
+|                                          |                                          | This allows to optimize iterations for   |
+|                                          |                                          | Image builds and speeds up CI builds.    |
++------------------------------------------+------------------------------------------+------------------------------------------+
diff --git a/docs/docker-stack/build.rst b/docs/docker-stack/build.rst
index 98673c69205f4..a07a837ba940a 100644
--- a/docs/docker-stack/build.rst
+++ b/docs/docker-stack/build.rst
@@ -22,6 +22,45 @@ Before you dive-deeply in the way how the Airflow Image is build, named and why
 way we do, you might want to know very quickly how you can extend or customize the existing image
 for Apache Airflow. This chapter gives you a short answer to those questions.
 
+Extending vs. customizing the image
+-----------------------------------
+
+Here is the comparison of the two types of building images. Here is your guide if you want to choose
+how you want to build your image.
+
++----------------------------------------------------+-----------+-------------+
+|                                                    | Extending | Customizing |
++====================================================+===========+=============+
+| Can be built without airflow sources               | Yes       | No          |
++----------------------------------------------------+-----------+-------------+
+| Uses familiar 'FROM ' pattern of image building    | Yes       | No          |
++----------------------------------------------------+-----------+-------------+
+| Requires only basic knowledge about images         | Yes       | No          |
++----------------------------------------------------+-----------+-------------+
+| Builds quickly                                     | Yes       | No          |
++----------------------------------------------------+-----------+-------------+
+| Produces image heavily optimized for size          | No        | Yes         |
++----------------------------------------------------+-----------+-------------+
+| Can build from custom airflow sources (forks)      | No        | Yes         |
++----------------------------------------------------+-----------+-------------+
+| Can build on air-gaped system                      | No        | Yes         |
++----------------------------------------------------+-----------+-------------+
+
+TL;DR; If you have a need to build custom image, it is easier to start with "Extending" however if your
+dependencies require compilation step or when your require to build the image from security vetted
+packages, switching to "Customizing" the image provides much more optimized images. In the example further
+where we compare equivalent "Extending" and "Customizing" the image, similar images build by
+Extending vs. Customization had shown 1.1GB vs 874MB image sizes respectively - with 20% improvement in
+size of the Customized image.
+
+.. note::
+
+  You can also combine both - customizing & extending the image in one. You can build your
+  optimized base image first using ``customization`` method (for example by your admin team) with all
+  the heavy compilation required dependencies and you can publish it in your registry and let others
+  ``extend`` your image using ``FROM`` and add their own lightweight dependencies. This reflects well
+  the split where typically "Casual" users will Extend the image and "Power-users" will customize it.
+
 Airflow Summit 2020's `Production Docker Image <https://youtu.be/wDr3Y7q2XoI>`_ talk provides more
 details about the context, architecture and customization/extension methods for the Production Image.
 
@@ -41,210 +80,322 @@ Debian dependencies with ``apt`` or PyPI dependencies with ``pip install`` or an
 You should be aware, about a few things:
 
 * The production image of airflow uses "airflow" user, so if you want to add some of the tools
-  as ``root`` user, you need to switch to it with ``USER`` directive of the Dockerfile. Also you
-  should remember about following the
+  as ``root`` user, you need to switch to it with ``USER`` directive of the Dockerfile and switch back to
+  ``airflow`` user when you are done. Also you should remember about following the
   `best practises of Dockerfiles <https://docs.docker.com/develop/develop-images/dockerfile_best-practices/>`_
   to make sure your image is lean and small.
 
-  .. code-block:: dockerfile
+* The PyPI dependencies in Apache Airflow are installed in the user library, of the "airflow" user, so
+  PIP packages are installed to ``~/.local`` folder as if the ``--user`` flag was specified when running PIP.
+  Note also that using ``--no-cache-dir`` is a good idea that can help to make your image smaller.
 
-    FROM apache/airflow:2.0.1
-    USER root
-    RUN apt-get update \
-      && apt-get install -y --no-install-recommends \
-             my-awesome-apt-dependency-to-add \
-      && apt-get autoremove -yqq --purge \
-      && apt-get clean \
-      && rm -rf /var/lib/apt/lists/*
-    USER airflow
+* If your apt, or PyPI dependencies require some of the ``build-essential`` or other packages that need
+  to compile your python dependencies, then your best choice is to follow the "Customize the image" route,
+  because you can build a highly-optimized (for size) image this way. However it requires to checkout sources
+  of Apache Airflow, so you might still want to choose to add ``build-essential`` to your image,
+  even if your image will be significantly bigger.
 
+* You can also embed your dags in the image by simply adding them with COPY directive of Airflow.
+  The DAGs in production image are in ``/opt/airflow/dags`` folder.
 
-* PyPI dependencies in Apache Airflow are installed in the user library, of the "airflow" user, so
-  you need to install them with the ``--user`` flag and WITHOUT switching to airflow user. Note also
-  that using --no-cache-dir is a good idea that can help to make your image smaller.
+* You can build your image without any need for Airflow sources. It is enough that you place the
+  ``Dockerfile`` and any files that are referred to (such as Dag files) in a separate directory and run
+  a command ``docker build . --tag my-image:my-tag`` (where ``my-image`` is the name you want to name it
+  and ``my-tag`` is the tag you want to tag the image with.
 
-  .. code-block:: dockerfile
+.. note::
+  As of 2.0.1 image the ``--user`` flag is turned on by default by setting ``PIP_USER`` environment variable
+  to ``true``. This can be disabled by un-setting the variable or by setting it to ``false``. In the
+  2.0.0 image you had to add the ``--user`` flag as ``pip install --user`` command.
 
-    FROM apache/airflow:2.0.1
-    RUN pip install --no-cache-dir --user my-awesome-pip-dependency-to-add
+Examples of image extending
+---------------------------
 
-* As of 2.0.1 image the ``--user`` flag is turned on by default by setting ``PIP_USER`` environment variable
-  to ``true``. This can be disabled by un-setting the variable or by setting it to ``false``.
+An ``apt`` package example
+..........................
 
+The following example adds ``vim`` to the airflow image.
 
-* If your apt, or PyPI dependencies require some of the build-essentials, then your best choice is
-  to follow the "Customize the image" route. However it requires to checkout sources of Apache Airflow,
-  so you might still want to choose to add build essentials to your image, even if your image will
-  be significantly bigger.
+.. exampleinclude:: docker-examples/extending/add-apt-packages/Dockerfile
+    :language: Dockerfile
+    :start-after: [START Dockerfile]
+    :end-before: [END Dockerfile]
 
-  .. code-block:: dockerfile
+A ``PyPI`` package example
+..........................
 
-    FROM apache/airflow:2.0.1
-    USER root
-    RUN apt-get update \
-      && apt-get install -y --no-install-recommends \
-             build-essential my-awesome-apt-dependency-to-add \
-      && apt-get autoremove -yqq --purge \
-      && apt-get clean \
-      && rm -rf /var/lib/apt/lists/*
-    USER airflow
-    RUN pip install --no-cache-dir --user my-awesome-pip-dependency-to-add
+The following example adds ``lxml`` python package from PyPI to the image.
 
-* You can also embed your dags in the image by simply adding them with COPY directive of Airflow.
-  The DAGs in production image are in ``/opt/airflow/dags`` folder.
+.. exampleinclude:: docker-examples/extending/add-pypi-packages/Dockerfile
+    :language: Dockerfile
+    :start-after: [START Dockerfile]
+    :end-before: [END Dockerfile]
+
+A ``build-essential`` requiring package example
+...............................................
+
+The following example adds ``mpi4py`` package which requires both ``build-essential`` and ``mpi compiler``.
+
+.. exampleinclude:: docker-examples/extending/add-build-essential-extend/Dockerfile
+    :language: Dockerfile
+    :start-after: [START Dockerfile]
+    :end-before: [END Dockerfile]
+
+The size of this image is ~ 1.1 GB when build. As you will see further, you can achieve 20% reduction in
+size of the image in case you use "Customizing" rather than "Extending" the image.
+
+DAG embedding example
+.....................
+
+The following example adds ``test_dag.py`` to your image in the ``/opt/airflow/dags`` folder.
+
+.. exampleinclude:: docker-examples/extending/embedding-dags/Dockerfile
+    :language: Dockerfile
+    :start-after: [START Dockerfile]
+    :end-before: [END Dockerfile]
+
+
+.. exampleinclude:: docker-examples/extending/embedding-dags/test_dag.py
+    :language: Python
+    :start-after: [START dag]
+    :end-before: [END dag]
 
 Customizing the image
 ---------------------
 
-Customizing the image is an alternative way of adding your own dependencies to the image - better
-suited to prepare optimized production images.
+Customizing the image is an optimized way of adding your own dependencies to the image - better
+suited to prepare highly optimized (for size) production images, especially when you have dependencies
+that require to be compiled before installing (such as ``mpi4py``).
+
+It also allows more sophisticated usages, needed by "Power-users" - for example using forked version
+of Airflow, or building the images from security-vetted sources.
+
+The big advantage of this method is that it produces optimized image even if you need some compile-time
+dependencies that are not needed in the final image.
+
+The disadvantage is that you need to use Airflow Sources to build such images from the
+`official distribution repository of Apache Airflow <https://downloads.apache.org/airflow/>`_ for the
+released versions, or from the checked out sources (using release tags or main branches) in the
+`Airflow GitHub Project <https://github.com/apache/airflow>`_ or from your own fork
+if you happen to do maintain your own fork of Airflow.
+
+Another disadvantage is that the pattern of building Docker images with ``--build-arg`` is less familiar
+to developers of such images. However it is quite well-known to "power-users". That's why the
+customizing flow is better suited for those users who have more familiarity and have more custom
+requirements.
+
+The image also usually builds much longer than the equivalent "Extended" image because instead of
+extending the layers that are already coming from the base image, it rebuilds the layers needed
+to add extra dependencies needed at early stages of image building.
+
+When customizing the image you can choose a number of options how you install Airflow:
+
+   * From the PyPI releases (default)
+   * From the custom installation sources - using additional/replacing the original apt or PyPI repositories
+   * From local sources. This is used mostly during development.
+   * From tag or branch, or specific commit from a GitHub Airflow repository (or fork). This is particularly
+     useful when you build image for a custom version of Airflow that you keep in your fork and you do not
+     want to release the custom Airflow version to PyPI.
+   * From locally stored binary packages for Airflow, Airflow Providers and other dependencies. This is
+     particularly useful if you want to build Airflow in a highly-secure environment where all such packages
+     must be vetted by your security team and stored in your private artifact registry. This also
+     allows to build airflow image in an air-gaped environment.
+   * Side note. Building ``Airflow`` in an ``air-gaped`` environment sounds pretty funny, doesn't it?
+
+You can also add a range of customizations while building the image:
+
+   * base python image you use for Airflow
+   * version of Airflow to install
+   * extras to install for Airflow (or even removing some default extras)
+   * additional apt/python dependencies to use while building Airflow (DEV dependencies)
+   * additional apt/python dependencies to install for runtime version of Airflow (RUNTIME dependencies)
+   * additional commands and variables to set if needed during building or preparing Airflow runtime
+   * choosing constraint file to use when installing Airflow
+
+Additional explanation is needed for the last point. Airflow uses constraints to make sure
+that it can be predictably installed, even if some new versions of Airflow dependencies are
+released (or even dependencies of our dependencies!). The docker image and accompanying scripts
+usually determine automatically the right versions of constraints to be used based on the Airflow
+version installed and Python version. For example 2.0.1 version of Airflow installed from PyPI
+uses constraints from ``constraints-2.0.1`` tag). However in some cases - when installing airflow from
+GitHub for example - you have to manually specify the version of constraints used, otherwise
+it will default to the latest version of the constraints which might not be compatible with the
+version of Airflow you use.
+
+You can also download any version of Airflow constraints and adapt it with your own set of
+constraints and manually set your own versions of dependencies in your own constraints and use the version
+of constraints that you manually prepared.
+
+You can read more about constraints in the documentation of the
+`Installation <http://airflow.apache.org/docs/apache-airflow/stable/installation.html#constraints-files>`_
+
+Examples of image customizing
+-----------------------------
+
+.. _image-build-pypi:
+
+
+Building from PyPI packages
+...........................
+
+This is the basic way of building the custom images from sources.
+
+The following example builds the production image in version ``3.6`` with latest PyPI-released Airflow,
+with default set of Airflow extras and dependencies. The ``2.0.1`` constraints are used automatically.
 
-The advantage of this method is that it produces optimized image even if you need some compile-time
-dependencies that are not needed in the final image. You need to use Airflow Sources to build such images
-from the `official distribution folder of Apache Airflow <https://downloads.apache.org/airflow/>`_ for the
-released versions, or checked out from the GitHub project if you happen to do it from git sources.
+.. exampleinclude:: docker-examples/customizing/stable-airflow.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
 
-The easiest way to build the image is to use ``breeze`` script, but you can also build such customized
-image by running appropriately crafted docker build in which you specify all the ``build-args``
-that you need to add to customize it. You can read about all the args and ways you can build the image
-in :doc:`build-arg-ref`.
+The following example builds the production image in version ``3.7`` with default extras from ``2.0.1`` PyPI
+package. The ``2.0.1`` constraints are used automatically.
 
-Here just a few examples are presented which should give you general understanding of what you can customize.
+.. exampleinclude:: docker-examples/customizing/pypi-selected-version.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
 
-This builds production image in version 3.6 with default extras from the local sources (master version
-of 2.0 currently):
+The following example builds the production image in version ``3.8`` with additional airflow extras
+(``mssql,hdfs``) from ``2.0.1`` PyPI package, and additional dependency (``oauth2client``).
 
-.. code-block:: bash
+.. exampleinclude:: docker-examples/customizing/pypi-extras-and-deps.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
 
-  docker build .
 
-This builds the production image in version 3.7 with default extras from 2.0.1 tag and
-constraints taken from constraints-2-0 branch in GitHub.
+The following example adds ``mpi4py`` package which requires both ``build-essential`` and ``mpi compiler``.
 
-.. code-block:: bash
+.. exampleinclude:: docker-examples/customizing/add-build-essential-custom.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
 
-  docker build . \
-    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
-    --build-arg PYTHON_MAJOR_MINOR_VERSION=3.7 \
-    --build-arg AIRFLOW_INSTALLATION_METHOD="https://github.com/apache/airflow/archive/2.0.1.tar.gz#egg=apache-airflow" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-0" \
-    --build-arg AIRFLOW_BRANCH="v1-10-test" \
-    --build-arg AIRFLOW_SOURCES_FROM="empty" \
-    --build-arg AIRFLOW_SOURCES_TO="/empty"
+The above image is equivalent of the "extended" image from previous chapter but it's size is only
+874 MB. Comparing to 1.1 GB of the "extended image" this is about 230 MB less, so you can achieve ~20%
+improvement in size of the image by using "customization" vs. extension. The saving can increase in case you
+have more complex dependencies to build.
 
-This builds the production image in version 3.7 with default extras from 2.0.1 PyPI package and
-constraints taken from 2.0.1 tag in GitHub and pre-installed pip dependencies from the top
-of ``v1-10-test`` branch.
 
-.. code-block:: bash
+.. _image-build-optimized:
 
-  docker build . \
-    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
-    --build-arg PYTHON_MAJOR_MINOR_VERSION=3.7 \
-    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
-    --build-arg AIRFLOW_VERSION="2.0.1" \
-    --build-arg AIRFLOW_VERSION_SPECIFICATION="==2.0.1" \
-    --build-arg AIRFLOW_BRANCH="v1-10-test" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2.0.1" \
-    --build-arg AIRFLOW_SOURCES_FROM="empty" \
-    --build-arg AIRFLOW_SOURCES_TO="/empty"
+Building optimized images
+.........................
 
-This builds the production image in version 3.7 with additional airflow extras from 2.0.1 PyPI package and
-additional python dependencies and pre-installed pip dependencies from 2.0.1 tagged constraints.
+The following example the production image in version ``3.6`` with additional airflow extras from ``2.0.1``
+PyPI package but it includes additional apt dev and runtime dependencies.
 
-.. code-block:: bash
+The dev dependencies are those that require ``build-essential`` and usually need to involve recompiling
+of some python dependencies so those packages might require some additional DEV dependencies to be
+present during recompilation. Those packages are not needed at runtime, so we only install them for the
+"build" time. They are not installed in the final image, thus producing much smaller images.
+In this case pandas requires recompilation so it also needs gcc and g++ as dev APT dependencies.
+The ``jre-headless`` does not require recompiling so it can be installed as the runtime APT dependency.
 
-  docker build . \
-    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
-    --build-arg PYTHON_MAJOR_MINOR_VERSION=3.7 \
-    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
-    --build-arg AIRFLOW_VERSION="2.0.1" \
-    --build-arg AIRFLOW_VERSION_SPECIFICATION="==2.0.1" \
-    --build-arg AIRFLOW_BRANCH="v1-10-test" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2.0.1" \
-    --build-arg AIRFLOW_SOURCES_FROM="empty" \
-    --build-arg AIRFLOW_SOURCES_TO="/empty" \
-    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="mssql,hdfs" \
-    --build-arg ADDITIONAL_PYTHON_DEPS="sshtunnel oauth2client"
+.. exampleinclude:: docker-examples/customizing/pypi-dev-runtime-deps.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
 
-This builds the production image in version 3.7 with additional airflow extras from 2.0.1 PyPI package and
-additional apt dev and runtime dependencies.
+.. _image-build-github:
 
-.. code-block:: bash
 
-  docker build . \
-    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
-    --build-arg PYTHON_MAJOR_MINOR_VERSION=3.7 \
-    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
-    --build-arg AIRFLOW_VERSION="2.0.1" \
-    --build-arg AIRFLOW_VERSION_SPECIFICATION="==2.0.1" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-0" \
-    --build-arg AIRFLOW_SOURCES_FROM="empty" \
-    --build-arg AIRFLOW_SOURCES_TO="/empty" \
-    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="jdbc" \
-    --build-arg ADDITIONAL_PYTHON_DEPS="pandas" \
-    --build-arg ADDITIONAL_DEV_APT_DEPS="gcc g++" \
-    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="default-jre-headless" \
-    --tag my-image
+Building from GitHub
+....................
 
+This method is usually used for development purpose. But in case you have your own fork you can point
+it to your forked version of source code without having to release it to PyPI. It is enough to have
+a branch or tag in your repository and use the tag or branch in the URL that you point the installation to.
 
-The same image can be built using ``breeze`` (it supports auto-completion of the options):
+In case of GitHyb builds you need to pass the constraints reference manually in case you want to use
+specific constraints, otherwise the default ``constraints-master`` is used.
 
-.. code-block:: bash
+The following example builds the production image in version ``3.7`` with default extras from the latest master version and
+constraints are taken from latest version of the constraints-master branch in GitHub.
 
-  ./breeze build-image \
-      --production-image  --python 3.7 --install-airflow-version=2.0.1 \
-      --additional-extras=jdbc --additional-python-deps="pandas" \
-      --additional-dev-apt-deps="gcc g++" --additional-runtime-apt-deps="default-jre-headless"
+.. exampleinclude:: docker-examples/customizing/github-master.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
 
+The following example builds the production image with default extras from the
+latest ``v2-0-test`` version and constraints are taken from the latest version of
+the ``constraints-2-0`` branch in GitHub. Note that this command might fail occasionally as only
+the "released version" constraints when building a version and "master" constraints when building
+master are guaranteed to work.
+
+.. exampleinclude:: docker-examples/customizing/github-v2-0-test.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
+
+You can also specify another repository to build from. If you also want to use different constraints
+repository source, you must specify it as additional ``CONSTRAINTS_GITHUB_REPOSITORY`` build arg.
+
+The following example builds the production image using ``potiuk/airflow`` fork of Airflow and constraints
+are also downloaded from that repository.
+
+.. exampleinclude:: docker-examples/customizing/github-different-repository.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
+
+.. _image-build-custom:
+
+Using custom installation sources
+.................................
 
 You can customize more aspects of the image - such as additional commands executed before apt dependencies
 are installed, or adding extra sources to install your dependencies from. You can see all the arguments
 described below but here is an example of rather complex command to customize the image
 based on example in `this comment <https://github.com/apache/airflow/issues/8605#issuecomment-690065621>`_:
 
-.. code-block:: bash
-
-  docker build . -f Dockerfile \
-    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
-    --build-arg PYTHON_MAJOR_MINOR_VERSION=3.7 \
-    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
-    --build-arg AIRFLOW_VERSION="2.0.1" \
-    --build-arg AIRFLOW_VERSION_SPECIFICATION="==2.0.1" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-0" \
-    --build-arg AIRFLOW_SOURCES_FROM="empty" \
-    --build-arg AIRFLOW_SOURCES_TO="/empty" \
-    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="slack" \
-    --build-arg ADDITIONAL_PYTHON_DEPS=" \
-        apache-airflow-providers-odbc \
-        azure-storage-blob \
-        sshtunnel \
-        google-api-python-client \
-        oauth2client \
-        beautifulsoup4 \
-        dateparser \
-        rocketchat_API \
-        typeform" \
-    --build-arg ADDITIONAL_DEV_APT_DEPS="msodbcsql17 unixodbc-dev g++" \
-    --build-arg ADDITIONAL_DEV_APT_COMMAND="curl https://packages.microsoft.com/keys/microsoft.asc | \
-    apt-key add --no-tty - && \
-    curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list" \
-    --build-arg ADDITIONAL_DEV_ENV_VARS="ACCEPT_EULA=Y" \
-    --build-arg ADDITIONAL_RUNTIME_APT_COMMAND="curl https://packages.microsoft.com/keys/microsoft.asc | \
-    apt-key add --no-tty - && \
-    curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list" \
-    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="msodbcsql17 unixodbc git procps vim" \
-    --build-arg ADDITIONAL_RUNTIME_ENV_VARS="ACCEPT_EULA=Y" \
-    --tag my-image
-
-Customizing images in high security restricted environments
-...........................................................
+In case you need to use your custom PyPI package indexes, you can also customize PYPI sources used during
+image build by adding a ``docker-context-files``/``.pypirc`` file when building the image.
+This ``.pypirc`` will not be committed to the repository (it is added to ``.gitignore``) and it will not be
+present in the final production image. It is added and used only in the build segment of the image.
+Therefore this ``.pypirc`` file can safely contain list of package indexes you want to use,
+usernames and passwords used for authentication. More details about ``.pypirc`` file can be found in the
+`pypirc specification <https://packaging.python.org/specifications/pypirc/>`_.
+
+Such customizations are independent of the way how airflow is installed.
+
+.. note::
+  Similar results could be achieved by modifying the Dockerfile manually (see below) and injecting the
+  commands needed, but by specifying the customizations via build-args, you avoid the need of
+  synchronizing the changes from future Airflow Dockerfiles. Those customizations should work with the
+  future version of Airflow's official ``Dockerfile`` at most with minimal modifications od parameter
+  names (if any), so using the build command for your customizations makes your custom image more
+  future-proof.
+
+The following - rather complex - example shows capabilities of:
+
+  * Adding airflow extras (slack, odbc)
+  * Adding PyPI dependencies (``azure-storage-blob, oauth2client, beautifulsoup4, dateparser, rocketchat_API,typeform``)
+  * Adding custom environment variables while installing ``apt`` dependencies - both DEV and RUNTIME
+    (``ACCEPT_EULA=Y'``)
+  * Adding custom curl command for adding keys and configuring additional apt sources needed to install
+    ``apt`` dependencies (both DEV and RUNTIME)
+  * Adding custom ``apt`` dependencies, both DEV (``msodbcsql17 unixodbc-dev g++) and runtime msodbcsql17 unixodbc git procps vim``)
+
+.. exampleinclude:: docker-examples/customizing/custom-sources.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
+
+.. _image-build-secure-environments:
+
+Build images in security restricted environments
+................................................
 
 You can also make sure your image is only build using local constraint file and locally downloaded
 wheel files. This is often useful in Enterprise environments where the binary files are verified and
-vetted by the security teams.
+vetted by the security teams. It is also the most complex way of building the image. You should be an
+expert of building and using Dockerfiles in order to use it and have to have specific needs of security if
+you want to follow that route.
 
-This builds below builds the production image in version 3.7 with packages and constraints used from the local
+This builds below builds the production image  with packages and constraints used from the local
 ``docker-context-files`` rather than installed from PyPI or GitHub. It also disables MySQL client
 installation as it is using external installation method.
 
@@ -262,119 +413,99 @@ The ``pip download`` might happen in a separate environment. The files can be co
 binary repository and vetted/verified by the security team and used subsequently to build images
 of Airflow when needed on an air-gaped system.
 
-Preparing the constraint files and wheel files:
+Example of preparing the constraint files and wheel files. Note that ``mysql`` dependency is removed
+as ``mysqlclient`` is installed from Oracle's ``apt`` repository and if you want to add it, you need
+to provide this library from you repository if you want to build Airflow image in an "air-gaped" system.
 
-.. code-block:: bash
+.. exampleinclude:: docker-examples/restricted/restricted_environments.sh
+    :language: bash
+    :start-after: [START download]
+    :end-before: [END download]
 
-  rm docker-context-files/*.whl docker-context-files/*.txt
+After this step is finished, your ``docker-context-files`` folder will contain all the packages that
+are needed to install Airflow from.
 
-  curl -Lo "docker-context-files/constraints-2-0.txt" \
-    https://raw.githubusercontent.com/apache/airflow/constraints-2-0/constraints-3.7.txt
+Those downloaded packages and constraint file can be pre-vetted by your security team before you attempt
+to install the image. You can also store those downloaded binary packages in your private artifact registry
+which allows for the flow where you will download the packages on one machine, submit only new packages for
+security vetting and only use the new packages when they were vetted.
 
-  pip download --dest docker-context-files \
-    --constraint docker-context-files/constraints-2-0.txt  \
-    apache-airflow[async,aws,azure,celery,dask,elasticsearch,gcp,kubernetes,mysql,postgres,redis,slack,ssh,statsd,virtualenv]==2.0.1
+On a separate (air-gaped) system, all the PyPI packages can be copied to ``docker-context-files``
+where you can build the image using the packages downloaded by passing those build args:
 
-Since apache-airflow .whl packages are treated differently by the docker image, you need to rename the
-downloaded apache-airflow* files, for example:
+  * ``INSTALL_FROM_DOCKER_CONTEXT_FILES="true"``  - to use packages present in ``docker-context-files``
+  * ``AIRFLOW_PRE_CACHED_PIP_PACKAGES="false"``  - to not pre-cache packages from PyPI when building image
+  * ``AIRFLOW_CONSTRAINTS_LOCATION=/docker-context-files/YOUR_CONSTRAINT_FILE.txt`` - to downloaded constraint files
+  * (Optional) ``INSTALL_MYSQL_CLIENT="false"`` if you do not want to install ``MySQL``
+    client from the Oracle repositories. In this case also make sure that your
 
-.. code-block:: bash
+Note, that the solution we have for installing python packages from local packages, only solves the problem
+of "air-gaped" python installation. The Docker image also downloads ``apt`` dependencies and ``node-modules``.
+Those type of dependencies are however more likely to be available in your "air-gaped" system via transparent
+proxies and it should automatically reach out to your private registries, however in the future the
+solution might be applied to both of those installation steps.
 
-   pushd docker-context-files
-   for file in apache?airflow*
-   do
-     mv ${file} _${file}
-   done
-   popd
+You can also use techniques described in the previous chapter to make ``docker build`` use your private
+apt sources or private PyPI repositories (via ``.pypirc``) available which can be security-vetted.
 
-Building the image:
+If you fulfill all the criteria, you can build the image on an air-gaped system by running command similar
+to the below:
 
-.. code-block:: bash
+.. exampleinclude:: docker-examples/restricted/restricted_environments.sh
+    :language: bash
+    :start-after: [START build]
+    :end-before: [END build]
 
-  ./breeze build-image \
-      --production-image --python 3.7 --install-airflow-version=2.0.1 \
-      --disable-mysql-client-installation --disable-pip-cache --install-from-local-files-when-building \
-      --constraints-location="/docker-context-files/constraints-2-0.txt"
+Modifying the Dockerfile
+........................
 
-or
+The build arg approach is a convenience method if you do not want to manually modify the ``Dockerfile``.
+Our approach is flexible enough, to be able to accommodate most requirements and
+customizations out-of-the-box. When you use it, you do not need to worry about adapting the image every
+time new version of Airflow is released. However sometimes it is not enough if you have very
+specific needs and want to build a very custom image. In such case you can simply modify the
+``Dockerfile`` manually as you see fit and store it in your forked repository. However you will have to
+make sure to rebase your changes whenever new version of Airflow is released, because we might modify
+the approach of our Dockerfile builds in the future and you might need to resolve conflicts
+and rebase your changes.
 
-.. code-block:: bash
+There are a few things to remember when you modify the ``Dockerfile``:
 
-  docker build . \
-    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
-    --build-arg PYTHON_MAJOR_MINOR_VERSION=3.7 \
-    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
-    --build-arg AIRFLOW_VERSION="2.0.1" \
-    --build-arg AIRFLOW_VERSION_SPECIFICATION="==2.0.1" \
-    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-0" \
-    --build-arg AIRFLOW_SOURCES_FROM="empty" \
-    --build-arg AIRFLOW_SOURCES_TO="/empty" \
-    --build-arg INSTALL_MYSQL_CLIENT="false" \
-    --build-arg AIRFLOW_PRE_CACHED_PIP_PACKAGES="false" \
-    --build-arg INSTALL_FROM_DOCKER_CONTEXT_FILES="true" \
-    --build-arg AIRFLOW_CONSTRAINTS_LOCATION="/docker-context-files/constraints-2-0.txt"
+* We are using the widely recommended pattern of ``.dockerignore`` where everything is ignored by default
+  and only the required folders are added through exclusion (!). This allows to keep docker context small
+  because there are many binary artifacts generated in the sources of Airflow and if they are added to
+  the context, the time of building the image would increase significantly. If you want to add any new
+  folders to be available in the image you must add it here with leading ``!``.
 
+  .. code-block:: text
 
-Customizing & extending the image together
-..........................................
+      # Ignore everything
+      **
 
-You can combine both - customizing & extending the image. You can build the image first using
-``customize`` method (either with docker command or with ``breeze`` and then you can ``extend``
-the resulting image using ``FROM`` any dependencies you want.
+      # Allow only these directories
+      !airflow
+      ...
 
-Customizing PYPI installation
-.............................
 
-You can customize PYPI sources used during image build by adding a ``docker-context-files``/``.pypirc`` file
-This ``.pypirc`` will never be committed to the repository and will not be present in the final production image.
-It is added and used only in the build segment of the image so it is never copied to the final image.
+* The ``docker-context-files`` folder is automatically added to the context of the image, so if you want
+  to add individual files, binaries, requirement files etc you can add them there. The
+  ``docker-context-files`` is copied to the ``/docker-context-files`` folder of the build segment of the
+  image, so it is not present in the final image - which makes the final image smaller in case you want
+  to use those files only in the ``build`` segment. You must copy any files from the directory manually,
+  using COPY command if you want to get the files in your final image (in the main image segment).
 
-External sources for dependencies
-.................................
 
-In corporate environments, there is often the need to build your Container images using
-other than default sources of dependencies. The docker file uses standard sources (such as
-Debian apt repositories or PyPI repository. However, in corporate environments, the dependencies
-are often only possible to be installed from internal, vetted repositories that are reviewed and
-approved by the internal security teams. In those cases, you might need to use those different
-sources.
-
-This is rather easy if you extend the image - you simply write your extension commands
-using the right sources - either by adding/replacing the sources in apt configuration or
-specifying the source repository in pip install command.
-
-It's a bit more involved in the case of customizing the image. We do not have yet (but we are working
-on it) a capability of changing the sources via build args. However, since the builds use
-Dockerfile that is a source file, you can rather easily simply modify the file manually and
-specify different sources to be used by either of the commands.
-
-
-Comparing extending and customizing the image
----------------------------------------------
-
-Here is the comparison of the two types of building images.
-
-+----------------------------------------------------+---------------------+-----------------------+
-|                                                    | Extending the image | Customizing the image |
-+====================================================+=====================+=======================+
-| Produces optimized image                           | No                  | Yes                   |
-+----------------------------------------------------+---------------------+-----------------------+
-| Use Airflow Dockerfile sources to build the image  | No                  | Yes                   |
-+----------------------------------------------------+---------------------+-----------------------+
-| Requires Airflow sources                           | No                  | Yes                   |
-+----------------------------------------------------+---------------------+-----------------------+
-| You can build it with Breeze                       | No                  | Yes                   |
-+----------------------------------------------------+---------------------+-----------------------+
-| Allows to use non-default sources for dependencies | Yes                 | No [1]                |
-+----------------------------------------------------+---------------------+-----------------------+
-
-[1] When you combine customizing and extending the image, you can use external sources
-in the "extend" part. There are plans to add functionality to add external sources
-option to image customization. You can also modify Dockerfile manually if you want to
-use non-default sources for dependencies.
-
-More details about the images
------------------------------
+More details
+------------
+
+Build Args reference
+....................
+
+The detailed ``--build-arg`` reference can be found in :doc:`build-arg-ref`.
+
+
+The architecture of the images
+..............................
 
 You can read more details about the images - the context, their parameters and internal structure in the
 `IMAGES.rst <https://github.com/apache/airflow/blob/master/IMAGES.rst>`_ document.
diff --git a/docs/docker-stack/docker-examples/customizing/add-build-essential-custom.sh b/docs/docker-stack/docker-examples/customizing/add-build-essential-custom.sh
new file mode 100755
index 0000000000000..716447055666e
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/add-build-essential-custom.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.6-slim-buster" \
+    --build-arg AIRFLOW_VERSION="2.0.1" \
+    --build-arg ADDITIONAL_PYTHON_DEPS="mpi4py" \
+    --build-arg ADDITIONAL_DEV_APT_DEPS="libopenmpi-dev" \
+    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="openmpi-common" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/custom-sources.sh b/docs/docker-stack/docker-examples/customizing/custom-sources.sh
new file mode 100755
index 0000000000000..242fc2e41bcb7
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/custom-sources.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . -f Dockerfile \
+    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
+    --build-arg AIRFLOW_VERSION="2.0.1" \
+    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="slack,odbc" \
+    --build-arg ADDITIONAL_PYTHON_DEPS=" \
+        azure-storage-blob \
+        oauth2client \
+        beautifulsoup4 \
+        dateparser \
+        rocketchat_API \
+        typeform" \
+    --build-arg ADDITIONAL_DEV_APT_COMMAND="curl https://packages.microsoft.com/keys/microsoft.asc | \
+    apt-key add --no-tty - && \
+    curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list" \
+    --build-arg ADDITIONAL_DEV_APT_ENV="ACCEPT_EULA=Y" \
+    --build-arg ADDITIONAL_DEV_APT_DEPS="msodbcsql17 unixodbc-dev g++" \
+    --build-arg ADDITIONAL_RUNTIME_APT_COMMAND="curl https://packages.microsoft.com/keys/microsoft.asc | \
+    apt-key add --no-tty - && \
+    curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list" \
+    --build-arg ADDITIONAL_RUNTIME_APT_ENV="ACCEPT_EULA=Y" \
+    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="msodbcsql17 unixodbc git procps vim" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/github-different-repository.sh b/docs/docker-stack/docker-examples/customizing/github-different-repository.sh
new file mode 100755
index 0000000000000..b980b5b7c0e88
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/github-different-repository.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.8-slim-buster" \
+    --build-arg AIRFLOW_INSTALLATION_METHOD="https://github.com/potiuk/airflow/archive/master.tar.gz#egg=apache-airflow" \
+    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-master" \
+    --build-arg CONSTRAINTS_GITHUB_REPOSITORY="potiuk/airflow" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/github-master.sh b/docs/docker-stack/docker-examples/customizing/github-master.sh
new file mode 100755
index 0000000000000..4237e91e6ff56
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/github-master.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
+    --build-arg AIRFLOW_INSTALLATION_METHOD="https://github.com/apache/airflow/archive/master.tar.gz#egg=apache-airflow" \
+    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-master" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/github-v2-0-test.sh b/docs/docker-stack/docker-examples/customizing/github-v2-0-test.sh
new file mode 100755
index 0000000000000..b893618a66834
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/github-v2-0-test.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.8-slim-buster" \
+    --build-arg AIRFLOW_INSTALLATION_METHOD="https://github.com/apache/airflow/archive/v2-0-test.tar.gz#egg=apache-airflow" \
+    --build-arg AIRFLOW_CONSTRAINTS_REFERENCE="constraints-2-0" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/pypi-dev-runtime-deps.sh b/docs/docker-stack/docker-examples/customizing/pypi-dev-runtime-deps.sh
new file mode 100755
index 0000000000000..43a80927a0125
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/pypi-dev-runtime-deps.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.6-slim-buster" \
+    --build-arg AIRFLOW_VERSION="2.0.1" \
+    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="jdbc" \
+    --build-arg ADDITIONAL_PYTHON_DEPS="pandas" \
+    --build-arg ADDITIONAL_DEV_APT_DEPS="gcc g++" \
+    --build-arg ADDITIONAL_RUNTIME_APT_DEPS="default-jre-headless" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/pypi-extras-and-deps.sh b/docs/docker-stack/docker-examples/customizing/pypi-extras-and-deps.sh
new file mode 100755
index 0000000000000..7d150bc588488
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/pypi-extras-and-deps.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.8-slim-buster" \
+    --build-arg AIRFLOW_VERSION="2.0.1" \
+    --build-arg ADDITIONAL_AIRFLOW_EXTRAS="mssql,hdfs" \
+    --build-arg ADDITIONAL_PYTHON_DEPS="oauth2client" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/pypi-selected-version.sh b/docs/docker-stack/docker-examples/customizing/pypi-selected-version.sh
new file mode 100755
index 0000000000000..98e06a1c523c4
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/pypi-selected-version.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
+    --build-arg AIRFLOW_VERSION="2.0.1" \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/customizing/stable-airflow.sh b/docs/docker-stack/docker-examples/customizing/stable-airflow.sh
new file mode 100755
index 0000000000000..d3471acc181fe
--- /dev/null
+++ b/docs/docker-stack/docker-examples/customizing/stable-airflow.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START build]
+docker build . \
+    --tag "$(basename "$0")"
+# [END build]
+docker rmi --force "$(basename "$0")"
diff --git a/docs/docker-stack/docker-examples/extending/add-apt-packages/Dockerfile b/docs/docker-stack/docker-examples/extending/add-apt-packages/Dockerfile
new file mode 100644
index 0000000000000..8fb128ebae986
--- /dev/null
+++ b/docs/docker-stack/docker-examples/extending/add-apt-packages/Dockerfile
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is an example Dockerfile. It is not intended for PRODUCTION use
+# [START Dockerfile]
+FROM apache/airflow:2.0.1
+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
+# [END Dockerfile]
diff --git a/docs/docker-stack/docker-examples/extending/add-build-essential-extend/Dockerfile b/docs/docker-stack/docker-examples/extending/add-build-essential-extend/Dockerfile
new file mode 100644
index 0000000000000..f0dc0d1e53825
--- /dev/null
+++ b/docs/docker-stack/docker-examples/extending/add-build-essential-extend/Dockerfile
@@ -0,0 +1,28 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is an example Dockerfile. It is not intended for PRODUCTION use
+# [START Dockerfile]
+FROM apache/airflow:2.0.1
+USER root
+RUN apt-get update \
+  && apt-get install -y --no-install-recommends \
+         build-essential libopenmpi-dev \
+  && apt-get autoremove -yqq --purge \
+  && apt-get clean \
+  && rm -rf /var/lib/apt/lists/*
+USER airflow
+RUN pip install --no-cache-dir mpi4py
+# [END Dockerfile]
diff --git a/docs/docker-stack/docker-examples/extending/add-pypi-packages/Dockerfile b/docs/docker-stack/docker-examples/extending/add-pypi-packages/Dockerfile
new file mode 100644
index 0000000000000..401e493bd1cc9
--- /dev/null
+++ b/docs/docker-stack/docker-examples/extending/add-pypi-packages/Dockerfile
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is an example Dockerfile. It is not intended for PRODUCTION use
+# [START Dockerfile]
+FROM apache/airflow:2.0.1
+RUN pip install --no-cache-dir lxml
+# [END Dockerfile]
diff --git a/docs/docker-stack/docker-examples/extending/embedding-dags/Dockerfile b/docs/docker-stack/docker-examples/extending/embedding-dags/Dockerfile
new file mode 100644
index 0000000000000..9213729d72984
--- /dev/null
+++ b/docs/docker-stack/docker-examples/extending/embedding-dags/Dockerfile
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is an example Dockerfile. It is not intended for PRODUCTION use
+# [START Dockerfile]
+FROM apache/airflow:2.0.1
+
+COPY --chown=airflow:root test_dag.py /opt/airflow/dags
+
+# [END Dockerfile]
diff --git a/docs/docker-stack/docker-examples/extending/embedding-dags/test_dag.py b/docs/docker-stack/docker-examples/extending/embedding-dags/test_dag.py
new file mode 100644
index 0000000000000..467c8c3e6539e
--- /dev/null
+++ b/docs/docker-stack/docker-examples/extending/embedding-dags/test_dag.py
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# [START dag]
+"""This dag only runs some simple tasks to test Airflow's task execution."""
+from datetime import datetime, timedelta
+
+from airflow.models.dag import DAG
+from airflow.operators.dummy import DummyOperator
+from airflow.utils.dates import days_ago
+
+now = datetime.now()
+now_to_the_hour = (now - timedelta(0, 0, 0, 0, 0, 3)).replace(minute=0, second=0, microsecond=0)
+START_DATE = now_to_the_hour
+DAG_NAME = 'test_dag_v1'
+
+default_args = {'owner': 'airflow', 'depends_on_past': True, 'start_date': days_ago(2)}
+dag = DAG(DAG_NAME, schedule_interval='*/10 * * * *', default_args=default_args)
+
+run_this_1 = DummyOperator(task_id='run_this_1', dag=dag)
+run_this_2 = DummyOperator(task_id='run_this_2', dag=dag)
+run_this_2.set_upstream(run_this_1)
+run_this_3 = DummyOperator(task_id='run_this_3', dag=dag)
+run_this_3.set_upstream(run_this_2)
+# [END dag]
diff --git a/docs/docker-stack/docker-examples/restricted/restricted_environments.sh b/docs/docker-stack/docker-examples/restricted/restricted_environments.sh
new file mode 100755
index 0000000000000..e7a36994c6cef
--- /dev/null
+++ b/docs/docker-stack/docker-examples/restricted/restricted_environments.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This is an example docker build script. It is not intended for PRODUCTION use
+set -euo pipefail
+AIRFLOW_SOURCES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
+cd "${AIRFLOW_SOURCES}"
+
+# [START download]
+rm docker-context-files/*.whl docker-context-files/*.tar.gz docker-context-files/*.txt || true
+
+curl -Lo "docker-context-files/constraints-3.7.txt" \
+    https://raw.githubusercontent.com/apache/airflow/constraints-2.0.1/constraints-3.7.txt
+
+pip download --dest docker-context-files \
+    --constraint docker-context-files/constraints-3.7.txt  \
+    "apache-airflow[async,aws,azure,celery,dask,elasticsearch,gcp,kubernetes,postgres,redis,slack,ssh,statsd,virtualenv]==2.0.1"
+# [END download]
+
+# [START build]
+docker build . \
+    --build-arg PYTHON_BASE_IMAGE="python:3.7-slim-buster" \
+    --build-arg AIRFLOW_INSTALLATION_METHOD="apache-airflow" \
+    --build-arg AIRFLOW_VERSION="2.0.1" \
+    --build-arg INSTALL_MYSQL_CLIENT="false" \
+    --build-arg AIRFLOW_PRE_CACHED_PIP_PACKAGES="false" \
+    --build-arg INSTALL_FROM_DOCKER_CONTEXT_FILES="true" \
+    --build-arg AIRFLOW_CONSTRAINTS_LOCATION="/docker-context-files/constraints-3.7.txt"
+# [END build]
diff --git a/scripts/ci/images/ci_run_prod_image_test.sh b/scripts/ci/images/ci_run_prod_image_test.sh
new file mode 100755
index 0000000000000..3039eca88ca2d
--- /dev/null
+++ b/scripts/ci/images/ci_run_prod_image_test.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# shellcheck source=scripts/ci/libraries/_initialization.sh
+. "$(dirname "${BASH_SOURCE[0]}")/../libraries/_initialization.sh"
+
+initialization::set_output_color_variables
+
+job_name=$1
+file=$2
+
+set +e
+
+if [[ ${file} == *".sh" ]]; then
+    "${file}"
+    res=$?
+elif [[ ${file} == *"Dockerfile" ]]; then
+    cd "$(dirname "${file}")" || exit 1
+    docker build . --tag "${job_name}"
+    res=$?
+    docker rmi --force "${job_name}"
+else
+    echo "Bad file ${file}. Should be either a Dockerfile or script"
+    exit 1
+fi
+# Print status to status file
+echo "${res}" >"${PARALLEL_JOB_STATUS}"
+
+echo
+# print status to log
+if [[ ${res} == "0" ]]; then
+    echo "${COLOR_GREEN}Extend PROD image test ${job_name} succeeded${COLOR_RESET}"
+else
+    echo "${COLOR_RED}Extend PROD image test ${job_name} failed${COLOR_RESET}"
+fi
+echo
diff --git a/scripts/ci/images/ci_test_examples_of_prod_image_building.sh b/scripts/ci/images/ci_test_examples_of_prod_image_building.sh
new file mode 100755
index 0000000000000..7e045356be116
--- /dev/null
+++ b/scripts/ci/images/ci_test_examples_of_prod_image_building.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# shellcheck source=scripts/ci/libraries/_script_init.sh
+. "$(dirname "${BASH_SOURCE[0]}")/../libraries/_script_init.sh"
+
+SEMAPHORE_NAME="image_tests"
+export SEMAPHORE_NAME
+
+DOCKER_EXAMPLES_DIR=${AIRFLOW_SOURCES}/docs/docker-stack/docker-examples/
+export DOCKER_EXAMPLES_DIR
+
+# Launches parallel building of images. Redirects output to log set the right directories
+# $1 - name of the job
+# $2 - bash file to execute in parallel
+function run_image_test_job() {
+    local file=$1
+
+    local job_name=$2
+    mkdir -p "${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${job_name}"
+    export JOB_LOG="${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${job_name}/stdout"
+    export PARALLEL_JOB_STATUS="${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${job_name}/status"
+    parallel --ungroup --bg --semaphore --semaphorename "${SEMAPHORE_NAME}" \
+        --jobs "${MAX_PARALLEL_IMAGE_JOBS}" \
+            "$(dirname "${BASH_SOURCE[0]}")/ci_run_prod_image_test.sh" "${job_name}" "${file}" >"${JOB_LOG}" 2>&1
+}
+
+
+function test_images() {
+    if [[ ${CI=} == "true" ]]; then
+        echo
+        echo "Skipping the script builds on CI! "
+        echo "They take very long time to build."
+        echo
+    else
+        local scripts_to_test
+        scripts_to_test=$(find "${DOCKER_EXAMPLES_DIR}" -type f -name '*.sh' )
+        for file in ${scripts_to_test}
+        do
+            local job_name
+            job_name=$(basename "${file}")
+            run_image_test_job "${file}" "${job_name}"
+        done
+    fi
+    local dockerfiles_to_test
+    dockerfiles_to_test=$(find "${DOCKER_EXAMPLES_DIR}" -type f -name 'Dockerfile' )
+    for file in ${dockerfiles_to_test}
+    do
+        local job_name
+        job_name="$(basename "$(dirname "${file}")")"
+        run_image_test_job "${file}" "${job_name}"
+    done
+
+}
+
+cd "${AIRFLOW_SOURCES}" || exit 1
+
+docker_engine_resources::get_available_cpus_in_docker
+
+# Building max for images in parlallel helps to conserve docker image space
+MAX_PARALLEL_IMAGE_JOBS=4
+export MAX_PARALLEL_IMAGE_JOBS
+
+parallel::make_sure_gnu_parallel_is_installed
+parallel::kill_stale_semaphore_locks
+parallel::initialize_monitoring
+
+start_end::group_start "Testing image building"
+
+parallel::monitor_progress
+
+test_images
+
+parallel --semaphore --semaphorename "${SEMAPHORE_NAME}" --wait
+start_end::group_end
+
+parallel::print_job_summary_and_return_status_code
diff --git a/scripts/ci/libraries/_build_images.sh b/scripts/ci/libraries/_build_images.sh
index fa11128d94339..55801e2730684 100644
--- a/scripts/ci/libraries/_build_images.sh
+++ b/scripts/ci/libraries/_build_images.sh
@@ -820,6 +820,7 @@ function build_images::prepare_prod_build() {
             "--build-arg" "AIRFLOW_VERSION=${INSTALL_AIRFLOW_VERSION}"
         )
         export AIRFLOW_VERSION="${INSTALL_AIRFLOW_VERSION}"
+        export INSTALL_PROVIDERS_FROM_SOURCES="false"
         build_images::add_build_args_for_remote_install
     else
         # When no airflow version/reference is specified, production image is built either from the
diff --git a/scripts/ci/libraries/_docker_engine_resources.sh b/scripts/ci/libraries/_docker_engine_resources.sh
index 18b223df5df68..b5283b376e17a 100644
--- a/scripts/ci/libraries/_docker_engine_resources.sh
+++ b/scripts/ci/libraries/_docker_engine_resources.sh
@@ -28,24 +28,21 @@ function docker_engine_resources::print_overall_stats() {
 
 
 function docker_engine_resources::get_available_memory_in_docker() {
-    MEMORY_AVAILABLE_FOR_DOCKER=$(docker run --rm --entrypoint /bin/bash \
-        "${AIRFLOW_CI_IMAGE}" -c \
+    MEMORY_AVAILABLE_FOR_DOCKER=$(docker run --rm --entrypoint /bin/bash debian:buster-slim -c \
         'echo $(($(getconf _PHYS_PAGES) * $(getconf PAGE_SIZE) / (1024 * 1024)))')
     echo "${COLOR_BLUE}Memory available for Docker${COLOR_RESET}: $(numfmt --to iec $((MEMORY_AVAILABLE_FOR_DOCKER * 1024 * 1024)))"
     export MEMORY_AVAILABLE_FOR_DOCKER
 }
 
 function docker_engine_resources::get_available_cpus_in_docker() {
-    CPUS_AVAILABLE_FOR_DOCKER=$(docker run --rm --entrypoint /bin/bash \
-        "${AIRFLOW_CI_IMAGE}" -c \
+    CPUS_AVAILABLE_FOR_DOCKER=$(docker run --rm --entrypoint /bin/bash debian:buster-slim -c \
         'grep -cE "cpu[0-9]+" </proc/stat')
     echo "${COLOR_BLUE}CPUS available for Docker${COLOR_RESET}: ${CPUS_AVAILABLE_FOR_DOCKER}"
     export CPUS_AVAILABLE_FOR_DOCKER
 }
 
 function docker_engine_resources::get_available_disk_space_in_docker() {
-    DISK_SPACE_AVAILABLE_FOR_DOCKER=$(docker run --rm --entrypoint /bin/bash \
-        "${AIRFLOW_CI_IMAGE}" -c \
+    DISK_SPACE_AVAILABLE_FOR_DOCKER=$(docker run --rm --entrypoint /bin/bash debian:buster-slim -c \
         'df  / | tail -1 | awk '\''{print $4}'\')
     echo "${COLOR_BLUE}Disk space available for Docker${COLOR_RESET}: $(numfmt --to iec $((DISK_SPACE_AVAILABLE_FOR_DOCKER * 1024)))"
     export DISK_SPACE_AVAILABLE_FOR_DOCKER
diff --git a/scripts/ci/libraries/_initialization.sh b/scripts/ci/libraries/_initialization.sh
index 66923a6cac037..310d6be7e5084 100644
--- a/scripts/ci/libraries/_initialization.sh
+++ b/scripts/ci/libraries/_initialization.sh
@@ -193,6 +193,7 @@ function initialization::initialize_files_for_rebuild_check() {
         "Dockerfile.ci"
         ".dockerignore"
         "scripts/docker/compile_www_assets.sh"
+        "scripts/docker/common.sh"
         "scripts/docker/install_additional_dependencies.sh"
         "scripts/docker/install_airflow.sh"
         "scripts/docker/install_airflow_from_branch_tip.sh"
diff --git a/scripts/ci/libraries/_parallel.sh b/scripts/ci/libraries/_parallel.sh
index 09c3121189111..dfe1c4daf3f4d 100644
--- a/scripts/ci/libraries/_parallel.sh
+++ b/scripts/ci/libraries/_parallel.sh
@@ -16,12 +16,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
+
+# Require SEMAPHORE_NAME
+
 function parallel::initialize_monitoring() {
     PARALLEL_MONITORED_DIR="$(mktemp -d)"
     export PARALLEL_MONITORED_DIR
-
-    PARALLEL_JOBLOG="$(mktemp)"
-    export PARALLEL_JOBLOG
 }
 
 function parallel::make_sure_gnu_parallel_is_installed() {
@@ -53,6 +53,7 @@ function parallel::kill_stale_semaphore_locks() {
 
 # Periodical loop to print summary of all the processes run by parallel
 function parallel::monitor_loop() {
+    trap 'exit 0' TERM
     echo
     echo "Start monitoring of parallel execution in ${PARALLEL_MONITORED_DIR} directory."
     echo
@@ -79,16 +80,13 @@ function parallel::monitor_loop() {
             echo
         done
         echo
-        echo "${COLOR_YELLOW}########### Monitoring progress end: ${progress_report_number} #################${COLOR_RESET}}"
+        echo "${COLOR_YELLOW}########### Monitoring progress end: ${progress_report_number} #################${COLOR_RESET}"
         echo
         end_time=${SECONDS}
         echo "${COLOR_YELLOW}############## $((end_time - start_time)) seconds passed since start ####################### ${COLOR_RESET}"
         sleep 10
         progress_report_number=$((progress_report_number + 1))
     done
-    echo "${COLOR_BLUE}########### STATISTICS #################"
-    docker_engine_resources::print_overall_stats
-    echo "########### STATISTICS #################${COLOR_RESET}"
 }
 
 # Monitors progress of parallel execution and periodically summarizes stdout entries created by
@@ -96,8 +94,6 @@ function parallel::monitor_loop() {
 # parameter to GNU parallel execution.
 function parallel::monitor_progress() {
     echo "Parallel results are stored in: ${PARALLEL_MONITORED_DIR}"
-    echo "Parallel joblog is stored in: ${PARALLEL_JOBLOG}"
-
     parallel::monitor_loop 2>/dev/null &
 
     # shellcheck disable=SC2034
@@ -108,5 +104,59 @@ function parallel::monitor_progress() {
 
 
 function parallel::kill_monitor() {
-    kill -9 ${PARALLEL_MONITORING_PID} >/dev/null 2>&1 || true
+    kill ${PARALLEL_MONITORING_PID} >/dev/null 2>&1 || true
+}
+
+# Outputs logs for successful test type
+# $1 test type
+function parallel::output_log_for_successful_job(){
+    local job=$1
+    local log_dir="${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${job}"
+    start_end::group_start "${COLOR_GREEN}Output for successful ${job}${COLOR_RESET}"
+    echo "${COLOR_GREEN}##### The ${job} succeeded ##### ${COLOR_RESET}"
+    echo
+    cat "${log_dir}"/stdout
+    echo
+    echo "${COLOR_GREEN}##### The ${job} succeeded ##### ${COLOR_RESET}"
+    echo
+    start_end::group_end
+}
+
+# Outputs logs for failed test type
+# $1 test type
+function parallel::output_log_for_failed_job(){
+    local job=$1
+    local log_dir="${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${job}"
+    start_end::group_start "${COLOR_RED}Output: for failed ${job}${COLOR_RESET}"
+    echo "${COLOR_RED}##### The ${job} failed ##### ${COLOR_RESET}"
+    echo
+    cat "${log_dir}"/stdout
+    echo
+    echo
+    echo "${COLOR_RED}##### The ${job} failed ##### ${COLOR_RESET}"
+    echo
+    start_end::group_end
+}
+
+# Prints summary of jobs and returns status:
+# 0 - all jobs succeeded (SKIPPED_FAILED_JOBS is not counted)
+# >0 - number of failed jobs (except Quarantine)
+function parallel::print_job_summary_and_return_status_code() {
+    local return_code="0"
+    local job
+    for job_path in "${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/"*
+    do
+        job="$(basename "${job_path}")"
+        status=$(cat "${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${job}/status")
+        if [[ ${status} == "0" ]]; then
+            parallel::output_log_for_successful_job "${job}"
+        else
+            parallel::output_log_for_failed_job "${job}"
+            # SKIPPED_FAILED_JOB failure does not trigger whole test failure
+            if [[ ${SKIPPED_FAILED_JOB=} != "${job}" ]]; then
+                return_code=$((return_code + 1))
+            fi
+        fi
+    done
+    return "${return_code}"
 }
diff --git a/scripts/ci/testing/ci_run_airflow_testing.sh b/scripts/ci/testing/ci_run_airflow_testing.sh
index 1cd1c3682e026..8286874557990 100755
--- a/scripts/ci/testing/ci_run_airflow_testing.sh
+++ b/scripts/ci/testing/ci_run_airflow_testing.sh
@@ -20,6 +20,9 @@
 RUN_TESTS="true"
 export RUN_TESTS
 
+SKIPPED_FAILED_JOB="Quarantined"
+export SKIPPED_FAILED_JOB
+
 # shellcheck source=scripts/ci/libraries/_script_init.sh
 . "$( dirname "${BASH_SOURCE[0]}" )/../libraries/_script_init.sh"
 
@@ -167,58 +170,6 @@ function run_test_types_in_parallel() {
     start_end::group_end
 }
 
-# Outputs logs for successful test type
-# $1 test type
-function output_log_for_successful_test_type(){
-    local test_type=$1
-    local log_dir="${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${test_type}"
-    start_end::group_start "${COLOR_GREEN}Output for successful ${test_type}${COLOR_RESET}"
-    echo "${COLOR_GREEN}##### Test type ${test_type} succeeded ##### ${COLOR_RESET}"
-    echo
-    cat "${log_dir}"/stdout
-    echo
-    echo "${COLOR_GREEN}##### Test type ${test_type} succeeded ##### ${COLOR_RESET}"
-    echo
-    start_end::group_end
-}
-
-# Outputs logs for failed test type
-# $1 test type
-function output_log_for_failed_test_type(){
-    local test_type=$1
-    local log_dir="${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${test_type}"
-    start_end::group_start "${COLOR_RED}Output: for failed ${test_type}${COLOR_RESET}"
-    echo "${COLOR_RED}##### Test type ${test_type} failed ##### ${COLOR_RESET}"
-    echo
-    cat "${log_dir}"/stdout
-    echo
-    echo
-    echo "${COLOR_RED}##### Test type ${test_type} failed ##### ${COLOR_RESET}"
-    echo
-    start_end::group_end
-}
-
-# Prints summary of tests and returns status:
-# 0 - all test types succeeded (Quarantine is not counted)
-# >0 - number of failed test types (except Quarantine)
-function print_test_summary_and_return_test_status_code() {
-    local return_code="0"
-    local test_type
-    for test_type in ${TEST_TYPES}
-    do
-        status=$(cat "${PARALLEL_MONITORED_DIR}/${SEMAPHORE_NAME}/${test_type}/status")
-        if [[ ${status} == "0" ]]; then
-            output_log_for_successful_test_type "${test_type}"
-        else
-            output_log_for_failed_test_type "${test_type}"
-            # Quarantined tests failure does not trigger whole test failure
-            if [[ ${TEST_TYPE} != "Quarantined" ]]; then
-                return_code=$((return_code + 1))
-            fi
-        fi
-    done
-    return "${return_code}"
-}
 
 export MEMORY_REQUIRED_FOR_INTEGRATION_TEST_PARALLEL_RUN=33000
 
@@ -236,8 +187,6 @@ export MEMORY_REQUIRED_FOR_INTEGRATION_TEST_PARALLEL_RUN=33000
 #   * MEMORY_AVAILABLE_FOR_DOCKER - memory that is available in docker (set by cleanup_runners)
 #
 function run_all_test_types_in_parallel() {
-    local test_type
-
     cleanup_runner
 
     start_end::group_start "Determine how to run the tests"
@@ -278,7 +227,7 @@ function run_all_test_types_in_parallel() {
     fi
     set -e
     # this will exit with error code in case some of the non-Quarantined tests failed
-    print_test_summary_and_return_test_status_code
+    parallel::print_job_summary_and_return_status_code
 }
 
 build_images::prepare_ci_build
diff --git a/scripts/docker/common.sh b/scripts/docker/common.sh
new file mode 100755
index 0000000000000..28307e3333050
--- /dev/null
+++ b/scripts/docker/common.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+set -euo pipefail
+
+test -v INSTALL_MYSQL_CLIENT
+test -v AIRFLOW_INSTALL_USER_FLAG
+test -v AIRFLOW_REPO
+test -v AIRFLOW_BRANCH
+test -v AIRFLOW_PIP_VERSION
+
+set -x
+
+function common::get_airflow_version_specification() {
+    if [[ -z ${AIRFLOW_VERSION_SPECIFICATION}
+        && -n ${AIRFLOW_VERSION}
+        && ${AIRFLOW_INSTALLATION_METHOD} != "." ]]; then
+        AIRFLOW_VERSION_SPECIFICATION="==${AIRFLOW_VERSION}"
+    fi
+}
+
+function common::get_constraints_location() {
+    # auto-detect Airflow-constraint reference and location
+    if [[ -z "${AIRFLOW_CONSTRAINTS_REFERENCE}" ]]; then
+        if [[ ${AIRFLOW_VERSION} =~ [^0-9]*1[^0-9]*10[^0-9]([0-9]*) ]]; then
+            # All types of references/versions match this regexp for 1.10 series
+            # for example v1_10_test, 1.10.10, 1.10.9 etc. ${BASH_REMATCH[1]} matches last
+            # minor digit of version and it's length is 0 for v1_10_test, 1 for 1.10.9 and 2 for 1.10.10+
+            AIRFLOW_MINOR_VERSION_NUMBER=${BASH_REMATCH[1]}
+            if [[ ${#AIRFLOW_MINOR_VERSION_NUMBER} == "0" ]]; then
+                # For v1_10_* branches use constraints-1-10 branch
+                AIRFLOW_CONSTRAINTS_REFERENCE=constraints-1-10
+            else
+                AIRFLOW_CONSTRAINTS_REFERENCE=constraints-${AIRFLOW_VERSION}
+            fi
+        elif  [[ ${AIRFLOW_VERSION} =~ v?2.* ]]; then
+            AIRFLOW_CONSTRAINTS_REFERENCE=constraints-${AIRFLOW_VERSION}
+        else
+            AIRFLOW_CONSTRAINTS_REFERENCE=${DEFAULT_CONSTRAINTS_BRANCH}
+        fi
+    fi
+
+    if [[ -z ${AIRFLOW_CONSTRAINTS_LOCATION} ]]; then
+        local constraints_base="https://raw.githubusercontent.com/${CONSTRAINTS_GITHUB_REPOSITORY}/${AIRFLOW_CONSTRAINTS_REFERENCE}"
+        local python_version
+        python_version="$(python --version 2>/dev/stdout | cut -d " " -f 2 | cut -d "." -f 1-2)"
+        AIRFLOW_CONSTRAINTS_LOCATION="${constraints_base}/${AIRFLOW_CONSTRAINTS}-${python_version}.txt"
+    fi
+}
diff --git a/scripts/docker/compile_www_assets.sh b/scripts/docker/compile_www_assets.sh
index 04157b6bb3ea4..e303f51b9a626 100755
--- a/scripts/docker/compile_www_assets.sh
+++ b/scripts/docker/compile_www_assets.sh
@@ -17,9 +17,6 @@
 # under the License.
 # shellcheck disable=SC2086
 set -euo pipefail
-
-test -v PYTHON_MAJOR_MINOR_VERSION
-
 set -x
 
 # Installs additional dependencies passed as Argument to the Docker build command
@@ -31,7 +28,7 @@ function compile_www_assets() {
     md5sum_file="static/dist/sum.md5"
     readonly md5sum_file
     local airflow_site_package
-    airflow_site_package="/root/.local/lib/python${PYTHON_MAJOR_MINOR_VERSION}/site-packages/airflow"
+    airflow_site_package="$(python -m site --user-site)"
     local www_dir=""
     if [[ -f "${airflow_site_package}/www_rbac/package.json" ]]; then
         www_dir="${airflow_site_package}/www_rbac"
diff --git a/scripts/docker/install_airflow.sh b/scripts/docker/install_airflow.sh
index 5f1e9d9c612b6..bfcc7e91b5549 100755
--- a/scripts/docker/install_airflow.sh
+++ b/scripts/docker/install_airflow.sh
@@ -26,17 +26,8 @@
 #                                 dependencies (with EAGER_UPGRADE_ADDITIONAL_REQUIREMENTS added)
 #
 # shellcheck disable=SC2086
-set -euo pipefail
-
-test -v AIRFLOW_INSTALLATION_METHOD
-test -v AIRFLOW_INSTALL_EDITABLE_FLAG
-test -v AIRFLOW_INSTALL_USER_FLAG
-test -v INSTALL_MYSQL_CLIENT
-test -v UPGRADE_TO_NEWER_DEPENDENCIES
-test -v CONTINUE_ON_PIP_CHECK_FAILURE
-test -v AIRFLOW_CONSTRAINTS_LOCATION
-
-set -x
+# shellcheck source=scripts/docker/common.sh
+. "$( dirname "${BASH_SOURCE[0]}" )/common.sh"
 
 function install_airflow() {
     # Sanity check for editable installation mode.
@@ -87,6 +78,11 @@ function install_airflow() {
         pip install ${AIRFLOW_INSTALL_USER_FLAG} --upgrade "pip==${AIRFLOW_PIP_VERSION}"
         pip check || ${CONTINUE_ON_PIP_CHECK_FAILURE}
     fi
+
 }
 
+common::get_airflow_version_specification
+
+common::get_constraints_location
+
 install_airflow
diff --git a/scripts/docker/install_airflow_from_branch_tip.sh b/scripts/docker/install_airflow_from_branch_tip.sh
index 3741055878f10..6e34d05159b43 100755
--- a/scripts/docker/install_airflow_from_branch_tip.sh
+++ b/scripts/docker/install_airflow_from_branch_tip.sh
@@ -26,16 +26,9 @@
 #
 # If INSTALL_MYSQL_CLIENT is set to false, mysql extra is removed
 #
-set -euo pipefail
+# shellcheck source=scripts/docker/common.sh
+. "$( dirname "${BASH_SOURCE[0]}" )/common.sh"
 
-test -v INSTALL_MYSQL_CLIENT
-test -v AIRFLOW_INSTALL_USER_FLAG
-test -v AIRFLOW_REPO
-test -v AIRFLOW_BRANCH
-test -v AIRFLOW_CONSTRAINTS_LOCATION
-test -v AIRFLOW_PIP_VERSION
-
-set -x
 
 function install_airflow_from_branch_tip() {
     echo
@@ -57,4 +50,6 @@ function install_airflow_from_branch_tip() {
     pip uninstall --yes apache-airflow
 }
 
+common::get_constraints_location
+
 install_airflow_from_branch_tip
diff --git a/scripts/docker/install_from_docker_context_files.sh b/scripts/docker/install_from_docker_context_files.sh
index 48aa933a8c603..d1982cf771bc0 100755
--- a/scripts/docker/install_from_docker_context_files.sh
+++ b/scripts/docker/install_from_docker_context_files.sh
@@ -22,19 +22,13 @@
 # The packages are prepared from current sources and placed in the 'docker-context-files folder
 # Then both airflow and provider packages are installed using those packages rather than
 # PyPI
-set -euo pipefail
-
-test -v AIRFLOW_EXTRAS
-test -v AIRFLOW_INSTALL_USER_FLAG
-test -v AIRFLOW_CONSTRAINTS_LOCATION
-test -v AIRFLOW_PIP_VERSION
-test -v CONTINUE_ON_PIP_CHECK_FAILURE
-test -v EAGER_UPGRADE_ADDITIONAL_REQUIREMENTS
-test -v UPGRADE_TO_NEWER_DEPENDENCIES
-
-set -x
+# shellcheck source=scripts/docker/common.sh
+. "$( dirname "${BASH_SOURCE[0]}" )/common.sh"
 
 function install_airflow_and_providers_from_docker_context_files(){
+    if [[ ${INSTALL_MYSQL_CLIENT} != "true" ]]; then
+        AIRFLOW_EXTRAS=${AIRFLOW_EXTRAS/mysql,}
+    fi
     # Find Apache Airflow packages in docker-context files
     local reinstalling_apache_airflow_package
     reinstalling_apache_airflow_package=$(ls \
@@ -68,8 +62,12 @@ function install_airflow_and_providers_from_docker_context_files(){
         echo
         echo Force re-installing airflow and providers from local files with constraints and upgrade if needed
         echo
-        # Remove provider packages from constraint files because they are locally prepared
-        curl -L "${AIRFLOW_CONSTRAINTS_LOCATION}" | grep -ve '^apache-airflow' > /tmp/constraints.txt
+        if [[ ${AIRFLOW_CONSTRAINTS_LOCATION} == "/"* ]]; then
+            grep -ve '^apache-airflow' <"${AIRFLOW_CONSTRAINTS_LOCATION}" > /tmp/constraints.txt
+        else
+            # Remove provider packages from constraint files because they are locally prepared
+            curl -L "${AIRFLOW_CONSTRAINTS_LOCATION}" | grep -ve '^apache-airflow' > /tmp/constraints.txt
+        fi
         # force reinstall airflow + provider package local files with constraints + upgrade if needed
         pip install ${AIRFLOW_INSTALL_USER_FLAG} --force-reinstall \
             ${reinstalling_apache_airflow_package} ${reinstalling_apache_airflow_providers_packages} \
@@ -106,5 +104,7 @@ install_all_other_packages_from_docker_context_files() {
     fi
 }
 
+common::get_constraints_location
+
 install_airflow_and_providers_from_docker_context_files
 install_all_other_packages_from_docker_context_files
