WarpBuild LogoWarpBuild Docs

Python with pip

Best practices for Dockerfile for Python with pip

🐳 Annotated Dockerfile for Python with pip:

# 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 environment variables
ENV PYTHONFAULTHANDLER=1 \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONHASHSEED=random \
    PIP_NO_CACHE_DIR=off \
    PIP_DISABLE_PIP_VERSION_CHECK=on \
    PIP_DEFAULT_TIMEOUT=100

# --------------------------------------
# Stage 1: Builder - installs dependencies and prepares app
# --------------------------------------
FROM base AS builder

# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Set up a virtual environment
RUN python -m venv /opt/venv
# Ensure we use the virtual environment
ENV PATH="/opt/venv/bin:$PATH"

# Set working directory
WORKDIR /app

# Copy and install requirements first for better caching
COPY requirements.txt .
COPY requirements-prod.txt .

# Install Python dependencies with caching
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements-prod.txt

# Copy the rest of the application code
COPY . .

# If you have a build step (e.g., compiling assets), run it here
RUN pip install --no-deps -e .

# --------------------------------------
# Stage 2: Final production image
# --------------------------------------
FROM base

# Create a non-root user and group
RUN groupadd -r appuser && useradd -r -g appuser appuser

# Set working directory
WORKDIR /app

# Copy only the virtual environment from the builder stage
COPY --from=builder /opt/venv /opt/venv

# Copy application code
COPY --from=builder /app /app

# Set environment variables to use virtual environment
ENV PATH="/opt/venv/bin:$PATH"

# Set ownership for application files
RUN chown -R appuser:appuser /app

# Switch to non-root user
USER appuser

# Expose the port your application runs on
EXPOSE 8000

# Run your application
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myapp.wsgi:application"]

🔍 Why these are best practices:

✅ Multi-stage builds

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

✅ Virtual environments

  • Isolates application dependencies from system Python packages.
  • Ensures consistent environment for your application.
  • Makes it easier to manage dependency conflicts.

✅ Dependency caching

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

✅ Environment variable optimization

  • PYTHONDONTWRITEBYTECODE=1 avoids creating .pyc files, reducing image size.
  • PYTHONUNBUFFERED=1 ensures Python output is sent straight to the container logs.
  • PIP_DISABLE_PIP_VERSION_CHECK=on eliminates unnecessary version checks.

✅ Non-root user

  • Runs application as non-privileged user for enhanced security.
  • Follows principle of least privilege to reduce risk of container escape.
  • Required in many enterprise Kubernetes environments.

🚀 Additional Dockerfile best practices you can adopt:

Split requirements for dev and prod

Maintain separate requirements files for different environments:

# requirements.txt - Base requirements
flask==2.3.2
sqlalchemy==2.0.16
pydantic==2.0.2

# requirements-dev.txt - Development requirements
-r requirements.txt
pytest==7.3.1
black==23.3.0
flake8==6.0.0

# requirements-prod.txt - Production requirements
-r requirements.txt
gunicorn==20.1.0

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
.env
.venv
env/
venv/
ENV/
.pytest_cache/
.coverage
htmlcov/
.git/
.github/
.idea/
.vscode/
*.md
!README.md

Pin exact dependency versions

For deterministic builds, pin exact versions in your requirements.txt:

# Good - pinned exact versions
flask==2.3.2
sqlalchemy==2.0.16

# Bad - allows minor or patch version changes
flask>=2.3.0
sqlalchemy~=2.0.0

Consider using a dedicated Python app server

Use a production-ready (Web Server Gateway Interface) server instead of development servers:

# Install production server
RUN pip install gunicorn

# Use in your CMD
CMD ["gunicorn", "--workers", "4", "--bind", "0.0.0.0:8000", "app:app"]

Pre-compile Python code

For slightly faster startup, pre-compile your Python modules:

# Pre-compile Python bytecode
RUN python -m compileall /app

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

On this page