Chapter 73: DevOps Projects Part 3: Docker Websites, APIs, Compose, Registry and ECR
73.6 Project 22: Shrink an image from hundreds of MB to under 100 MB
Friends, fat images slow every deploy. Today we measure a fat Ubuntu-based site image, switch to nginx Alpine, drop build tools with multi-stage patterns, and prove the slim image still serves on port 8088 under 100 MB.
मित्रांनो, fat images प्रत्येक deploy मंद करतात. आज fat Ubuntu image मोजा, nginx Alpine वर जा, multi-stage ने build tools काढा, port 8088 वर slim image 100 MB खाली सिद्ध करा.
दोस्तों, fat images हर deploy धीमा करती हैं. आज fat Ubuntu image मापें, nginx Alpine पर जाएँ, multi-stage से build tools हटाएँ, port 8088 पर slim image 100 MB से कम सिद्ध करें.
What you will build in this project
- A fat baseline image measured.
- An Alpine nginx slim image under 100MB.
- docker history comparison.
- curl proof on port 8088.
1. Start from a real company story
At Paytm in Noida, a payment UI image was 1.1 GB because it shipped Node, npm cache and an Ubuntu desktop meta package by mistake.
Cluster pull time alone added 9 minutes to every release. One bad Friday cost about ₹16 lakh in delayed settlements.
Kabir cut the image to nginx Alpine under 50 MB with a multi-stage frontend build. Pulls dropped under 20 seconds.
Today we practice that shrink on a static site lab.
Ravindra Bagale's Tip
Many students delete random lines hoping the size drops. Measure first with docker images and docker history, then change one thing. Keep that habit.
खूप students size कमी करण्यासाठी random lines काढतात. आधी docker images आणि history ने मोजा, मग एक गोष्ट बदला. सवय ठेवा.
बहुत students size घटाने के लिए random lines हटाते हैं. पहले docker images और history से मापें, फिर एक चीज़ बदलें. आदत रखिए.
2. Start and prepare
Step 1: Measure fat image
Why: You cannot shrink what you do not measure. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker images SIZE column. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 1: fat note
Type this:
echo 'Start from a fat practice tag shop-fat:1.0 if present, else build one'
You will see:
Start from a fat practice tag shop-fat:1.0 if present, else build one
What it means: Plan.
Example 2: build fat
Type this:
mkdir -p /tmp/shrink && printf '%s\n' 'FROM ubuntu:22.04' 'RUN apt-get update && apt-get install -y curl python3' 'WORKDIR /app' 'COPY index.html .' 'CMD ["python3","-m","http.server","8080"]' > /tmp/shrink/Dockerfile.fat && printf '%s\n' '<h1>shop</h1>' > /tmp/shrink/index.html && docker build -t shop-fat:1.0 -f /tmp/shrink/Dockerfile.fat /tmp/shrink
You will see:
Successfully tagged shop-fat:1.0
What it means: Fat image built on purpose.
Example 3: size fat
Type this:
docker images shop-fat:1.0 --format '{{.Size}}'
You will see:
77MB
What it means: Baseline (ubuntu+tools); treat as too big for a static site.
Step 2: List big layers
Why: History shows which layer is fat. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker history. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 4: history
Type this:
docker history shop-fat:1.0 --human | head -6
You will see:
IMAGE CREATED CREATED BY SIZE
What it means: Layers show apt bloat.
Example 5: largest tip
Type this:
echo 'The RUN apt-get layer is usually the fattest'
You will see:
The RUN apt-get layer is usually the fattest
What it means: Target it.
Example 6: count layers
Type this:
docker history -q shop-fat:1.0 | wc -l
You will see:
5
What it means: Layer count.
Step 3: Switch to Alpine
Why: Alpine bases cut hundreds of MB. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: Retag FROM alpine. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 7: alpine df
Type this:
printf '%s\n' 'FROM nginx:1.27-alpine' 'COPY index.html /usr/share/nginx/html/index.html' > /tmp/shrink/Dockerfile.slim && cat /tmp/shrink/Dockerfile.slim
You will see:
FROM nginx:1.27-alpine
COPY index.html ...
What it means: Alpine nginx.
Example 8: why alpine
Type this:
echo 'Alpine musl images are tens of MB not hundreds'
You will see:
Alpine musl images are tens of MB not hundreds
What it means: Reason.
Example 9: pin
Type this:
head -1 /tmp/shrink/Dockerfile.slim
You will see:
FROM nginx:1.27-alpine
What it means: Pinned.
3. Build the main pieces
Step 4: Multi-stage copy
Why: Build tools must not ship. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: COPY --from=build. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 10: multistage note
Type this:
printf '%s\n' 'FROM node:20 AS build' 'WORKDIR /src' 'COPY package.json .' 'RUN npm install' 'COPY . .' 'RUN npm run build' 'FROM nginx:1.27-alpine' 'COPY --from=build /src/dist /usr/share/nginx/html' > /tmp/shrink/Dockerfile.ms && head -3 /tmp/shrink/Dockerfile.ms
You will see:
FROM node:20 AS build
WORKDIR /src
COPY package.json .
What it means: Build tools stay in build stage.
Example 11: final from
Type this:
grep '^FROM' /tmp/shrink/Dockerfile.ms | tail -1
You will see:
FROM nginx:1.27-alpine
What it means: Only nginx ships.
Example 12: copy from
Type this:
grep 'COPY --from' /tmp/shrink/Dockerfile.ms
You will see:
COPY --from=build /src/dist ...
What it means: Artifacts only.
Step 5: Omit dev deps
Why: DevDependencies bloat Node images. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: npm omit=dev / pip no-cache. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 13: omit
Type this:
echo 'For Node: RUN npm ci --omit=dev'
You will see:
For Node: RUN npm ci --omit=dev
What it means: No devDeps.
Example 14: pip
Type this:
echo 'For Python: pip install --no-cache-dir -r requirements.txt'
You will see:
For Python: pip install --no-cache-dir -r requirements.txt
What it means: No pip cache.
Example 15: apt clean
Type this:
echo 'If you must use apt: rm -rf /var/lib/apt/lists/*'
You will see:
If you must use apt: rm -rf /var/lib/apt/lists/*
What it means: Clean lists.
Step 6: Add dockerignore
Why: Junk in context becomes layers. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: .dockerignore. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 16: ignore
Type this:
printf '%s\n' '.git' 'node_modules' 'tests' '*.md' 'Dockerfile*' > /tmp/shrink/.dockerignore && cat /tmp/shrink/.dockerignore
You will see:
.git
node_modules
tests
*.md
Dockerfile*
What it means: Lean context.
Example 17: du context
Type this:
du -sh /tmp/shrink
You will see:
20K /tmp/shrink
What it means: Small.
Example 18: mode
Type this:
stat -c '%a' /tmp/shrink/.dockerignore
You will see:
644
What it means: Safe.
4. Automate and verify
Step 7: Rebuild slim
Why: Rebuild proves the new size. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker build -t slim. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 19: build slim
Type this:
docker build -t shop-slim:1.0 -f /tmp/shrink/Dockerfile.slim /tmp/shrink
You will see:
Successfully tagged shop-slim:1.0
What it means: Slim built.
Example 20: images
Type this:
docker images 'shop-*' --format '{{.Repository}}:{{.Tag}} {{.Size}}'
You will see:
shop-fat:1.0 77MB
shop-slim:1.0 45MB
What it means: Side by side.
Example 21: tag
Type this:
docker tag shop-slim:1.0 shop-slim:latest && echo tagged
You will see:
tagged
What it means: Alias.
Step 8: Compare sizes
Why: Numbers convince the team. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: Compare before after. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 22: compare
Type this:
python3 -c 'print("fat_vs_slim: prefer alpine+only runtime")'
You will see:
fat_vs_slim: prefer alpine+only runtime
What it means: Rule.
Example 23: under 100
Type this:
docker images shop-slim:1.0 --format '{{.Size}}'
You will see:
45MB
What it means: Under 100MB goal.
Example 24: goal
Type this:
echo 'Goal: static site under 100MB; we are at ~45MB'
You will see:
Goal: static site under 100MB; we are at ~45MB
What it means: Met.
Step 9: Dive-style history
Why: history is the poor man dive. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: history --human. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 25: hist slim
Type this:
docker history shop-slim:1.0 --human | head -5
You will see:
IMAGE ... nginx alpine ...
What it means: Few small layers.
Example 26: no apt
Type this:
docker history shop-slim:1.0 | grep -i apt || echo 'no_apt_layer'
You will see:
no_apt_layer
What it means: Clean.
Example 27: layers
Type this:
docker history -q shop-slim:1.0 | wc -l
You will see:
3
What it means: Fewer layers.
5. Harden and accept
Step 10: Run slim image
Why: Slim must still run. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker run. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 28: run
Type this:
docker rm -f shop-slim >/dev/null 2>&1; docker run -d --name shop-slim -p 8088:80 shop-slim:1.0 && echo started
You will see:
started
What it means: Running.
Example 29: ps
Type this:
docker ps --filter name=shop-slim --format '{{.Names}} {{.Ports}}'
You will see:
shop-slim 0.0.0.0:8088->80/tcp
What it means: Mapped.
Example 30: wait
Type this:
sleep 1; echo up
You will see:
up
What it means: Ready.
Step 11: curl still works
Why: Behaviour must stay green. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: curl health. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 31: curl
Type this:
curl -s http://127.0.0.1:8088/
You will see:
<h1>shop</h1>
What it means: Content OK.
Example 32: code
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8088/
You will see:
200
What it means: OK.
Example 33: logs
Type this:
docker logs shop-slim 2>&1 | tail -2
You will see:
GET / ...
What it means: Access.
Step 12: Acceptance checklist
Why: Kabir signs off when size and health both pass. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: All green under 100MB. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 34: under
Type this:
docker images shop-slim:1.0 --format '{{.Size}}' | grep -E 'MB|B' && echo size_ok
You will see:
45MB
size_ok
What it means: Under 100MB.
Example 35: rm fat optional
Type this:
echo 'Keep fat tag only for teaching diffs'
You will see:
Keep fat tag only for teaching diffs
What it means: Lab note.
Example 36: Sign-off
Type this:
echo 'Kabir: PASS'
You will see:
Kabir: PASS
What it means: Done.
Check yourself
Q1. Who shrank the Paytm UI image?
Kabir
Q2. Fat base in the lab?
ubuntu:22.04
Q3. Slim base?
nginx:1.27-alpine
Q4. Size goal?
Under 100MB
Q5. Command to see layer sizes?
docker history
Q6. Host port for slim lab?
8088
Friends, you shrank a fat site image under 100 MB without breaking curl. Did you understand? Let us try it once more. Apply the same Alpine multi-stage idea to your Node or Python API from the earlier projects.
मित्रांनो, तुम्ही fat site image 100 MB खाली आणली आणि curl टिकवला. आधीच्या Node किंवा Python API वर तोच Alpine multi-stage लागू करा.
दोस्तों, आपने fat site image 100 MB से कम की और curl बचाया. पहले वाली Node या Python API पर वही Alpine multi-stage लागू करें.
Lab: try at home
- Build a fat baseline on purpose.
- Read history for the big layer.
- Write Alpine Dockerfile.
- Rebuild and compare sizes.
- curl the slim container.
Recap
- Measure before you shrink.
- Alpine cuts base weight.
- Multi-stage drops compilers.
- omit=dev and no-cache help.
- Under 100MB is a clear goal for static sites.