🐳Understanding Multi-Stage Docker Builds

Discover how to reduce Docker image size, improve security, and optimize performance by switching from traditional Dockerfiles to multi-stage Docker builds — with real-world Python examples.

⚠️ The Common Docker Dilemma

So, you’re containerizing your Python app. You build a Dockerfile, run the build, and boom — your image is 500MB+. It works... but it feels bloated. You’re not alone.

Here’s what typically happens:

  • You install everything, including build tools and dependencies

  • You copy the whole project (maybe even with .git and .env files!)

  • You unknowingly ship unnecessary stuff into production

This leads to:

  • Longer build and deployment times

  • More attack surface for security vulnerabilities

  • Increased costs on cloud platforms

Let’s fix that — professionally and efficiently.

📄 Your Python App Example

Say you have a Python script that performs a simple log operation:

# app.py
print("Processing logs securely...")

Your requirements.txt might look like:

pandas
numpy

These are heavy libraries. Let’s see how they affect your image.

Traditional Dockerfile (The Common Mistake)

FROM python:3.10-slim

WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt

CMD ["python", "app.py"]

Issues:

  • Copies EVERYTHING from the local folder

  • Leaves pip, build tools, and unused files inside the image

  • Results in an image that can be 400MB+

What Are Multi-Stage Builds?

Multi-stage builds allow you to use multiple FROM statements in your Dockerfile. You:

  1. Build your app in one stage

  2. Copy only what you need to a clean runtime image

This gives you lightweight, secure, and production-ready containers.

Think of it like this:

👨‍🍳 You use a full kitchen to cook a dish, but only serve the final plate — not the dirty pots, the stove, or the spice rack.

✅ Optimized Multi-Stage Dockerfile (for Python)

# Stage 1: Build dependencies
FROM python:3.10-slim as builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

# Stage 2: Final runtime image
FROM python:3.10-slim

WORKDIR /app
COPY --from=builder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
COPY --from=builder /app/app.py .

CMD ["python", "app.py"]

🔍 Breakdown: What Just Happened?

FeatureTraditional BuildMulti-Stage Build
Final Image Size~400-500MB~150-200MB
Dev Tools Included✅ Yes❌ No
Attack SurfaceHighLow
Ideal for Production❌ Not Recommended✅ Absolutely
Dockerfile ComplexitySimple but riskySlightly complex but safe

📝 BONUS: Add .dockerignore

Create a .dockerignore file to skip unnecessary files:

__pycache__/
.env
*.log
.git
*.pyc

Just like .gitignore, this keeps your image lean and secure.

🔒 Real Benefits for You

  • Faster builds & deployments

  • ⚖️ Lower storage and transfer costs

  • 🚫 Minimized exposure to vulnerabilities

  • 🌟 Cleaner, maintainable Dockerfiles

Whether you’re deploying a Flask app or a data pipeline, this technique is worth your time.

🤔 Still Wondering Why It Matters?

Imagine a CI/CD pipeline where:

  • Every MB counts

  • You’re auto-scanning images for vulnerabilities

  • You need faster test feedback loops

Multi-stage builds become your DevOps superpower.

🎯 Final Thoughts

Multi-stage Docker builds are not just an optimization trick. They're a foundational best practice for:

  • Anyone deploying apps

  • DevOps engineers

  • Backend or data developers

Even if your project is small today, preparing for scale and security is always a smart move.

Share it with someone who need it 👍

10
Subscribe to my newsletter

Read articles from Anuj Kumar Upadhyay directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Anuj Kumar Upadhyay
Anuj Kumar Upadhyay

I am a developer from India. I am passionate to contribute to the tech community through my writing. Currently i am in my Graduation in Computer Application.