Docker Mastery: Real-World Patterns, Gotchas, and Power Moves
Docker is everywhere, but most devs only scratch the surface. Here’s a practical, code-heavy guide to using Docker like a pro—beyond just docker run and docker-compose up.
Multi-Stage Builds: Smaller, Safer Images
# --- Build Stage ---
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json .
RUN npm install --frozen-lockfile
COPY . .
RUN npm run build
# --- Production Stage ---
FROM node:20-alpine AS prod
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY package.json .
CMD ["node", "dist/server.js"]Why?
| Pattern | Benefit |
|---|---|
| Multi-stage | Smaller images, no dev deps |
| COPY --from | Only what you need in prod |
| Alpine base | Lower attack surface, smaller |
Debugging Containers: Attach, Logs, and Shell
# See logs
docker logs <container>
# Attach a shell (if bash is installed)
docker exec -it <container> bash
# For Alpine/minimal images
docker exec -it <container> shPerformance Tips
- Use
.dockerignoreto avoid copying node_modules, build artifacts, etc. - Pin base image versions (avoid
latest). - Layer order matters: put rarely-changed lines (like
RUN npm install) first. - Use healthchecks for robust containers:
HEALTHCHECK CMD curl --fail http://localhost:3000/health || exit 1Compose: Real-World Example
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Common Gotchas
| Problem | Solution/Tip |
|---|---|
| "Works on my machine" | Use Compose for parity, pin image versions |
| File permissions in container | Set USER and chown as needed |
| Large images | Multi-stage, .dockerignore, Alpine |
| Slow builds | Order layers, cache dependencies |