This project demonstrates how to use Docker multi-stage builds to create a minimal, production-ready image for a PDF generator application. Multi-stage builds help separate the build environment from the runtime environment, resulting in smaller and more secure images.
A multi-stage build in Docker allows you to use multiple FROM statements in a single Dockerfile. Each stage can use a different base image and perform different tasks. Artifacts (such as installed dependencies or built files) can be copied from one stage to another, ensuring that only the necessary files are included in the final image.
This repository contains a Python Flask app that generates PDFs using the fpdf library. The Dockerfile uses a multi-stage build to optimize the final image.
# Stage 1 - Builder
FROM python:3.11-slim AS builder
WORKDIR /install
COPY app/requirements.txt .
RUN pip install --prefix=/install/python-packages -r requirements.txt
# Stage 2 - Runtime
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /install/python-packages /usr/local
COPY app /app
EXPOSE 5000
CMD ["python", "app.py"]How it works:
- The first stage (
builder) installs all dependencies into a separate directory. - The second stage copies only the installed dependencies and application code into a fresh, minimal image.
- This results in a smaller, more secure production image.
-
Build the Docker image:
docker build -t multistage-pdf-generator . -
Run the container:
docker run -p 5000:5000 multistage-pdf-generator
POST /generate-pdf
Content-Type: application/json
{
"name": "Alice"
}- Returns a PDF file with a greeting for the provided name.
curl -X POST http://localhost:5000/generate-pdf \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}' --output output.pdfInvoke-RestMethod `
-Uri http://localhost:5000/generate-pdf `
-Method POST `
-Headers @{ "Content-Type" = "application/json" } `
-Body '{ "name": "Alice" }' `
-OutFile "output.pdf"This will save the PDF response as output.pdf in your current directory.
- Smaller Images: Only necessary files and dependencies are included in the final image.
- Security: Build tools and unnecessary packages are excluded from production.
- Cleaner Deployments: Only production code and assets are shipped.
- Use minimal base images (e.g.,
alpine,slim). - Copy only what is needed from the build stage.
- Clean up caches and temporary files.
- Pin dependency versions for reproducibility.
- Use
.dockerignoreto exclude unnecessary files from the build context.
- Use
docker build --target <stage>to debug intermediate stages. - Use
docker image lsanddocker history <image>to inspect image sizes and layers.