<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Daniel Adeboye - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Daniel Adeboye - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 16 May 2026 19:38:15 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/AdeboyeDN/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Choose the Best GPU for Your AI Workloads ]]>
                </title>
                <description>
                    <![CDATA[ Choosing a GPU for your AI workload shouldn't be complicated, but it often feels that way. You're weighing specs you don't fully understand, comparing prices that seem arbitrary, and wondering if you're about to waste thousands on GPUs you don't need... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-choose-the-best-gpu-for-your-ai-workloads/</link>
                <guid isPermaLink="false">69691ef945288420c3465766</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GPUs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ai-workloads ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Adeboye ]]>
                </dc:creator>
                <pubDate>Thu, 15 Jan 2026 17:08:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768427017581/585da014-5cb6-45bd-b6f7-a9a8a257b288.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Choosing a GPU for your AI workload shouldn't be complicated, but it often feels that way. You're weighing specs you don't fully understand, comparing prices that seem arbitrary, and wondering if you're about to waste thousands on GPUs you don't need.</p>
<p>The good news: it's simpler than it looks. The right GPU matches your workload, not the spec sheet. This article breaks down what actually matters and helps you make a decision that fits your budget and needs.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before diving in, a few assumptions about where you’re starting from:</p>
<ul>
<li><p>Basic familiarity with ML workflows like training, fine-tuning, and inference</p>
</li>
<li><p>Some experience with PyTorch, TensorFlow or similar frameworks</p>
</li>
<li><p>No GPU, CUDA, or hardware expertise required</p>
</li>
<li><p>Our focus is on practical decisions, not low level theory</p>
</li>
</ul>
<h2 id="heading-heres-what-well-cover">Here's What We'll Cover:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-why-gpu-choice-matters-for-ai">Why GPU Choice Matters for AI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-your-workload-first">Understanding Your Workload First</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-gpu-specifications-that-matter-for-ai">Key GPU Specifications That Matter for AI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-which-gpu-for-which-ai-workload">Which GPU for Which AI Workload?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-should-you-rent-or-buy-gpus">Should You Rent or Buy GPUs?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-practical-decision-framework">Practical Decision Framework</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-pitfalls-to-avoid">Common Pitfalls to Avoid</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-why-gpu-choice-matters-for-ai">Why GPU Choice Matters for AI</h2>
<p>The GPU you choose directly impacts how fast you can work. Training a model that takes 12 hours on appropriate hardware might take three days on an under-specced machine. That difference compounds across dozens of training runs. Slow iteration means slower learning and delayed launches.</p>
<p>Memory constraints are worse. Running out of VRAM doesn't just slow you down. Your code crashes. You're forced to reduce batch sizes, which can hurt model quality. You spend days optimizing memory usage instead of improving your model.</p>
<p>Cost matters, too. Overspending on a data center GPU when a consumer card would work wastes money. Under-speccing and upgrading six months later wastes more. The goal is to buy hardware that matches your actual needs.</p>
<h2 id="heading-understanding-your-workload-first">Understanding Your Workload First</h2>
<p>Before you start comparing specs, get clear on what you're actually building. GPU requirements change dramatically depending on whether you're training models, serving predictions, or experimenting with new ideas.</p>
<h3 id="heading-training-workloads">Training Workloads</h3>
<p>Training needs serious power. You're loading datasets, running passes through your network, and updating millions of parameters. A transformer model can easily need 16GB of VRAM before you think about batch size.</p>
<p>Training also means iteration. You run an experiment, check results, tweak something, run again. The difference between two-hour feedback versus eight hours changes how many ideas you test per day.</p>
<h3 id="heading-inference-workloads">Inference Workloads</h3>
<p>Inference is different. Your model is trained – now you're serving predictions to users. You care about requests per second and latency. Inference needs way less memory since you're not storing gradients or optimizer states.</p>
<p>Quantization helps too. An INT8 model that needed 24GB during training might serve predictions on 8GB.</p>
<h3 id="heading-research-and-experimentation">Research and Experimentation</h3>
<p>Research lives in between. One day you're training a small model. The next you're loading a pre-trained transformer for inference. You need hardware that handles changing workloads without constant optimization. Flexibility beats raw power here.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768291283031/36d46102-aad1-45e2-a41d-fa7ffc505e14.png" alt="Image summarizing AI workload" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-key-gpu-specifications-that-matter-for-ai">Key GPU Specifications That Matter for AI</h2>
<p>Most GPU spec sheets throw numbers at you. TFLOPS, CUDA cores, clock speeds. Most of it doesn't matter for AI work. But there are three things that do.</p>
<h3 id="heading-vram-capacity">VRAM Capacity</h3>
<p>Memory is everything. Your model needs to fit. Your training batches need space. All those intermediate calculations need room. Run out and your code doesn't slow down – it crashes.</p>
<p>Here's what works in practice (assuming standard FP16/BF16 training without aggressive offloading). 12GB gets you started but you'll hit walls fast. 16GB is where serious work begins. You can train most common architectures without constantly wrestling with memory. 24GB is the comfort zone. Bigger models, larger batches, room to experiment.</p>
<p>Beyond that, you're training something massive or working with video and high-resolution images. If you're only running inference, 8GB to 12GB usually does the job.</p>
<h3 id="heading-compute-performance">Compute Performance</h3>
<p>Raw speed matters, but not the way marketing wants you to think. Modern GPUs pack specialized hardware for AI. Tensor cores do the heavy lifting for deep learning math. Mixed precision training runs parts of your model in FP16 for speed and FP32 where accuracy matters. For inference, INT8 support is gold. Compress your model down and watch it fly. Four times faster with barely any quality drop.</p>
<p>Ignore TFLOPS comparisons between different GPU architectures. They don't tell you how fast your actual training run completes. Look for real benchmarks on frameworks you'll actually use.</p>
<h3 id="heading-memory-bandwidth">Memory Bandwidth</h3>
<p>This is the spec everyone skips. Bandwidth determines how fast your GPU shuttles data between memory and compute. Doesn't matter how powerful your cores are if they're sitting idle waiting for data.</p>
<p>GPUs with HBM memory move data 50 to 100 percent faster than GDDR cards. That gap shows up immediately in training speed. Memory-bound workloads, which is most AI work, get direct speedups from better bandwidth. This is why an A100 often outperforms an RTX 4090 in training despite similar raw compute. The cores on the 4090 are fast, but they spend more time waiting for data. Bandwidth keeps your GPU fed.</p>
<h2 id="heading-which-gpu-for-which-ai-workload">Which GPU for Which AI Workload?</h2>
<p>Here's what you actually need for common AI tasks. This maps workloads to specific GPUs based on memory requirements and what works in practice.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Workload Type</strong></td><td><strong>VRAM Needed</strong></td><td><strong>Recommended GPUs</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Training LLMs from scratch (small, ≤3B)</td><td>40–80GB</td><td>A100 80GB, H100 80GB</td></tr>
<tr>
<td>Training LLMs from scratch (mid, 7B–30B)</td><td>80–160GB</td><td>H100 80GB, H200 141GB, multi-A100</td></tr>
<tr>
<td>Training LLMs from scratch (large, 70B+)</td><td>160GB+ multi-GPU</td><td>B100, B200, multi-H100</td></tr>
<tr>
<td>Fine-tuning LLMs (7B–13B)</td><td>24–48GB</td><td>RTX 4090 (24GB), RTX A6000 (48GB), A100 40GB</td></tr>
<tr>
<td>Fine-tuning LLMs (30B–70B)</td><td>48–80GB (or sharded)</td><td>A100 80GB, H100, H200, 2–4× RTX 4090</td></tr>
<tr>
<td>Fine-tuning LLMs (70B+)</td><td>80GB+ multi-GPU</td><td>H100, H200, multi-4090 with FSDP</td></tr>
<tr>
<td>LLM inference (7B–13B)</td><td>8–16GB</td><td>RTX 4060 Ti 16GB, RTX 4070, L4</td></tr>
<tr>
<td>LLM inference (30B–70B)</td><td>24–80GB</td><td>RTX 4090, L40S, A100 80GB, H200</td></tr>
<tr>
<td>LLM inference (70B–180B)</td><td>80–160GB</td><td>H200, A100 80GB (multi-GPU)</td></tr>
<tr>
<td>LLM inference (200B+)</td><td>160GB+</td><td>B100, B200</td></tr>
<tr>
<td>Training vision models (ResNet, ViT)</td><td>16–24GB</td><td>RTX 4080 Super, RTX 4090</td></tr>
<tr>
<td>Fine-tuning vision models</td><td>12–16GB</td><td>RTX 4070 Ti Super, RTX 4080</td></tr>
<tr>
<td>Training diffusion models</td><td>24–48GB</td><td>RTX 4090, RTX A6000, A100</td></tr>
<tr>
<td>Image generation (Stable Diffusion)</td><td>8–24GB</td><td>RTX 4070, RTX 4070 Ti, RTX 4090</td></tr>
<tr>
<td>Production inference at scale</td><td>24–48GB</td><td>L4, L40S, A10, A100</td></tr>
<tr>
<td>Research / OSS experimentation</td><td>24GB sweet spot</td><td>RTX 3090, RTX 4090</td></tr>
</tbody>
</table>
</div><h2 id="heading-should-you-rent-or-buy-gpus">Should You Rent or Buy GPUs?</h2>
<p>Whether you buy or rent depends on the type of GPU and how you're using it.</p>
<h3 id="heading-when-to-rent-a-gpu">When to Rent a GPU</h3>
<p>Rent when your workloads are sporadic. You're experimenting, testing ideas, running training jobs here and there. Cloud GPUs make sense because you only pay when you're actually using them.</p>
<p>Renting is also your only option for data center GPUs like the <a target="_blank" href="https://northflank.com/blog/nvidia-a100-gpu-cost">A100</a>, <a target="_blank" href="https://northflank.com/blog/how-much-does-an-nvidia-h100-gpu-cost">H100</a>, and <a target="_blank" href="https://northflank.com/blog/how-much-does-an-nvidia-b200-gpu-cost">B200</a>. You can't just buy one. NVIDIA sells them through server manufacturers as complete systems. You'd be dropping $100,000+ on a pre-configured server. Unless you're running at enterprise scale, renting H100 instances when you need that power is the move.</p>
<h3 id="heading-when-to-buy-a-gpu">When to Buy a GPU</h3>
<p>Buy when you're running things constantly. Inference servers running 24/7. Training models every week. The GPU isn't sitting idle.</p>
<p>Consumer cards like the RTX 4090 or professional cards like the RTX A6000 make sense to own. You can buy them from retailers, install them in your workstation, and they pay for themselves in months. An RTX 4090 costs around $2,000. Renting the equivalent at $2 per hour hits $1,440 monthly. Do the math.</p>
<h3 id="heading-most-people-do-both">Most people do both</h3>
<p>Own hardware for daily work. Rent when you need extra power or want to test with data center GPUs. Keeps costs down while staying flexible.</p>
<h2 id="heading-practical-decision-framework">Practical Decision Framework</h2>
<p>Figure out what you actually need before looking at hardware. How big are your models? What batch sizes are you running? How often are you training?</p>
<h3 id="heading-individual-developers">Individual Developers</h3>
<p>Renting cloud GPUs is the smart starting point. There’s no massive upfront cost. You pay $1 to $3 per hour for solid hardware. Train for a few hours, run experiments, shut it down. Your costs stay under control, and you're not stuck with hardware that might not fit your needs six months from now.</p>
<p>If you're training constantly and the monthly cloud bills are adding up, then consider buying. An RTX 4090 with 24GB costs around $1,600 to $2,000. RTX 4080 Super with 16GB runs about $1,000. They pay for themselves after several months of heavy use. But only buy when cloud costs clearly justify it.</p>
<h3 id="heading-small-teams">Small Teams</h3>
<p>Start with cloud GPUs. Rent what you need when you need it. As your workloads become predictable and sustained, then look at owning hardware. A workstation with one or two GPUs runs $3,000 to $7,000. Makes sense when you're running jobs daily, and cloud bills hit $1000+ monthly.</p>
<p>Most teams end up hybrid, anyway. Rent for experiments and burst workloads. Buy one solid GPU for daily work if the math works out.</p>
<h3 id="heading-production-at-scale">Production at Scale</h3>
<p>Cloud dominates here for good reason. Flexibility, geographic distribution, and no infrastructure headaches. Renting H100 or A100 instances gives you access to hardware you'd never buy outright.</p>
<p>Some companies buy servers for sustained inference when they're processing massive request volumes 24/7. You're looking at $50,000 to $100,000+ per server from Dell or Supermicro. But even then, most production deployments stay primarily cloud-based. The operational simplicity is worth it.</p>
<h2 id="heading-common-pitfalls-to-avoid">Common Pitfalls to Avoid</h2>
<h3 id="heading-dont-buy-based-on-gaming-benchmarks">Don’t Buy Based on Gaming Benchmarks</h3>
<p>Gaming reviews test completely different workloads. A GPU that dominates in Cyberpunk might underperform for training because of limited VRAM or memory bandwidth.</p>
<p>For AI work, look for benchmarks that reflect real workloads, such as:</p>
<ul>
<li><p>PyTorch or TensorFlow training throughput (tokens/sec) on models like Mistral-style LLMs.</p>
</li>
<li><p>Time-to-train for a fixed number of steps or epochs</p>
</li>
<li><p>Inference latency and throughput using tools like TensorRT or vLLM</p>
</li>
</ul>
<p>Vendor specs and gaming FPS don’t capture this. AI benchmarks show whether a GPU is compute-bound, memory-bound, or limited by VRAM.</p>
<h3 id="heading-never-skimp-on-vram-to-save-money">Never Skimp on VRAM to Save Money</h3>
<p>Running out of memory doesn't slow you down – it crashes your code. You're forced to rewrite things, reduce batch sizes, or buy new hardware anyway. When choosing between two GPUs at similar prices, take the one with more VRAM every time. That extra headroom matters more than you think.</p>
<h3 id="heading-ignoring-power-and-cooling-requirements">Ignoring Power and Cooling Requirements</h3>
<p>High-end GPUs pull 300 to 450 watts under load. Your power supply needs to handle it with headroom. Your case needs proper airflow, or that expensive GPU will thermal throttle and perform worse than cheaper hardware running cool. Check that your setup can actually support the card before buying it.</p>
<h3 id="heading-assuming-you-need-the-latest-hardware">Assuming You Need the Latest Hardware</h3>
<p>Last-generation GPUs often deliver 80% of the performance at 50% of the price. An RTX 4080 might be newer, but a used RTX 3090 with 24GB can handle many of the same workloads for way less money. Don't chase the latest release unless your work genuinely needs it.</p>
<h3 id="heading-not-testing-in-the-cloud-first">Not Testing in the Cloud First</h3>
<p>If you're unsure what you need, rent it first. Spend $50 testing different GPU types on cloud platforms before dropping $2,000 on hardware. You'll learn what actually bottlenecks your workloads. Maybe you need more VRAM. Maybe compute is fine, and you're memory bandwidth limited. Test before you buy.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Choosing the right GPU comes down to matching hardware to your actual workload. Training needs memory and bandwidth. Inference prioritizes efficiency. How often you run jobs determines whether renting or buying makes sense.</p>
<p>Start with the cloud. Test different GPUs, learn where your models bottleneck, and keep costs low while you experiment. When your workloads become predictable and sustained, then buying hardware starts to make sense.</p>
<p>Don’t overthink it. The wrong GPU slows you down. The right one disappears and lets you ship.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Slim and Fast Docker Images with Multi-Stage Builds ]]>
                </title>
                <description>
                    <![CDATA[ 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... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-slim-fast-docker-images-with-multi-stage-builds/</link>
                <guid isPermaLink="false">6824b1638a2b26e9d2d0fe86</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ docker images ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dockerfile ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Adeboye ]]>
                </dc:creator>
                <pubDate>Wed, 14 May 2025 15:06:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747235146559/0bce7dc3-0abe-4241-a188-1c05c773e810.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Apps don’t stay simple forever. More features mean more dependencies, slower builds, and heavier Docker images. That’s where things start to hurt.</p>
<p>Docker helps, but without the right setup, your builds can quickly get bloated.</p>
<p>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.</p>
<p>Let’s get into it.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow this guide, you should have:</p>
<ul>
<li><p>Docker installed and running</p>
</li>
<li><p>Basic understanding of Docker</p>
</li>
<li><p>Some Python knowledge (or any language, really)</p>
</li>
<li><p>Familiarity with the terminal</p>
</li>
</ul>
<h2 id="heading-heres-what-well-cover"><strong>Here's what we'll cover:</strong></h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-are-docker-images">What are Docker Images?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-multi-stage-builds">How to Implement Multi-Stage Builds</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-chunky-single-stage-build">The Chunky Single-Stage Build</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-multi-stage-builds">When to Use Multi-Stage Builds</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-are-docker-images">What are Docker Images?</h2>
<p>Before we dive into optimization, let’s quickly get clear on what Docker images actually are.</p>
<p>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.</p>
<p>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.</p>
<p>Now that we’ve got the basics, let’s talk about making those images smaller and faster.</p>
<h2 id="heading-how-to-implement-multi-stage-builds"><strong>How to Implement Multi-Stage Builds</strong></h2>
<p>Let’s get hands-on by creating a basic Flask app and using a multi-stage build to keep our Docker image slim.</p>
<h3 id="heading-step-1-create-apppyhttpapppy">Step 1: Create <a target="_blank" href="http://app.py"><code>app.py</code></a></h3>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask

app = Flask(__name__)

<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hello</span>():</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, Docker Multi-stage Builds! 🐳"</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">5000</span>)
</code></pre>
<h3 id="heading-step-2-install-and-save-dependencies">Step 2: Install and save dependencies</h3>
<p>Install Flask and Gunicorn using pip:</p>
<pre><code class="lang-bash">pip install flask gunicorn
</code></pre>
<p>Then freeze your environment into a <code>requirements.txt</code> file:</p>
<pre><code class="lang-bash">pip freeze &gt; requirements.txt
</code></pre>
<p>This file is what Docker will use to install dependencies inside your container.</p>
<h3 id="heading-step-3-create-the-multi-stage-dockerfile">Step 3: Create the multi-stage <code>Dockerfile</code></h3>
<pre><code class="lang-docker"><span class="hljs-comment"># Stage 1: Build Stage</span>
<span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.9</span>-slim AS builder

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">COPY</span><span class="bash"> requirements.txt .</span>

<span class="hljs-keyword">RUN</span><span class="bash"> python -m venv /opt/venv &amp;&amp; \\
    . /opt/venv/bin/activate &amp;&amp; \\
    pip install --no-cache-dir -r requirements.txt</span>

<span class="hljs-comment"># Stage 2: Production Stage</span>
<span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.9</span>-slim

<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /opt/venv /opt/venv</span>

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-keyword">ENV</span> PATH=<span class="hljs-string">"/opt/venv/bin/:$PATH"</span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">5000</span>

<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"gunicorn"</span>, <span class="hljs-string">"--bind"</span>, <span class="hljs-string">"0.0.0.0:5000"</span>, <span class="hljs-string">"app:app"</span>]</span>
</code></pre>
<p>In the Dockerfile above, we’ve defined both a development and a production stage for our application. The first stage, the <strong>Build Stage</strong>, uses the <code>python:3.9-slim</code> 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.</p>
<p>In the <strong>Production Stage</strong>, we again start from <code>python:3.9-slim</code>, 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.</p>
<p>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:</p>
<ul>
<li><p>Some packages may have different dependencies, depending on the Python version.</p>
</li>
<li><p>My <code>requirements.txt</code> file contains version-specific dependencies, so sticking to the same Python version across both stages helps avoid compatibility issues.</p>
</li>
</ul>
<p>Once the multi-stage Dockerfile is ready, go ahead and build the images. You’ll clearly see the size difference.</p>
<h3 id="heading-step-4-build-and-run-your-image">Step 4: Build and run your image</h3>
<p>To build and run your image container, use the following command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Build the image</span>
docker build -t my-python-app .

<span class="hljs-comment"># Run the container</span>
docker run -p 5000:5000 my-python-app
</code></pre>
<p>If everything works correctly, your Flask app should now be live at <a target="_blank" href="http://localhost:5000"><code>http://localhost:5000</code></a> in your browser.</p>
<p>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.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746875902903/9e8348ac-d21c-4371-bb42-e514457a12ff.png" alt="9e8348ac-d21c-4371-bb42-e514457a12ff" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-the-chunky-single-stage-build">The Chunky Single-Stage Build</h2>
<p>Let’s compare with a traditional one-stage Docker build that includes everything in one go:</p>
<pre><code class="lang-docker"><span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.9</span>-slim

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">RUN</span><span class="bash"> apt-get update &amp;&amp; apt-get install -y \\
    build-essential \\
    python3-dev \\
    gcc \\
    &amp;&amp; rm -rf /var/lib/apt/lists/*</span>

<span class="hljs-keyword">COPY</span><span class="bash"> requirements.txt .</span>

<span class="hljs-keyword">RUN</span><span class="bash"> python -m venv /opt/venv</span>
<span class="hljs-keyword">ENV</span> PATH=<span class="hljs-string">"/opt/venv/bin:$PATH"</span>

<span class="hljs-keyword">RUN</span><span class="bash"> pip install --no-cache-dir -r requirements.txt</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">5000</span>

<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"gunicorn"</span>, <span class="hljs-string">"--bind"</span>, <span class="hljs-string">"0.0.0.0:5000"</span>, <span class="hljs-string">"app:app"</span>]</span>
</code></pre>
<p>The Dockerfile above uses a straightforward build process: it starts from the <code>python:3.9-slim</code> 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.</p>
<p>Let’s build our image to compare the size with that of the multi-stage build:</p>
<pre><code class="lang-bash">docker build -t my-chunky-app .
</code></pre>
<p>You’ll notice that this Dockerfile takes longer to build compared to the previous one, which was much faster.</p>
<p>Before we continue, confirm your Docker image was successfully built.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746886030667/5b83915e-b5b5-4927-9981-f35dad8fb1ff.png" alt="5b83915e-b5b5-4927-9981-f35dad8fb1ff" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now, let’s compare build sizes:</p>
<pre><code class="lang-bash">docker images | grep <span class="hljs-string">'my-'</span>
</code></pre>
<p>In case you're wondering why we used "my" to search for the images, it's because we named our Docker images <code>my-python-app</code> and <code>my-chunky-app</code>, so using "my" as a keyword makes it easy to filter them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746885989703/1e3667ad-b2fd-4fff-a0e2-31d4705582a7.png" alt="1e3667ad-b2fd-4fff-a0e2-31d4705582a7" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The image above compares the build sizes of our single-stage and multi-stage Docker images. As you can see, <code>my-python-app</code> – the multi-stage build – is small and lightweight, while <code>my-chunky-app</code> 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?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746885947258/9584255b-c6aa-4d25-8a4a-e4a841808b57.png" alt="9584255b-c6aa-4d25-8a4a-e4a841808b57" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>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.</p>
<h2 id="heading-when-to-use-multi-stage-builds">When to Use Multi-Stage Builds</h2>
<p><strong>Use multi-stage builds if:</strong></p>
<ul>
<li><p>Your app needs build tools (for example, compilers, dev dependencies)</p>
</li>
<li><p>You want smaller, faster Docker images</p>
</li>
<li><p>You care about image security and performance</p>
</li>
</ul>
<p><strong>Use single-stage builds if:</strong></p>
<ul>
<li><p>You're just testing or prototyping</p>
</li>
<li><p>Your app is tiny and doesn’t need external tools</p>
</li>
<li><p>You’re still learning the basics</p>
</li>
</ul>
<p>Pick what fits your project’s scale and complexity.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Multi-stage builds are an easy win. They help keep your Docker images clean, fast, and secure – especially as your app grows.</p>
<p>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.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
