Docker Essentials
Docker allows you to package applications with their dependencies into lightweight, portable containers. Here’s everything you need to get started.
Why Docker?
- Consistency — “It works on my machine” becomes irrelevant
- Isolation — Each service runs in its own container
- Portability — Deploy the same image anywhere
- Scalability — Spin up multiple instances effortlessly
Installing Docker
# macOS (via Homebrew)
brew install --cask docker
# Verify installation
docker --version
docker compose versionYour First Dockerfile
Create a Dockerfile in your project root:
# Use an official Node.js runtime
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy dependency files first (for caching)
COPY package*.json ./
RUN npm ci --only=production
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Start the application
CMD ["node", "server.js"]Essential Commands
# Build an image
docker build -t my-app .
# Run a container
docker run -d -p 3000:3000 --name my-app my-app
# View running containers
docker ps
# View logs
docker logs my-app
# Stop and remove
docker stop my-app
docker rm my-appDocker Compose
For multi-service applications, use docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://db:5432/myapp
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:# Start all services
docker compose up -d
# Stop all services
docker compose downBest Practices
- Use multi-stage builds to reduce image size
- Never store secrets in images — use environment variables
- Use
.dockerignoreto exclude unnecessary files - Pin specific versions of base images (
node:20-alpine, notnode:latest) - One process per container — keep containers focused