Docker Masterclass
Master Docker fundamentals and advanced techniques, from writing efficient Dockerfiles to orchestrating multi-container applications with Docker Compose.
DevOpsIntermediate20 min read
AI Summary
Quick context for Docker Masterclass
Docker Masterclass is a intermediate guide in DevOps. Master Docker fundamentals and advanced techniques, from writing efficient Dockerfiles to orchestrating multi-container applications with Docker Compose.. Key topics: containers, deployment, devops, docker.
## Why Docker?
Docker packages applications into lightweight, portable containers that run consistently across any environment. It eliminates the "works on my machine" problem by bundling code with its dependencies, runtime, and system libraries.
For developers, Docker simplifies local development, speeds up onboarding, and creates reproducible builds. For operations, it enables efficient resource utilization and consistent deployments.
## Writing Efficient Dockerfiles
A Dockerfile defines the steps to build a container image. Writing efficient Dockerfiles reduces build times, image sizes, and security surface area.
```dockerfile
# Use specific versions, never 'latest'
FROM node:20-alpine AS base
# Set working directory
WORKDIR /app
# Copy dependency files first for better layer caching
COPY package.json pnpm-lock.yaml ./
# Install dependencies
RUN corepack enable && pnpm install --frozen-lockfile --prod
# Copy application source
COPY . .
# Build the application
RUN pnpm build
# Production stage — minimal image
FROM node:20-alpine AS production
WORKDIR /app
# Copy only what's needed
COPY --from=base /app/dist ./dist
COPY --from=base /app/node_modules ./node_modules
COPY --from=base /app/package.json ./
# Run as non-root user
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]
```
### Key Principles
- **Use multi-stage builds** to separate build dependencies from runtime
- **Order instructions by change frequency** — static dependencies first, source code last
- **Pin base image versions** for reproducible builds
- **Use `.dockerignore`** to exclude unnecessary files from the build context
```text
# .dockerignore
node_modules
.git
dist
.env
.env.local
*.md
coverage
.turbo
```
## Docker Compose for Local Development
Docker Compose defines multi-container environments in a single YAML file. This is ideal for local development with databases, caches, and message queues.
```yaml
# docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
target: base
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
DATABASE_URL: postgresql://postgres:secret@db:5432/myapp
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- pg data-removed:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pg data-removed:
```
## Networking
Docker networks allow containers to communicate with each other. Bridge networks are the default for single-host setups, while overlay networks span multiple hosts.
```bash
# Create a custom network
docker network create app-network
# Run containers on the network
docker run --network app-network --name api my-api:latest
docker run --network app-network --name db postgres:16
# Containers can reference each other by name
# api connects to db at "db:5432"
```
## Managing Data with Volumes
Volumes persist data beyond a container's lifecycle. Named volumes are managed by Docker; bind mounts map host directories directly.
```bash
# Named volume for database persistence
docker volume create pgdata
docker run -v pg data-removed:/var/lib/postgresql/data postgres:16
# Bind mount for live code reloading during development
docker run -v $(pwd)/src:/app/src my-app:dev
```
## Docker Best Practices for Production
### Security
- Run containers as non-root users
- Scan images for vulnerabilities with tools like Trivy or Docker Scout
- Use read-only file systems where possible
- Limit container resources with CPU and memory constraints
```yaml
services:
api:
image: my-api:latest
read_only: true
tmpfs:
- /tmp
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
security_opt:
- no-new-privileges:true
```
### Health Checks
Health checks let Docker and orchestrators detect and replace unhealthy containers.
```dockerfile
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
```
### Logging
Use stdout and stderr for application logs. Docker captures these and makes them available through logging drivers.
```typescript
// Application logs go to stdout
console.log(JSON.stringify({
level: "info",
message: "Server started",
port: 3000,
timestamp: new Date().toISOString(),
}));
```
## Debugging Containers
```bash
# View running containers
docker ps
# Inspect container details
docker inspect
# View container logs
docker logs -f
# Execute commands inside a running container
docker exec -it sh
# View resource usage
docker stats
```
## When to Use Docker vs. Alternatives
Docker is excellent for most development and deployment scenarios. Consider alternatives for specific cases: Podman for rootless containers, Nix for reproducible development environments, or platform-native solutions like App Runner or Cloud Run for simplified deployments without managing infrastructure.
## Next Steps
Once comfortable with Docker, explore Docker Swarm for simple orchestration, Kubernetes for complex deployments, and CI/CD integration to build and push images automatically on every commit.
Continue with related content
Suggested next reads and tools from the knowledge graph.
toolrelates to
GitHub
Platform for version control and collaboration using Git
DevOps#git#version-control#collaboration
toolrelates to
Vercel
Cloud platform for frontend frameworks and serverless functions
Cloud#deployment#hosting#serverless
toolrelates to
Prisma
Next-generation ORM for TypeScript and JavaScript