Skip to content

Instantly share code, notes, and snippets.

@Jarmos-san
Last active April 15, 2024 02:21
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Jarmos-san/11bf22c59d26daf0aa5223bdd50440da to your computer and use it in GitHub Desktop.
Save Jarmos-san/11bf22c59d26daf0aa5223bdd50440da to your computer and use it in GitHub Desktop.
An example multi-stage Dockerfile for use Python's poetry package manager

An Example Multi-Stage Dockerfile for Python's Poetry-based Projects

The gist contains an example Dockerfile to be used as a source of reference for future usage.

Most of the Dockerfile is derived from the suggestions and tips in this comment and some of the articles I found in this website - Python Speed. The Dockerfile is well documented and you can read the comments in it for further reference.

And if you feel there is something missing or can be improved further, please let me know in the comment section below.

If you are also looking an example "healthcheck" endpoint to build with FastAPI, check out this gist - https://gist.github.com/Jarmos-san/0b655a3f75b698833188922b714562e5

# Set the working directory globally
ARG SOURCEDIR="app"
# Pin the Python version to use
# See the following article to learn more about choosing the right base image
# https://pythonspeed.com/articles/base-image-python-docker-images
ARG PYTHONVERSION="3.11-slim-bookworm"
# Createh the base image for generating the requirements file
FROM python:${PYTHONVERSION} AS builder
# Reinitialise the "source directory" for the current stage
ARG SOURCEDIR
# Configure Python to not buffer "stdout" or create .pyc files
ENV PYTHONBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Add a non-root user for security reasons
RUN adduser --home /${SOURCEDIR} app-name
USER app-name
# Ensure the poetry binary is accessible and executable
ENV PATH=/${SOURCEDIR}/.local/bin:$PATH
# Update pip to start working with Python
RUN python -m pip install pip~=23.0 \
--upgrade --no-cache-dir --disable-pip-version-check --no-compile && \
python -m pip install poetry~=1.0 \
--no-cache-dir --disable-pip-version-check --no-compile
# Set the proper working directory to generate the "requirements.txt" file into
WORKDIR /home/app-name/${SOURCEDIR}
# Copy the necessary files for Poetry to the generate the requirements file from
COPY ../pyproject.toml ../poetry.lock ./
# Create a "requirements.txt" file for the "runtime" stage
RUN poetry export --output=requirements.txt --only=main
# Base image for the "runtime" stage
FROM python:${PYTHONVERSION} AS runtime
# Reinitialise the working directory for the current stage
ARG SOURCEDIR
# Ensure the Poetry is accessible and executable
ENV PATH=/${SOURCEDIR}/.local/bin:$PATH
# Ensure the system is up-to-date with the latest security updates
RUN apt-get update && \
apt-get upgrade --assume-yes && \
# cURL is necessary to invoke the "healthcheck" function using Docker
apt-get install curl --assume-yes && \
rm --recursive --force /var/lib/apt/lists/*
# See the following article for information on this regards:
# https://pythonspeed.com/articles/root-capabilities-docker-security
RUN adduser --home /${SOURCEDIR} app-name
USER app-name
# Set the working directory to a secure non-root location
WORKDIR /home/app-name
# Copy the necessary runtime files from the previous "builder" stage
COPY --from=builder /home/app-name/${SOURCEDIR}/requirements.txt ./
# Install the runtime dependencies using pip and additionally cache the dependencies to optimise build times
RUN --mount=type=cache,target=/root/.cache \
python -m pip install pip~=23.0 --upgrade --no-cache-dir \
--disable-pip-version-check --no-compile && \
python -m pip install --requirement "requirements.txt" \
--require-hashes --no-cache-dir --disable-pip-version-check --no-compile && \
rm --force "requirements.txt"
# Copy the source code to the container
COPY . ./${SOURCEDIR}
# Document which port to export
# TODO: Might remove this line if it proves to be a security bad practice
EXPOSE 8000
# Invoke the Python entrypoint file to start the backend service
CMD [ "python", "-m", "${SOURCEDIR}.main" ]
# Perform a healthcheck to ensure the service is up and running
HEALTHCHECK --interval=5s --timeout=5s --retries=5 CMD curl --include --request GET http://localhost:8000/healthcheck || exit 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment