So you know the basics of Docker — docker run, docker build, maybe some docker compose up. But do you know what happens when things break?
This guide walks you through 5 real-world Docker scenarios that will sharpen your skills around debugging, security, storage, and production-readiness.
Scenario 1: The Broken Build 🔨
Goal
Fix a broken Dockerfile, then optimize it to be 10x smaller and production-ready.
Setup
app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello from Docker!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt
Flask==2.3.0
❌ The Broken Dockerfile
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install python3
COPY . /opt/app
RUN pip install -r requirements.txt
USER root
CMD python /opt/app/app.py
Tasks
1. Troubleshoot the Build
Run:
docker build -t broken-app .
There are at least 3 issues hiding in that Dockerfile:
-
pipis never installed -
apt-get installis missing the-yflag (hangs waiting for input) -
COPYhappens before dependency install — busting the cache on every change
2. Fix Layer Caching
Copy requirements.txt first, install deps, then copy the rest of the app. This way, changing app.py won't trigger a full pip install on every build.
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . /opt/app
3. Optimize the Image
Switch from ubuntu:latest to a slim base:
FROM python:3.12-slim
WORKDIR /opt/app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
Target: < 150MB image size ✅
4. Security Hardening
# Run as non-root
USER 1001
Run with a read-only filesystem:
docker run --read-only --tmpfs /tmp myapp:latest
Scenario 2: The Ghost Data 👻
Goal
Understand Docker's storage lifecycle and how to make data actually persist.
Tasks
1. Run an Ephemeral Container
docker run -d --name web-test -p 8080:80 nginx
2. Write Data Inside the Container
docker exec -it web-test bash
echo "hello" > /usr/share/nginx/html/test.txt
Visit http://localhost:8080/test.txt — it's there!
3. Destroy & Recreate
docker stop web-test
docker rm web-test
docker run -d --name web-test2 -p 8080:80 nginx
The file is gone. Why?
Because the container filesystem is ephemeral — it only lives as long as the container does.
4. Named Volume Persistence
docker volume create web-data
docker run -d \
--name web-test \
-p 8080:80 \
-v web-data:/usr/share/nginx/html \
nginx
5. Verify Persistence
docker exec -it web-test bash
echo "persistent data" > /usr/share/nginx/html/test.txt
Stop and remove the container, then start a new one with the same volume — your file will still be there. 🎉
6. Cleanup
docker system prune -f
Note:
system pruneremoves stopped containers, dangling images, and unused networks — but not named volumes unless you add--volumes.
Scenario 3: The Flaky Database Connection 🐘
Goal
Fix service startup dependency issues in Docker Compose.
The Broken docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
environment:
- DB_HOST=postgres
depends_on:
- postgres
postgres:
image: postgres:15
environment:
- POSTGRES_PASSWORD=secret
Tasks
1. Run the Stack
docker compose up
The problem: depends_on only waits for the container to start, not for Postgres to be ready to accept connections. Your web app crashes on startup.
2. Add a Healthcheck to Postgres
postgres:
image: postgres:15
environment:
- POSTGRES_PASSWORD=secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
3. Fix depends_on with a Condition
web:
depends_on:
postgres:
condition: service_healthy
Now your web service won't start until Postgres passes its healthcheck. ✅
4. Move Secrets to .env
.env
POSTGRES_PASSWORD=secret
docker-compose.yml
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
5. Keep Secrets Out of Git
.gitignore
.env
Never commit .env files. Ever.
Scenario 4: The Evil Image Scanner 🔍
Goal
Find and fix vulnerabilities hiding in your Docker images.
Tasks
1. Build a Vulnerable Image
FROM nginx:1.21.0
2. Install Trivy
brew install aquasecurity/trivy/trivy
Or on Linux:
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
3. Scan the Image
trivy image nginx:1.21.0
You'll see a wall of HIGH and CRITICAL CVEs. This is what ships to production when nobody checks.
4. Fix the Base Image
FROM nginx:1.25-bookworm
Or go even lighter:
FROM nginx:alpine
5. Rescan
trivy image nginx:1.25-bookworm
Expected: 0 CRITICAL vulnerabilities ✅
Make image scanning part of your CI/CD pipeline — not an afterthought.
Scenario 5: The Limited Resource Box 📦
Goal
Run containers safely under strict resource constraints and enable offline portability.
Tasks
1. Build the Optimized Image
Use the hardened image from Scenario 1.
2. Run with Resource Limits
docker run -d \
--memory=256m \
--cpus=0.5 \
--read-only \
--tmpfs /tmp \
myapp:latest
| Flag | What it does |
|---|---|
--memory=256m |
Hard cap on RAM usage |
--cpus=0.5 |
Limits to half a CPU core |
--read-only |
Prevents filesystem writes |
--tmpfs /tmp |
Mounts writable in-memory temp dir |
3. Verify the Restrictions
docker exec -it <container_id> bash
touch /etc/test
# Permission denied ✅
Monitor resource usage live:
docker stats
4. Export the Image
docker save -o myapp.tar myapp:latest
5. Simulate an Offline Machine
docker rmi myapp:latest
6. Restore the Image
docker load -i myapp.tar
7. Run Without Internet
docker run myapp:latest
Works fully offline. 🚀 Great for air-gapped environments, demos, or CI runners without registry access.
What You've Learned 🎓
After completing all five scenarios, you now have hands-on experience with:
- ✅ Dockerfile debugging & optimization — fixing real build errors and shrinking image sizes
- ✅ Layer caching strategies — speeding up builds without reinstalling dependencies
- ✅ Volume persistence — understanding ephemeral vs. persistent storage
- ✅ Compose healthchecks — preventing race conditions between services
- ✅ Image vulnerability scanning — catching CVEs before they reach production
- ✅ Runtime security hardening — non-root users, read-only filesystems, resource limits
- ✅ Offline container portability — shipping containers without a registry
Want more? Drop a comment and I can turn this into a full GitHub repo with solutions, or expand each scenario into its own deep-dive post.
Top comments (0)