Docker for Backend Engineers: Beyond the Basics
Docker for Backend Engineers: Beyond the Basics
Docker is ubiquitous in backend engineering. But most of us stop at basic commands. Here's what you should know to use Docker effectively in production.
Multi-Stage Builds
The difference between a 1GB image and a 50MB image often comes down to multi-stage builds.
Before: Single-Stage Build
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["npm", "start"]
Problem: Includes dev dependencies, source files, and build tools in the final image.
After: Multi-Stage Build
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]
Result: Smaller images, faster deployments, reduced attack surface.
Layer Caching Strategy
Docker builds layers sequentially. Leverage this for faster builds.
Optimization Rules
- Copy package files first - Install dependencies before copying source code
- Separate static assets - Cache what doesn't change
- Order matters - Most stable layers first
Example: Optimized Python Build
FROM python:3.11-slim
# Install system dependencies (rarely changes)
RUN apt-get update && apt-get install -y \\
gcc \\
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies (changes occasionally)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code (changes frequently)
COPY . /app
WORKDIR /app
CMD ["python", "main.py"]
Health Checks
Always include health checks in production containers.
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
CMD curl -f http://localhost:3000/health || exit 1
This enables:
- Automatic container restarts
- Load balancer integration
- Monitoring and alerting
.dockerignore is Critical
Treat it like .gitignore but for containers.
node_modules
npm-debug.log
.git
.env
*.md
.vscode
coverage
.DS_Store
Why? Faster builds, smaller contexts, no accidentally leaking secrets.
Environment-Specific Images
Use build arguments for environment-specific builds.
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
RUN if [ "$NODE_ENV" = "development" ]; then \\
npm install; \\
else \\
npm ci --only=production; \\
fi
Build for different environments:
# Production
docker build --build-arg NODE_ENV=production -t app:prod .
# Development
docker build --build-arg NODE_ENV=development -t app:dev .
Security Best Practices
1. Use Official Base Images
FROM python:3.11-slim # Official Python image
FROM node:18-alpine # Minimal Node image
2. Run as Non-Root User
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
3. Scan for Vulnerabilities
docker scan my-image:latest
Or use tools like:
- Trivy
- Snyk
- Anchore
Docker Compose for Local Development
docker-compose.yml for consistent dev environments:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
depends_on:
- db
volumes:
- ./src:/app/src # Hot reload
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Production Deployment Checklist
Before deploying:
- [ ] Multi-stage build implemented
- [ ] Image size optimized (< 200MB for most apps)
- [ ] Health checks configured
- [ ] Non-root user specified
- [ ] Secrets managed externally (not in image)
- [ ] Logs sent to stdout/stderr
- [ ] Resource limits defined (CPU, memory)
- [ ] Vulnerability scan passed
Common Pitfalls
1. Building from source in production
Don't: Install dev tools in production images
Do: Use multi-stage builds to separate build and runtime
2. Hardcoded secrets
Don't: ENV API_KEY=secret123
Do: Use environment variables or secret management
3. Running as root
Don't: Leave default user
Do: Create and use a non-root user
Conclusion
Docker is more than a deployment tool—it's a way to ensure consistency across environments. Master these patterns and your deployments become predictable, secure, and fast.
Key Takeaways:
- Multi-stage builds reduce image size by 80%+
- Layer caching speeds up builds dramatically
- Security must be built in, not bolted on
- Health checks enable self-healing systems
The difference between good and great Docker usage is attention to these details.