Docker for Developers: Complete Guide
Master Docker containerization for modern development. Learn best practices for creating, managing, and deploying containerized applications.
What is Docker?
Docker is a platform that enables developers to package applications into containers—standardized executable components that combine application source code with all the dependencies needed to run that code in any environment.
Why Use Docker?
Consistency Across Environments
The age-old problem: “It works on my machine!”
# Define your environment once
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
Benefits
- Portability - Run anywhere Docker is installed
- Isolation - Each container is isolated from others
- Efficiency - Lightweight compared to VMs
- Version Control - Dockerfile is version controlled
- Scalability - Easy to scale horizontally
Core Concepts
Images
A Docker image is a read-only template with instructions for creating a container:
# Pull an image from Docker Hub
docker pull node:18-alpine
# List images
docker images
# Remove an image
docker rmi node:18-alpine
Containers
Containers are runnable instances of images:
# Run a container
docker run -d -p 3000:3000 --name myapp node:18-alpine
# List running containers
docker ps
# Stop a container
docker stop myapp
# Remove a container
docker rm myapp
Creating Your First Dockerfile
Node.js Application
# Use official Node.js 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 port
EXPOSE 3000
# Set environment to production
ENV NODE_ENV=production
# Start application
CMD ["node", "server.js"]
Multi-stage Builds
Optimize image size with multi-stage builds:
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/server.js"]
Docker Compose
Manage multi-container applications:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
depends_on:
- db
volumes:
- ./src:/app/src
db:
image: postgres:15-alpine
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
postgres_data:
Run with:
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose down
Best Practices
1. Use Official Images
# Good
FROM node:18-alpine
# Avoid
FROM random-user/node-custom
2. Minimize Layers
# Bad - Creates multiple layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
# Good - Single layer
RUN apt-get update && \
apt-get install -y curl git && \
rm -rf /var/lib/apt/lists/*
3. Use .dockerignore
node_modules
npm-debug.log
.git
.env
*.md
.vscode
dist
coverage
4. Don’t Run as Root
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Switch to user
USER nodejs
5. Health Checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js || exit 1
Common Commands
# Build image
docker build -t myapp:latest .
# Run container with volume mount
docker run -v $(pwd):/app myapp
# Execute command in running container
docker exec -it myapp sh
# View container logs
docker logs -f myapp
# Inspect container
docker inspect myapp
# Clean up
docker system prune -a
Development Workflow
Hot Reload Setup
services:
app:
build: .
volumes:
- ./src:/app/src
- /app/node_modules
environment:
- NODE_ENV=development
command: npm run dev
Debug Mode
services:
app:
build: .
ports:
- "9229:9229"
command: node --inspect=0.0.0.0:9229 server.js
Networking
Bridge Network
# Create network
docker network create mynetwork
# Run containers on network
docker run --network mynetwork --name db postgres
docker run --network mynetwork --name app myapp
Docker Compose Networks
services:
frontend:
networks:
- frontend
backend:
networks:
- frontend
- backend
database:
networks:
- backend
networks:
frontend:
backend:
Volumes
Named Volumes
# Create volume
docker volume create mydata
# Use volume
docker run -v mydata:/app/data myapp
# List volumes
docker volume ls
# Remove volume
docker volume rm mydata
Security
Scan Images
# Docker Scout
docker scout quickview
# Trivy
trivy image myapp:latest
Environment Variables
# Use .env file
docker run --env-file .env myapp
# Docker secrets (Swarm mode)
echo "my_secret" | docker secret create db_password -
Deployment
Build for Production
# Build optimized image
docker build --target production -t myapp:1.0.0 .
# Push to registry
docker tag myapp:1.0.0 registry.com/myapp:1.0.0
docker push registry.com/myapp:1.0.0
Troubleshooting
# Container won't start
docker logs myapp
docker inspect myapp
# Check resource usage
docker stats
# Enter container
docker exec -it myapp sh
# Clean everything
docker system prune -a --volumes
Conclusion
Docker revolutionizes how we develop, ship, and run applications. By containerizing your applications, you ensure consistency across environments, simplify deployment, and improve scalability. Start small, experiment with Docker Compose, and gradually adopt best practices as you become more comfortable with containers.