Docker changed how we build and ship software. This guide covers Docker from a developer's perspective — the concepts, commands, and patterns you'll use daily.
Core Concepts
An image is a read-only blueprint. A container is a running instance. A Dockerfile is the recipe. A volume persists data. A network connects containers.
Essential Commands
docker build --tag myapp:latest .
docker run --detach --name myapp --publish 3000:3000 myapp:latest
docker ps
docker logs --follow myapp
docker exec --interactive --tty myapp sh
docker stop myapp && docker rm myapp
docker system prune --all --forceMulti-Stage Builds
Separate the build environment from the runtime for smaller, more secure images.
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
RUN npm ci --production --ignore-scripts
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]Docker Compose for Local Dev
services:
app:
build: .
ports: ["3000:3000"]
volumes: [".:/app", "/app/node_modules"]
depends_on:
db: { condition: service_healthy }
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: myapp
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev"]
interval: 5s
retries: 5Wrapping Up
Start with a simple Dockerfile and Docker Compose for local dev. Add multi-stage builds for production. The goal: remove environment differences as a source of bugs.