distroless
distroless copied to clipboard
support different python versions (3.12, 3.13)
Hello, is there any more allegiant solution for using python 3.12, 3.13 than https://github.com/GoogleContainerTools/distroless/issues/1543#issuecomment-2432044825 ?
now i do understand nothing about bazel to do it myself.
Maybe you can recommend some step by step documentation how to do it? readme doesnt help me
thank
There's are a few suggestions in other issues. But it's not really our mission to provide this.
Not sure if more elegant, but I ended up copying binaries and libs into Distroless CC from the official Python image, which also serves as local development image.
In my case production is always amd64 anyway, so didn't bother to make it work with arm.
I understand if the Distroless team doesn't want to maintain multiple Python images, but would be good to document which version it is (3.11.2) so people at least know what to expect.
# Development dependencies stage
FROM python:3.12.7-bookworm as deps
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
WORKDIR /projects/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
ENTRYPOINT ["/bin/bash"]
# Production stage using CC Distroless
FROM --platform=linux/amd64 gcr.io/distroless/cc-debian12:nonroot AS prod
ARG PYTHON_VERSION=3.12
ENV PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages
ENV PATH="/usr/local/bin:${PATH}"
ENV LD_LIBRARY_PATH=/usr/local/lib:/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}
WORKDIR /projects/app
# Copy Python dependencies from deps stage
COPY --from=deps /usr/local/lib/python${PYTHON_VERSION}/site-packages /usr/local/lib/python${PYTHON_VERSION}/site-packages
# Copy the Python interpreter with a specific name
COPY --from=deps /usr/local/bin/python${PYTHON_VERSION} /usr/local/bin/pythonapp
# Copy Python library files including shared libraries
COPY --from=deps /usr/local/lib/python${PYTHON_VERSION} /usr/local/lib/python${PYTHON_VERSION}
COPY --from=deps /usr/local/lib/libpython${PYTHON_VERSION}.so* /usr/local/lib/
# Copy system libraries for x86_64
COPY --from=deps /lib/x86_64-linux-gnu/lib* /lib/x86_64-linux-gnu/
# Copy application code
COPY . ./
USER nonroot
ENTRYPOINT ["/usr/local/bin/pythonapp"]
CMD ["/projects/app/main.py"]
Not sure if more elegant, but I ended up copying binaries and libs into Distroless CC from the official Python image, which also serves as local development image.
In my case production is always amd64 anyway, so didn't bother to make it work with arm.
I understand if the Distroless team doesn't want to maintain multiple Python images, but would be good to document which version it is (3.11.2) so people at least know what to expect.
# Development dependencies stage FROM python:3.12.7-bookworm as deps SHELL ["/bin/bash", "-o", "pipefail", "-c"] WORKDIR /projects/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt ENTRYPOINT ["/bin/bash"] # Production stage using CC Distroless FROM --platform=linux/amd64 gcr.io/distroless/cc-debian12:nonroot AS prod ARG PYTHON_VERSION=3.12 ENV PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages ENV PATH="/usr/local/bin:${PATH}" ENV LD_LIBRARY_PATH=/usr/local/lib:/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-} WORKDIR /projects/app # Copy Python dependencies from deps stage COPY --from=deps /usr/local/lib/python${PYTHON_VERSION}/site-packages /usr/local/lib/python${PYTHON_VERSION}/site-packages # Copy the Python interpreter with a specific name COPY --from=deps /usr/local/bin/python${PYTHON_VERSION} /usr/local/bin/pythonapp # Copy Python library files including shared libraries COPY --from=deps /usr/local/lib/python${PYTHON_VERSION} /usr/local/lib/python${PYTHON_VERSION} COPY --from=deps /usr/local/lib/libpython${PYTHON_VERSION}.so* /usr/local/lib/ # Copy system libraries for x86_64 COPY --from=deps /lib/x86_64-linux-gnu/lib* /lib/x86_64-linux-gnu/ # Copy application code COPY . ./ USER nonroot ENTRYPOINT ["/usr/local/bin/pythonapp"] CMD ["/projects/app/main.py"]
ty also i can find a different way to do same
FROM python:3.13-slim-bookworm AS builder
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PYTHON_INSTALL_DIR=/python
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
RUN uv python install 3.13
WORKDIR /app
RUN --mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-dev --no-cache --no-editable --no-install-project --python-preference only-managed
ADD . /app
# Then, use a final image without uv
FROM --platform=linux/amd64 gcr.io/distroless/cc-debian12:nonroot as prod
ARG PYTHON_VERSION=3.13
ENV PATH="/usr/local/bin:${PATH}"
# Copy system libraries for x86_64
COPY --from=builder /lib/x86_64-linux-gnu/lib* /lib/x86_64-linux-gnu/
COPY --from=builder --chown=python:python /python /python
# Copy the application from the builder
COPY --from=builder --chown=app:app /app /app
# Place executables in the environment at the front of the path
ENV PATH="/app/.venv/bin:$PATH"
# Run the FastAPI application by default
ENTRYPOINT ["granian", "--interface", "asgi", "/app/main:app", "--port", "8000", "--host", "0.0.0.0"]
Oh cool, so by installing Python with uv you can get all the binaries and libraries under one directory (UV_PYTHON_INSTALL_DIR) so it's fewer paths to copy? It's probably much less disk space too because you only get stuff that Python will actually use?
Do you even need the Python image for the builder or a C (or even base Debian) one might be enough because you're copying uv from another image anyway?
I just realised that my Dockerfile might need another stage as currently the dev dependencies will get copied into the prod image too 🤔
im not sure will need investigate.
Oh cool, so by installing Python with uv you can get all the binaries and libraries under one directory (UV_PYTHON_INSTALL_DIR) so it's fewer paths to copy? It's probably much less disk space too because you only get stuff that Python will actually use?
Do you even need the Python image for the builder or a C (or even base Debian) one might be enough because you're copying uv from another image anyway?
I just realised that my Dockerfile might need another stage as currently the dev dependencies will get copied into the prod image too 🤔
Not sure if more elegant, but I ended up copying binaries and libs into Distroless CC from the official Python image, which also serves as local development image.
In my case production is always amd64 anyway, so didn't bother to make it work with arm.
I understand if the Distroless team doesn't want to maintain multiple Python images, but would be good to document which version it is (3.11.2) so people at least know what to expect.
# Development dependencies stage FROM python:3.12.7-bookworm as deps SHELL ["/bin/bash", "-o", "pipefail", "-c"] WORKDIR /projects/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt ENTRYPOINT ["/bin/bash"] # Production stage using CC Distroless FROM --platform=linux/amd64 gcr.io/distroless/cc-debian12:nonroot AS prod ARG PYTHON_VERSION=3.12 ENV PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages ENV PATH="/usr/local/bin:${PATH}" ENV LD_LIBRARY_PATH=/usr/local/lib:/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-} WORKDIR /projects/app # Copy Python dependencies from deps stage COPY --from=deps /usr/local/lib/python${PYTHON_VERSION}/site-packages /usr/local/lib/python${PYTHON_VERSION}/site-packages # Copy the Python interpreter with a specific name COPY --from=deps /usr/local/bin/python${PYTHON_VERSION} /usr/local/bin/pythonapp # Copy Python library files including shared libraries COPY --from=deps /usr/local/lib/python${PYTHON_VERSION} /usr/local/lib/python${PYTHON_VERSION} COPY --from=deps /usr/local/lib/libpython${PYTHON_VERSION}.so* /usr/local/lib/ # Copy system libraries for x86_64 COPY --from=deps /lib/x86_64-linux-gnu/lib* /lib/x86_64-linux-gnu/ # Copy application code COPY . ./ USER nonroot ENTRYPOINT ["/usr/local/bin/pythonapp"] CMD ["/projects/app/main.py"]
Thank you it worked for me! But do you think there's a way to customize the BUILD file for the original distroless python to adapt it to an higher python version like 3.12? I'm trying to use this distroless python image, because it's smaller than your cc version. Thank you!
Thank you it worked for me! But do you think there's a way to customize the BUILD file for the original distroless python to adapt it to an higher python version like 3.12? I'm trying to use this distroless python image, because it's smaller than your cc version. Thank you!
I'm not sure if I understood the question, but the purpose of this setup is that you can use different Python versions in the first stage (either by changing the version of the base image in my solution or downloading a different one with uv in gulldan's version) and then copy those binaries into the distroless image. I don't know if there can be a problem with the C version being different in the distroless C/C++ image to the one in the builder image, you probably need to align the images so there aren't several years between when they were created.
Interested by this too, because we have some requirement on python3.12 and distroless image only runs 3.11 atm.
Solutions are interesting but we have to support both x86_64 and aarch64, so moving system files is not a good idea and makes the file less maintainable.
🎏
I've tried using uv to install specific python version. This minimal version seems to be working. I will try on larger apps
FROM debian:12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ENV UV_PYTHON_INSTALL_DIR=/tmp/python
COPY .python-version .
RUN uv python install \
&& cd $UV_PYTHON_INSTALL_DIR \
&& version_dir=$(ls $UV_PYTHON_INSTALL_DIR) \
&& mkdir -p /opt/python \
&& mv $version_dir/* /opt/python
FROM gcr.io/distroless/base-debian12
COPY --from=builder /opt/python /opt/python
ENV PATH=/opt/python/bin:$PATH
ENV PYTHONHOME=/opt/python
WORKDIR /app
COPY main.py .
ENTRYPOINT ["python3"]
@enkhjile Consider using gcr.io/distroless/cc for runtime since it includes libgcc.
This would solve the ImportError: libgcc_s.so.1: cannot open shared object file: No such file or directory errors.
The example below uses /usr/local for Python interpreter, dependencies, and app package installation. There is no venv.
uv is invoked twice — once to install the interpreter, and a second time to install the project's dependencies and create module shims.
In my pyproject.toml, I use hatchling as a build-backend and specify entry points of the package in the [project.scripts] section.
FROM debian:12-slim AS builder
RUN --mount=type=bind,source=.,target=/app \
--mount=from=ghcr.io/astral-sh/uv,source=/uv,target=/usr/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
<<EOF
set -Eeux
# Install specific Python version from .python-version
uv python install --project=/app --managed-python --install-dir=/tmp/python
# Move Python into /usr/local
(cd /tmp/python/* && tar -cf- .) | (cd /usr/local && tar -xf-)
rm -r /tmp/python
# Install dependencies and the package
export UV_PROJECT_ENVIRONMENT=/usr/local
uv sync --project=/app --frozen --compile-bytecode --link-mode=copy --no-dev --no-editable --no-managed-python
EOF
# Using distroless as a main runtime image
# (add "debug-" tag prefix if you need a shell/busybox binary)
FROM gcr.io/distroless/cc-debian12:nonroot
# Copy Python interpreter and the package from the builder stage
COPY --from=builder /usr/local /usr/local
# Run as non-root
USER nonroot
ENTRYPOINT ["/usr/local/bin/py-hello-cmd"]
Any progress for this?
all major OSs are using python3.12 by default, it will be nice to support this as well directly gcr.io/distroless/python3.12-debian12. (3.13 is latest now)
Python is provided as is from what debian gives us. If someone wants to mirror what we do for java/node here, happy to accept and review a PR.
as Debian Trixie contains 3.13, soon it will be available for gcr.io/python3-debian13 which use python3.13.
Any plan or related tickets to follow?
Any news on gcr.io/python3-debian13 ?
I maintain a fork of this project a work. We build for Python 3.10, 3.11, 3.12, 3.13. We use the Docker Debian Python image and extract the binaries out, then we have created a bazel rule package them for distroless. We tag and republish them for mainly NVIDIA usecase. There are a few changes if you poke around, such as rootless. Other language flavors are supported too.
NVIDIA distroless v3 = debian 12. Go = base, the rest remains the same. We plan on adding v4 = debian 13 when it's released. Only amd64 and arm64 is supported and it will remain that way.
@loosebazooka I'm happy to contribute this feature out, but we need an package registry to host the packaged Python zip files.