Prefect is a workflow orchestration framework for building resilient data pipelines in Python.
docker pull PrefectHQ/prefect:latest
docker run -d --name prefect --restart always prefecthq/prefect:latest
podman run -d --name prefect --restart always prefecthq/prefect:latest
skopeo inspect docker://docker.io/prefecthq/prefect:latest
oras pull prefecthq/prefect:latest
# The version of Python in the final image
ARG PYTHON_VERSION=3.11
# The base image to use for the final image; Prefect and its Python requirements will
# be installed in this image. The default is the official Python slim image.
# The following images are also available in this file:
# prefect-conda: Derivative of continuum/miniconda3 with a 'prefect' environment. Used for the 'conda' flavor.
# Any image tag can be used, but it must have apt and pip.
ARG BASE_IMAGE=python:${PYTHON_VERSION}-slim
# The version used to build the Python distributable.
ARG BUILD_PYTHON_VERSION=3.11
# The version used to build the V1 UI distributable.
ARG NODE_VERSION=22.23.4
# The version used to build the V2 UI distributable (requires Node 22+).
ARG NODE_V2_VERSION=22.23.4
# SQLite version โ must match the tag published to prefecthq/prefect-sqlite on DockerHub
# See Dockerfile.sqlite-builder and .github/workflows/sqlite-builder.yaml
ARG SQLITE_VERSION=3.50.4
# Any extra Python requirements to install
ARG EXTRA_PIP_PACKAGES=""
# Pull pre-compiled SQLite binaries (built by .github/workflows/sqlite-builder.yaml).
# This avoids compiling SQLite from source on every build.
# To publish a new version: bump SQLITE_VERSION and run the sqlite-builder workflow.
FROM prefecthq/prefect-sqlite:${SQLITE_VERSION} AS sqlite-builder
# Build the V1 UI distributable.
FROM --platform=$BUILDPLATFORM node:${NODE_VERSION}-bookworm-slim AS ui-builder
WORKDIR /opt/ui
RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get upgrade --no-install-recommends -y && \
apt-get install --no-install-recommends -y \
# Required for arm64 builds
chromium \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install dependencies separately so they cache
COPY ./ui/package*.json ./
RUN npm ci --legacy-peer-deps
# Build static UI files
COPY ./ui .
RUN npm run build
# Build the V2 UI distributable.
FROM --platform=$BUILDPLATFORM node:${NODE_V2_VERSION}-bookworm-slim AS ui-v2-builder
# Optional Amplitude API key for analytics (build still works without it)
ARG VITE_AMPLITUDE_API_KEY=""
ENV VITE_AMPLITUDE_API_KEY=$VITE_AMPLITUDE_API_KEY
WORKDIR /opt/ui-v2
RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get upgrade --no-install-recommends -y && \
apt-get install --no-install-recommends -y \
# Required for arm64 builds
chromium \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install dependencies separately so they cache
COPY ./ui-v2/package*.json ./
RUN npm ci --legacy-peer-deps
# Build static UI files
COPY ./ui-v2 .
RUN npm run build
# Build the Python distributable.
# Without this build step, versioningit cannot infer the version without git
FROM python:${BUILD_PYTHON_VERSION}-slim AS python-builder
WORKDIR /opt/prefect
RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get upgrade --no-install-recommends -y && \
apt-get install --no-install-recommends -y \
gpg \
git=1:2.* \
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
&& dpkg --compare-versions "$(dpkg-query -W -f='${Version}' git)" ge '1:2.47.3' || \
(echo "ERROR: git version must be >= 1:2.47.3" && exit 1)
# Install UV from official image - pin to specific version for build caching
COPY --from=ghcr.io/astral-sh/uv:0.13.4 /uv /bin/uv
# Copy the repository in; requires full git history for versions to generate correctly
COPY . ./
# Package the V1 UI into the distributable.
COPY --from=ui-builder /opt/ui/dist ./src/prefect/server/ui
# Package the V2 UI into the distributable.
COPY --from=ui-v2-builder /opt/ui-v2/dist ./src/prefect/server/ui-v2
# Create a source distributable archive; ensuring existing dists are removed first
RUN rm -rf dist && PREFECT_REQUIRE_PACKAGED_UI_BUNDLES=1 uv build --sdist --out-dir dist
RUN mv "dist/prefect-"*".tar.gz" "dist/prefect.tar.gz"
# Export the locked versions so the final image installs what CI tests against
# (`uv sync --locked`) instead of whatever PyPI resolves to on build day. All
# extras are exported so one file covers every PREFECT_EXTRAS flavor.
RUN uv export --frozen --no-dev --all-extras --no-emit-project --no-hashes -o dist/constraints.txt
# Setup a base final image from miniconda
FROM continuumio/miniconda3:26.7.1 AS prefect-conda
# Create a new conda environment with our required Python version
ARG PYTHON_VERSION
RUN conda create \
python=${PYTHON_VERSION} \
--name prefect
# Use the prefect environment by default
RUN echo "conda activate prefect" >> ~/.bashrc
SHELL ["/bin/bash", "--login", "-c"]
# Build the final image with Prefect installed and our entrypoint configured
FROM ${BASE_IMAGE} AS final
# Redeclare ARGs needed in this stage
ARG BASE_IMAGE
ARG PYTHON_VERSION
ARG SQLITE_VERSION
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
ENV UV_LINK_MODE=copy
ENV UV_SYSTEM_PYTHON=1
# Ensure Python uses the upgraded SQLite library
ENV LD_LIBRARY_PATH=/usr/local/lib
LABEL maintainer="[email protected]" \
io.prefect.python-version=${PYTHON_VERSION} \
io.prefect.sqlite-version=${SQLITE_VERSION} \
org.label-schema.schema-version="1.0" \
org.label-schema.name="prefect" \
org.label-schema.url="https://www.prefect.io/"
WORKDIR /opt/prefect
# Install system requirements
# For Debian Bookworm (used by conda base), we need to add Trixie sources to get git >= 2.47.3
# This is because miniconda3 images are still based on Bookworm which only has git ~2.39
# We install tini and build-essential first (from the base distro), then handle git separately
RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get upgrade --no-install-recommends -y && \
apt-get install --no-install-recommends -y \
tini=0.19.* \
build-essential \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install git - for Bookworm, we need to use Trixie sources since git >= 2.47.3 is required
# and Bookworm only provides ~2.39. We allow apt to pull git's dependencies from Trixie as needed.
RUN DEBIAN_VERSION=$(. /etc/os-release && echo $VERSION_CODENAME) && \
if [ "$DEBIAN_VERSION" = "bookworm" ]; then \
echo "deb http://deb.debian.org/debian trixie main" > /etc/apt/sources.list.d/trixie.list; \
apt-get update; \
apt-get install --no-install-recommends -y -t trixie git; \
rm -f /etc/apt/sources.list.d/trixie.list; \
else \
apt-get update; \
apt-get install --no-install-recommends -y git; \
fi \
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
&& dpkg --compare-versions "$(dpkg-query -W -f='${Version}' git)" ge '1:2.47.3' || \
(echo "ERROR: git version must be >= 1:2.47.3" && exit 1)
# Copy pre-compiled SQLite ${SQLITE_VERSION} from sqlite-builder stage
COPY --from=sqlite-builder /usr/local/lib/libsqlite3* /usr/local/lib/
COPY --from=sqlite-builder /usr/local/include/sqlite3*.h /usr/local/include/
COPY --from=sqlite-builder /usr/local/bin/sqlite3 /usr/local/bin/
COPY --from=sqlite-builder /usr/local/lib/pkgconfig/sqlite3.pc /usr/local/lib/pkgconfig/
RUN ldconfig
# Install UV from official image - pin to specific version for build caching
COPY --from=ghcr.io/astral-sh/uv:0.13.4 /uv /bin/uv
# Extras to include during installation
ARG PREFECT_EXTRAS=[redis,client,otel]
# Install prefect from the sdist.
#
# The sdist is bind-mounted rather than copied. A COPY commits the archive to its
# own layer, and a `rm -rf` in a later RUN can only add a whiteout on top of that
# layer -- it cannot remove it from the image, so the archive ships in everything
# we push. A bind mount is never committed to a layer.
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,from=python-builder,source=/opt/prefect/dist,target=/dist \
UV_COMPILE_BYTECODE=1 uv pip install --constraint /dist/constraints.txt \
"/dist/prefect.tar.gz${PREFECT_EXTRAS:-""}"
# Setuptools is required by pip in the conda environment. Remove it only from
# base images where it is not managed as part of the environment.
RUN if [ "${BASE_IMAGE}" != "prefect-conda" ]; then \
uv pip uninstall setuptools; \
fi
# Install any extra packages
ARG EXTRA_PIP_PACKAGES
RUN --mount=type=cache,target=/root/.cache/uv \
[ -z "${EXTRA_PIP_PACKAGES:-""}" ] || UV_COMPILE_BYTECODE=1 uv pip install "${EXTRA_PIP_PACKAGES}"
# Smoke test
RUN prefect version
# Setup entrypoint
COPY scripts/entrypoint.sh ./entrypoint.sh
ENTRYPOINT ["/usr/bin/tini", "-g", "--", "/opt/prefect/entrypoint.sh"]