Reducing Python Docker Image Size with Multi-Stage Builds
One of the most effective ways to reduce the size of a Docker image is using multi-stage builds. This technique allows you to use a heavy image with all the necessary build tools (compilers, headers) to install dependencies, and then copy only the necessary artifacts (installed packages) to a lightweight "runner" image.
The Problem
When you install Python packages (like numpy, pandas, or psycopg2), pip often needs to compile C code. This requires system tools like gcc and python3-dev.
If you install these tools in your final image, it becomes unnecessarily large and increases the security attack surface.
The Solution: Multi-Stage Dockerfile
Here is a pattern that separates the build environment from the runtime environment.
# __________ STAGE 1: BUILDER __________
# We use a slim image, but we will install build tools here.
FROM python:3.11-slim as builder
WORKDIR /app
# 1. Install system dependencies required for building Python packages
# (e.g., gcc, libpq-dev). These won't make it to the final image.
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# 2. Create a virtual environment to isolate our packages
RUN python -m venv /opt/venv
# 3. Activate the venv for the following commands
ENV PATH="/opt/venv/bin:$PATH"
# 4. Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# __________ STAGE 2: FINAL __________
# We start fresh with a clean, lightweight image.
FROM python:3.11-slim
WORKDIR /app
# 1. Copy the virtual environment from the 'builder' stage
# This is the magic step. We get the compiled packages without the compiler tools.
COPY --from=builder /opt/venv /opt/venv
# 2. Activate the venv
ENV PATH="/opt/venv/bin:$PATH"
# 3. Copy application code
COPY . .
# 4. Run
CMD ["python", "main.py"]
Key Concepts
as builder: Names the first stage so we can reference it later.- Virtual Environment (
/opt/venv): We install everything into a specific folder. This makes it incredibly easy to copy just the dependencies to the next stage. COPY --from=builder: Copies files from the previous stage instead of the host machine.- Result: Your final image contains only Python, your code, and the compiled libraries. It does not contain
gcc,aptcaches, or build artifacts.