⚙️ Production Ready Docker Aplications: Mini Projects - A 🐳🚀
Deploying a Single-Service Container (Monolithic Setup)

DevOps/Cloud Engineer focused on building, automating, and scaling cloud native systems. I write about Linux, CI/CD pipelines, Docker, Terraform, Kubernetes, and AWS, sharing practical insights from real-world DevOps workflows.
Project A: Single-Service Production Container (Python App)
Goal: Build a production ready containerized service using Docker best practices: secure, optimized, observable, and resilient.
Project Structure

Why this matters
• Clean separation of concerns
• No secrets committed
• Easy CI/CD integration later
Step1: Create a Python Service
(app/main.py) (app/requirements.txt)
app/main.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import osclass HealthHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/health":
self.send_response(200)
self.end_headers()
self.wfile.write(b"OK")
else:
self.send_response(404)
self.end_headers()if name == "main":
port = int(os.getenv("APP_PORT", 8080))
server = HTTPServer(("0.0.0.0", port), HealthHandler)
print("Starting server on port", port)
server.serve_forever()
app/requirements.txt
No external deps yet (kept minimal intentionally)
**Production Insight:
**• Logs go to stdout/stderr
• Health endpoint included for orchestration readiness
Step 2: Secrets Handling (No Hardcoding)
.env.example
APP_PORT=8080
APP_ENV=production
Note: Never commit real .env files. Only .env.example goes to Git.
Step 3: .dockerignore (Security + Performance)
.dockerignore
.git
.gitignore
pycache/
.env
.env.*
*.log
node_modules
Why this is critical:
• Prevents secrets leaking into images
• Reduces build context size
• Smaller → safer → faster images
Step 4: Multi-Stage Production Dockerfile
Dockerfile
#Build Stage
FROM python:3.12-slim AS builder
WORKDIR /app
COPY app/requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt#Runtime Stage
FROM python:3.12-slim#Security: create non-root user
RUN useradd -m appuser
WORKDIR /app#Copy only what we need
COPY --from=builder /install /usr/local
COPY app/ .#Environment
ENV PYTHONUNBUFFERED=1
ENV APP_PORT=8080#Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --retries=3
CMD curl -f http://localhost:8080/health || exit 1#Drop privileges
USER appuserEXPOSE 8080
CMD ["python", "main.py"]
Security (Shift-Left)
• Multi-stage build
• Minimal base image (slim)
• Non-root user
• No secrets in image
• No unnecessary packages
Security is not an afterthought, it is shift-left and baked into every Dockerfile.
Step 5: Build the Image
docker build -t python-prod:1.0 .

While building the image we received error:
ERROR: failed to build: failed to solve: failed to compute cache key: failed to calculate checksum of ref 4f55ec3b-efa6-49d1-914e-357d8a02e471::k4khh8rjb1cm1wa92ayu97qar: "/install": not found
this is a classic multi-stage Dockerfile issue
The Problem (Why the build failed)
COPY --from=builder /install /usr/local
"/install": not found
What Happened?
• In the builder stage, /install is only created if pip installs something
• Your requirements.txt is empty
• So pip install ... --prefix=/install does not create /install
• When the runtime stage tries to copy it → Docker fails
Solution: Add a Dependency, Update app/requirements.txt
gunicorn==21.2.0
Now rebuild
docker build -t python-prod:1.0 .

Check image size
docker images python-prod

Run with Production Controls

Run it:
chmod +x docker-run.sh
./docker-run.sh

Why we are getting application status as “Unhealthy“, Nothing is “broken” in our app. The healthcheck is failing,
What’s Actually Wrong (Root Cause)
Our container is running: Starting server on port 8080
But Docker reports: Up 2 minutes (unhealthy)
Why?
• Dockerfile has this healthcheck:
HEALTHCHECK --interval=30s --timeout=3s --retries=3
CMD curl -f http://localhost:8080/heath || exit 1
However, python:3.12-slim does NOT include curl

So Docker tries to run: curl http://localhost:8080/health
And inside the container: /bin/sh: curl: not found
That makes the container unhealthy, even though the app itself is working.
Minimal images:
• Smaller attack surface
• Missing common tools (curl, wget, bash)
Solution: Use Python for Healthcheck (No extra packages. No bloat)
Update the /app/healthcheck.py
import urllib.request
import sys
URL = "http://127.0.0.1:8080/health"
try:
with urllib.request.urlopen(URL, timeout=3) as response:
if response.status == 200:
sys.exit(0)
else:
sys.exit(1)
except Exception as e:
sys.exit(1)
Why container was unhealthy earlier
• Healthcheck ran before server fully started
• localhost DNS resolution glitch → fixed by 127.0.0.1
• Duplicate COPY confused file layout
• Short start-period
Step 6: Rebuild Again (New build with 1.1 tag
(Best practice: always stop the container first and then remove it)
docker stop python-prod
docker rm -f python-prod#Build and Run
docker build -t python-prod:1.1 .
docker run -d --name python-prod --env-file .env --memory="256m" --cpus="0.5" --restart unless-stopped -p8080:8080 python-prod:1.1



verify the status
docker inspect --format='{{.State.Health.Status}}' python-prod

Access the site on browser: http://<server_ip:8080> & http://<server_ip:8080/health>


Tip: Even though the application was running correctly, the container was marked unhealthy. The issue wasn’t the app, it was the healthcheck relying on a binary (curl) that doesn’t exist in minimal images. This reinforced an important production lesson: healthchecks must match the base image, not assumptions.
Note: Minimal images improve security, but require deliberate operational design, more secure, smaller → but missing common tools like curl.
Step: 8 Restart Policy

Step 7: Git Repository Setup


Note: .dockerignore has NOTHING to do with Git.
Why .env is still going to Git:
• .dockerignore → only affects Docker build context
• .gitignore → controls what Git tracks and commits
So if .env is excluded in .dockerignore but not in .gitignore, Git will still track it.
Tip: Add .env to .gitignore
Key Takeaway from this Project
• Minimal Python image
• Non-root user
• Proper healthcheck
• Clean production-ready container