WarpBuild LogoWarpBuild Docs

Python with Poetry

Best practices for Dockerfile for Python with Poetry

🐳 Annotated Dockerfile for Python with Poetry:

# Start with a slim Python base image - 3.12 is recommended for modern features and performance
FROM python:3.12-slim-bookworm AS base
 
# Set build arguments and environment variables
ARG POETRY_VERSION=1.8.2
ENV PYTHONFAULTHANDLER=1 \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONHASHSEED=random \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1 \
    # Poetry configuration
    POETRY_NO_INTERACTION=1 \
    POETRY_VIRTUALENVS_IN_PROJECT=1 \
    POETRY_HOME="/opt/poetry"
 
# --------------------------------------
# Stage 1: Builder - installs poetry and dependencies
# --------------------------------------
FROM base AS builder
 
# Install Poetry using official installer
RUN pip install --no-cache-dir "poetry==$POETRY_VERSION"
 
# Set working directory
WORKDIR /app
 
# Copy only requirements-related files to optimize caching
COPY pyproject.toml poetry.lock* ./
 
# Configure Poetry to use system Python
RUN poetry config virtualenvs.create false
 
# Install dependencies using Poetry with caching
# (Only dependencies, without development dependencies)
RUN --mount=type=cache,target=/root/.cache/pypoetry \
    poetry install --only main --no-root
 
# Copy the rest of the application code
COPY . .
 
# Install the project itself
RUN --mount=type=cache,target=/root/.cache/pypoetry \
    poetry install --only main
 
# --------------------------------------
# Stage 2: Final production image
# --------------------------------------
FROM base
 
# Copy the virtual environment from the builder stage
COPY --from=builder /app /app
 
# Set working directory
WORKDIR /app
 
# Set proper path to include the virtual environment
ENV PATH="/app/.venv/bin:$PATH"
 
# Expose the port your application runs on
EXPOSE 8000
 
# Run your application using the virtual environment's Python
CMD ["python", "-m", "your_package.main"]

🔍 Why these are best practices:

✅ Multi-stage builds

  • Efficiently separates the build environment from the runtime environment.
  • Dramatically reduces final image size by not including build tools in production.
  • Improves security by minimizing the attack surface in your production container.

✅ Poetry for dependency management

  • Precise, deterministic dependency resolution with lockfiles.
  • Clear separation between development and production dependencies.
  • Ensures identical environments across development, testing, and production.

✅ Caching Poetry dependencies

  • Uses Docker's build cache effectively to avoid redundant downloads.
  • Significantly speeds up build time, especially in CI/CD environments.
  • Reduces network usage and dependency resolution time.

✅ Environment variable optimization

  • PYTHONDONTWRITEBYTECODE=1 avoids creating .pyc files, reducing image size.
  • PYTHONUNBUFFERED=1 ensures logs are output immediately, improving visibility.
  • POETRY_VIRTUALENVS_IN_PROJECT=1 keeps virtual environments in the project for better portability.

✅ Minimal final container

  • Smaller attack surface with fewer installed packages.
  • Faster container startup and less resource usage.
  • Improved security posture by excluding build tools from production.

🚀 Additional Dockerfile best practices you can adopt:

Create and use a non-root user

Enhance security by running your application as a non-privileged user:

FROM base
 
# Create a non-root user
RUN adduser --disabled-password --gecos "" appuser
 
COPY --from=builder /app /app
WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
 
# Change ownership and switch to non-root user
RUN chown -R appuser:appuser /app
USER appuser
 
EXPOSE 8000
CMD ["python", "-m", "your_package.main"]

Add a health check

Monitor the health of your container and enable automatic recovery:

HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8000/health || exit 1

Use .dockerignore

Exclude unnecessary files from your Docker build context:

__pycache__/
*.py[cod]
*$py.class
.pytest_cache/
.coverage
htmlcov/
.git/
.github/
.venv/
.vscode/
*.md
!README.md

Optimize for production builds with build arguments

Use build arguments to toggle between development and production builds:

ARG ENV=production
RUN if [ "$ENV" = "production" ] ; then \
      poetry install --only main ; \
    else \
      poetry install ; \
    fi

Separate dependency installation from code changes

To further optimize build caching:

# Copy and install dependencies first
COPY pyproject.toml poetry.lock* ./
RUN --mount=type=cache,target=/root/.cache/pypoetry \
    poetry install --only main --no-root
 
# Then copy application code (which changes more frequently)
COPY . .

By following these practices, you'll create Docker images for your Python applications that are efficient, secure, and optimized for both development and production environments.

Last updated on