Docker isn't just for DevOps teams. Every developer should know how to build efficient, secure container images. Here's a practical guide.
Your First Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Multi-Stage Builds
Keep images small by separating build and runtime:
# Stage 1: Build
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Stage 2: Runtime
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /install /usr/local
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Result: image goes from 1.2GB to ~200MB.
Layer Caching
Docker caches each layer. Order matters:
# BAD: Any code change invalidates pip install cache
COPY . .
RUN pip install -r requirements.txt
# GOOD: Dependencies cached unless requirements.txt changes
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
Docker Compose for Development
# docker-compose.yml
services:
app:
build: .
ports:
- "8000:8000"
volumes:
- .:/app # live reload
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
docker compose up -d
docker compose logs -f app
docker compose exec app python manage.py migrate
Security Best Practices
# Don't run as root
RUN adduser --disabled-password --gecos '' appuser
USER appuser
# Use specific image tags, not :latest
FROM python:3.12.1-slim
# Don't copy secrets into images
# Use .dockerignore
# .dockerignore
.git
.env
__pycache__
*.pyc
node_modules
.venv
Health Checks
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
Useful Commands
docker build -t myapp:v1 .
docker run -d -p 8000:8000 --name myapp myapp:v1
docker logs -f myapp
docker exec -it myapp bash
docker system prune -a # clean up everything
docker stats # live resource usage
Key Takeaways
- Use multi-stage builds for smaller images
- Order Dockerfile commands for optimal layer caching
- Never run containers as root in production
- Use
.dockerignoreto keep images clean - Docker Compose for local development environments
6. Always add health checks for production containers
🚀 Level up your AI workflow! Check out my AI Developer Mega Prompt Pack — 80 battle-tested prompts for developers. $9.99
Top comments (0)