Docker for Backend Developers: A Practical Guide
Docker has revolutionized how we develop, ship, and run applications. As a backend developer, understanding Docker is essential. Let’s explore how to containerize your backend apps.
Why Docker?#
“It works on my machine” - We’ve all heard this. Docker solves this by packaging your application with all its dependencies.
┌─────────────────────────────────────┐
│ Docker Container │
├─────────────────────────────────────┤
│ Your Application │
│ Runtime (Node.js, Python, Java) │
│ Dependencies │
│ System Libraries │
│ OS (Linux) │
└─────────────────────────────────────┘
Core Concepts#
Images vs Containers#
Image → Blueprint (like a class)
Container → Running instance (like an object)
Dockerfile#
A Dockerfile is your recipe for building an image:
# Start from a base image
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Expose the port
EXPOSE 3000
# Start the application
CMD ["node", "server.js"]
Building and Running#
Build an Image#
docker build -t my-backend-app:1.0 .
Run a Container#
docker run -d -p 3000:3000 --name my-app my-backend-app:1.0
Essential Commands#
# List running containers
docker ps
# View logs
docker logs my-app
# Stop container
docker stop my-app
# Remove container
docker rm my-app
# List images
docker images
Docker Compose#
For multi-container applications, use Docker Compose:
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db
- redis
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
postgres_data:
Run with:
docker-compose up -d
Best Practices#
1. Use Multi-Stage Builds#
Reduce image size by separating build and runtime:
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
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
CMD ["node", "dist/server.js"]
2. Use .dockerignore#
node_modules
.git
.env
*.log
Dockerfile
docker-compose.yml
3. Don’t Run as Root#
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser
USER appuser
4. Use Specific Tags#
# ❌ Bad - unpredictable
FROM node:latest
# ✅ Good - predictable
FROM node:18.19.0-alpine
Environment Variables#
Handle configuration with environment variables:
docker run -d \
-e DATABASE_URL=postgres://... \
-e JWT_SECRET=mysecret \
my-backend-app
Or use an env file:
docker run -d --env-file .env my-backend-app
Health Checks#
Add health checks to your containers:
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q --spider http://localhost:3000/health || exit 1
Conclusion#
Docker is a game-changer for backend development. It ensures consistency across environments, simplifies deployment, and makes your applications portable.
Start containerizing your apps today! 🐳