<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nadir</title>
    <description>The latest articles on Forem by Nadir (@nadir_83ac7c548ca3c97d440).</description>
    <link>https://forem.com/nadir_83ac7c548ca3c97d440</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3845820%2Fcaa28575-c4ae-4737-b2c5-e8984cd65ac1.png</url>
      <title>Forem: Nadir</title>
      <link>https://forem.com/nadir_83ac7c548ca3c97d440</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nadir_83ac7c548ca3c97d440"/>
    <language>en</language>
    <item>
      <title>How I reduced a Docker image from 1.12 GB to 131 MB (88% smaller)</title>
      <dc:creator>Nadir</dc:creator>
      <pubDate>Fri, 27 Mar 2026 08:01:32 +0000</pubDate>
      <link>https://forem.com/nadir_83ac7c548ca3c97d440/how-i-reduced-a-docker-image-from-112-gb-to-131-mb-88-smaller-952</link>
      <guid>https://forem.com/nadir_83ac7c548ca3c97d440/how-i-reduced-a-docker-image-from-112-gb-to-131-mb-88-smaller-952</guid>
      <description>&lt;p&gt;When I started working with Docker, I made the same &lt;br&gt;
mistake most beginners make — I used the full base image &lt;br&gt;
and copied everything into the container without thinking &lt;br&gt;
about size or performance.&lt;/p&gt;

&lt;p&gt;The result? A 1.12 GB image for a simple Flask app.&lt;/p&gt;

&lt;p&gt;Here's exactly what I changed to bring it down to 131 MB.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bad Dockerfile (what most tutorials show you)
&lt;/h2&gt;

&lt;p&gt;FROM python:3.11&lt;/p&gt;

&lt;p&gt;WORKDIR /app&lt;/p&gt;

&lt;p&gt;COPY . .&lt;/p&gt;

&lt;p&gt;RUN pip install -r requirements.txt&lt;/p&gt;

&lt;p&gt;CMD ["python", "app.py"]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems with this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;python:3.11&lt;/code&gt; is ~950 MB — includes compilers and 
tools your app will never use&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COPY . .&lt;/code&gt; before installing dependencies kills 
layer caching — pip reinstalls everything on 
every single code change&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;.dockerignore&lt;/code&gt; — copies your &lt;code&gt;.git&lt;/code&gt; folder, 
&lt;code&gt;__pycache__&lt;/code&gt;, and other junk into the image&lt;/li&gt;
&lt;li&gt;Flask dev server is not suitable for production&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The optimized Dockerfile
&lt;/h2&gt;

&lt;p&gt;FROM python:3.11-slim AS builder&lt;/p&gt;

&lt;p&gt;WORKDIR /app&lt;/p&gt;

&lt;p&gt;COPY requirements.txt .&lt;br&gt;
RUN pip install --no-cache-dir --user -r requirements.txt&lt;/p&gt;

&lt;p&gt;FROM python:3.11-slim&lt;/p&gt;

&lt;p&gt;WORKDIR /app&lt;/p&gt;

&lt;p&gt;COPY --from=builder /root/.local /root/.local&lt;br&gt;
COPY . .&lt;/p&gt;

&lt;p&gt;ENV PATH=/root/.local/bin:$PATH&lt;br&gt;
ENV PYTHONDONTWRITEBYTECODE=1&lt;br&gt;
ENV PYTHONUNBUFFERED=1&lt;/p&gt;

&lt;p&gt;EXPOSE 5000&lt;/p&gt;

&lt;p&gt;CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]&lt;/p&gt;
&lt;h2&gt;
  
  
  What changed and why
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Slim base image&lt;/strong&gt;&lt;br&gt;
Switching from &lt;code&gt;python:3.11&lt;/code&gt; to &lt;code&gt;python:3.11-slim&lt;/code&gt; &lt;br&gt;
removes all the build tools and system packages your &lt;br&gt;
app doesn't need in production. This alone cuts &lt;br&gt;
hundreds of MB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Multi-stage build&lt;/strong&gt;&lt;br&gt;
The builder stage installs all dependencies. The final &lt;br&gt;
stage copies only the installed packages — not pip &lt;br&gt;
cache, not build tools, nothing extra. Only what your &lt;br&gt;
app needs to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Layer caching&lt;/strong&gt;&lt;br&gt;
By copying &lt;code&gt;requirements.txt&lt;/code&gt; first and installing &lt;br&gt;
dependencies before copying the rest of the code, &lt;br&gt;
Docker caches the dependency layer. When you change &lt;br&gt;
your code, Docker skips the pip install step entirely &lt;br&gt;
— builds go from 45 seconds to under 5 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. .dockerignore&lt;/strong&gt;&lt;br&gt;
Create a &lt;code&gt;.dockerignore&lt;/code&gt; file to exclude:&lt;/p&gt;

&lt;p&gt;.git&lt;br&gt;
&lt;strong&gt;pycache&lt;/strong&gt;&lt;br&gt;
*.pyc&lt;br&gt;
.env&lt;br&gt;
.venv&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Production server&lt;/strong&gt;&lt;br&gt;
Replace Flask's dev server with Gunicorn — a &lt;br&gt;
production-grade WSGI server that handles real traffic.&lt;/p&gt;
&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base image&lt;/td&gt;
&lt;td&gt;python:3.11&lt;/td&gt;
&lt;td&gt;python:3.11-slim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build strategy&lt;/td&gt;
&lt;td&gt;Single stage&lt;/td&gt;
&lt;td&gt;Multi-stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Final size&lt;/td&gt;
&lt;td&gt;1.12 GB&lt;/td&gt;
&lt;td&gt;131 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduction&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;88% smaller&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same app. Same functionality. 88% smaller image.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why does image size matter?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster deployments&lt;/strong&gt; — smaller images pull faster 
from your registry to your server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower costs&lt;/strong&gt; — Docker Hub and cloud registries 
charge for storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; — fewer packages means fewer potential 
vulnerabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster CI/CD&lt;/strong&gt; — your pipeline runs faster when 
it doesn't have to pull a 1 GB image every time&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;The full code is on my GitHub:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Nadir-FK" rel="noopener noreferrer"&gt;
        Nadir-FK
      &lt;/a&gt; / &lt;a href="https://github.com/Nadir-FK/dockerfile-optimization" rel="noopener noreferrer"&gt;
        dockerfile-optimization
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Dockerfile Optimization&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A practical demonstration of Docker image optimization techniques — reducing a Python Flask image from &lt;strong&gt;1.12 GB down to 131 MB&lt;/strong&gt; (88% reduction) using multi-stage builds, slim base images, and Docker best practices.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Results&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base image&lt;/th&gt;
&lt;th&gt;Final size&lt;/th&gt;
&lt;th&gt;Build time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python:3.11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.12 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;slow (no cache)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;python:3.11-slim&lt;/code&gt; + multi-stage&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;131 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;fast (layer cache)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;88% smaller image&lt;/strong&gt; — faster pulls, lower cloud storage costs, reduced attack surface.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What changed and why&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. Base image — &lt;code&gt;python:3.11&lt;/code&gt; → &lt;code&gt;python:3.11-slim&lt;/code&gt;
&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;The full &lt;code&gt;python:3.11&lt;/code&gt; image ships with compilers, build tools, and system packages your app will never use in production. The slim variant contains only what's needed to run Python.&lt;/p&gt;
&lt;div class="highlight highlight-source-dockerfile notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Before — 1.12 GB&lt;/span&gt;
&lt;span class="pl-k"&gt;FROM&lt;/span&gt; python:3.11

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; After — starts at ~130 MB&lt;/span&gt;
&lt;span class="pl-k"&gt;FROM&lt;/span&gt; python:3.11-slim&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;2. Multi-stage build&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;The builder stage installs dependencies. The final stage copies only the installed packages — leaving behind pip cache, build tools…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Nadir-FK/dockerfile-optimization" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Build both images and compare:&lt;/p&gt;

&lt;p&gt;docker build -f Dockerfile.bad -t flask-bad .&lt;br&gt;
docker build -f Dockerfile.optimized -t flask-optimized .&lt;br&gt;
docker images | grep flask&lt;/p&gt;




&lt;p&gt;If you need help Dockerizing your own Python or Node.js &lt;br&gt;
app or setting up Docker Compose for your stack, I offer &lt;br&gt;
this as a freelance service on Fiverr:&lt;br&gt;
&lt;a href="https://www.fiverr.com/s/Q7m3l1p" rel="noopener noreferrer"&gt;https://www.fiverr.com/s/Q7m3l1p&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer any questions in the comments.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>freelance</category>
      <category>python</category>
    </item>
  </channel>
</rss>
