Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sentry launches MCP monitoring tool

      August 14, 2025

      10 Benefits of Hiring a React.js Development Company (2025–2026 Edition)

      August 13, 2025

      From Line To Layout: How Past Experiences Shape Your Design Career

      August 13, 2025

      Hire React.js Developers in the US: How to Choose the Right Team for Your Needs

      August 13, 2025

      I’ve tested every Samsung Galaxy phone in 2025 – here’s the model I’d recommend on sale

      August 14, 2025

      Google Photos just put all its best editing tools a tap away – here’s the shortcut

      August 14, 2025

      Claude can teach you how to code now, and more – how to try it

      August 14, 2025

      One of the best work laptops I’ve tested has MacBook written all over it (but it’s even better)

      August 14, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Controlling Execution Flow with Laravel’s Sleep Helper

      August 14, 2025
      Recent

      Controlling Execution Flow with Laravel’s Sleep Helper

      August 14, 2025

      Generate Secure Temporary Share Links for Files in Laravel

      August 14, 2025

      This Week in Laravel: Filament 4, Laravel Boost, and Junie Review

      August 14, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      KDE Plasma 6 on Wayland: the Payoff for Years of Plumbing

      August 14, 2025
      Recent

      KDE Plasma 6 on Wayland: the Payoff for Years of Plumbing

      August 14, 2025

      FOSS Weekly #25.33: Debian 13 Released, Torvalds vs RISC-V, Arch’s New Tool, GNOME Perfection and More Linux Stuff

      August 14, 2025

      Ultimate ChatGPT-5 Prompt Guide: 52 Ideas for Any Task

      August 14, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Build Slim and Fast Docker Images with Multi-Stage Builds

    How to Build Slim and Fast Docker Images with Multi-Stage Builds

    May 14, 2025

    Apps don’t stay simple forever. More features mean more dependencies, slower builds, and heavier Docker images. That’s where things start to hurt.

    Docker helps, but without the right setup, your builds can quickly get bloated.

    Multi-stage builds make things smoother by keeping your images fast, clean, and production-ready. In this guide, you’ll learn how to use them to supercharge your Docker workflow.

    Let’s get into it.

    Prerequisites

    To follow this guide, you should have:

    • Docker installed and running

    • Basic understanding of Docker

    • Some Python knowledge (or any language, really)

    • Familiarity with the terminal

    Here’s what we’ll cover:

    1. What are Docker Images?

    2. How to Implement Multi-Stage Builds

    3. The Chunky Single-Stage Build

    4. When to Use Multi-Stage Builds

    5. Conclusion

    What are Docker Images?

    Before we dive into optimization, let’s quickly get clear on what Docker images actually are.

    A Docker image is a lightweight, standalone package that has everything your app needs to run – code, dependencies, environment variables, and config files. Think of it as a snapshot of your app, ready to spin up anywhere.

    When you run an image, Docker turns it into a container: a self-contained environment that behaves the same on your machine, in staging, or in production. That consistency is a huge win for development and deployment.

    Now that we’ve got the basics, let’s talk about making those images smaller and faster.

    How to Implement Multi-Stage Builds

    Let’s get hands-on by creating a basic Flask app and using a multi-stage build to keep our Docker image slim.

    Step 1: Create app.py

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello():
        return "Hello, Docker Multi-stage Builds! 🐳"
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    

    Step 2: Install and save dependencies

    Install Flask and Gunicorn using pip:

    pip install flask gunicorn
    

    Then freeze your environment into a requirements.txt file:

    pip freeze > requirements.txt
    

    This file is what Docker will use to install dependencies inside your container.

    Step 3: Create the multi-stage Dockerfile

    # Stage 1: Build Stage
    FROM python:3.9-slim AS builder
    
    WORKDIR /app
    
    COPY requirements.txt .
    
    RUN python -m venv /opt/venv && \
        . /opt/venv/bin/activate && \
        pip install --no-cache-dir -r requirements.txt
    
    # Stage 2: Production Stage
    FROM python:3.9-slim
    
    COPY --from=builder /opt/venv /opt/venv
    
    WORKDIR /app
    
    COPY . .
    
    ENV PATH="/opt/venv/bin/:$PATH"
    
    EXPOSE 5000
    
    CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
    

    In the Dockerfile above, we’ve defined both a development and a production stage for our application. The first stage, the Build Stage, uses the python:3.9-slim base image, sets up a working directory, adds all the necessary files, and creates a virtual environment. All dependencies are installed inside that virtual environment.

    In the Production Stage, we again start from python:3.9-slim, but this time we copy only the virtual environment from the build stage along with the application code. Then we configure the environment to use that virtual environment and run the app using Gunicorn.

    Now, in a multi-stage build, you can experiment with using different Python versions across stages – but here’s why I didn’t go that route:

    • Some packages may have different dependencies, depending on the Python version.

    • My requirements.txt file contains version-specific dependencies, so sticking to the same Python version across both stages helps avoid compatibility issues.

    Once the multi-stage Dockerfile is ready, go ahead and build the images. You’ll clearly see the size difference.

    Step 4: Build and run your image

    To build and run your image container, use the following command:

    # Build the image
    docker build -t my-python-app .
    
    # Run the container
    docker run -p 5000:5000 my-python-app
    

    If everything works correctly, your Flask app should now be live at http://localhost:5000 in your browser.

    You’ll know your build succeeded when Docker completes without errors and starts the container. You should see terminal logs from Gunicorn indicating the app is up and running.

    9e8348ac-d21c-4371-bb42-e514457a12ff

    The Chunky Single-Stage Build

    Let’s compare with a traditional one-stage Docker build that includes everything in one go:

    FROM python:3.9-slim
    
    WORKDIR /app
    
    RUN apt-get update && apt-get install -y \
        build-essential \
        python3-dev \
        gcc \
        && rm -rf /var/lib/apt/lists/*
    
    COPY requirements.txt .
    
    RUN python -m venv /opt/venv
    ENV PATH="/opt/venv/bin:$PATH"
    
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY . .
    
    EXPOSE 5000
    
    CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
    

    The Dockerfile above uses a straightforward build process: it starts from the python:3.9-slim image, sets a working directory, installs system dependencies, creates a virtual environment, installs Python packages, copies over the app code, exposes port 5000, and runs the app using Gunicorn. This kind of Dockerfile is common and works fine, but it can lead to unnecessarily large and bloated images.

    Let’s build our image to compare the size with that of the multi-stage build:

    docker build -t my-chunky-app .
    

    You’ll notice that this Dockerfile takes longer to build compared to the previous one, which was much faster.

    Before we continue, confirm your Docker image was successfully built.

    5b83915e-b5b5-4927-9981-f35dad8fb1ff

    Now, let’s compare build sizes:

    docker images | grep 'my-'
    

    In case you’re wondering why we used “my” to search for the images, it’s because we named our Docker images my-python-app and my-chunky-app, so using “my” as a keyword makes it easy to filter them.

    1e3667ad-b2fd-4fff-a0e2-31d4705582a7

    The image above compares the build sizes of our single-stage and multi-stage Docker images. As you can see, my-python-app – the multi-stage build – is small and lightweight, while my-chunky-app is significantly larger. If you dig a bit deeper, you’ll notice that the multi-stage image built in just 1.2 seconds, whereas the single-stage one took a full 1 minute and 21 seconds. Pretty impressive difference, right?

    9584255b-c6aa-4d25-8a4a-e4a841808b57

    In my opinion, these are solid reasons to use a multi-stage build – but it’s not always necessary. There are cases where a single-stage build makes more sense. Let’s take a look at those.

    When to Use Multi-Stage Builds

    Use multi-stage builds if:

    • Your app needs build tools (for example, compilers, dev dependencies)

    • You want smaller, faster Docker images

    • You care about image security and performance

    Use single-stage builds if:

    • You’re just testing or prototyping

    • Your app is tiny and doesn’t need external tools

    • You’re still learning the basics

    Pick what fits your project’s scale and complexity.

    Conclusion

    Multi-stage builds are an easy win. They help keep your Docker images clean, fast, and secure – especially as your app grows.

    Not every project needs them, but when you do, they make a big difference. So next time you’re Dockerizing something serious, reach for multi-stage. Your future self will thank you.

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleLoad Balancing with Azure Application Gateway and Azure Load Balancer – When to Use Each One
    Next Article Learn Vite for a Better Web Development Workflow

    Related Posts

    Development

    Controlling Execution Flow with Laravel’s Sleep Helper

    August 14, 2025
    Development

    Generate Secure Temporary Share Links for Files in Laravel

    August 14, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    CVE-2025-3415 – Grafana Alerting DingDing Unauthenticated Viewer Escalation

    Common Vulnerabilities and Exposures (CVEs)

    Smashing Security podcast #415: Hacking hijinks at the hospital, and WASPI scams

    Development

    CVE-2025-48995 – SignXML Timing Attack HMAC Leak

    Common Vulnerabilities and Exposures (CVEs)

    Microsoft 365 Is Gaining Brand-New Apps — Here’s What’s on the Way to Windows 11

    News & Updates

    Highlights

    CVE-2025-5290 – Elementor Stored Cross-Site Scripting Vulnerability

    May 31, 2025

    CVE ID : CVE-2025-5290

    Published : May 31, 2025, 8:15 a.m. | 1 hour, 27 minutes ago

    Description : The Borderless – Elementor Addons and Templates plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the ‘title’ parameter in all versions up to, and including, 1.7.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

    Severity: 6.4 | MEDIUM

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    JFXChess is a JavaFX Chess GUI

    June 16, 2025

    CVE-2025-4547 – SourceCodester Web-based Pharmacy Product Management System Cross-Site Scripting Vulnerability

    May 11, 2025

    I tested the $13 multitool that everyone is talking about. Here’s my verdict

    April 14, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.