Skip to content

Docker Cheat Sheet

Container Lifecycle

docker run -d --name app -p 8080:80 nginx:1.25
docker run -it --rm ubuntu bash         # Interactive, auto-remove
docker stop app                         # Graceful stop (SIGTERM)
docker kill app                         # Force stop (SIGKILL)
docker rm app                           # Remove container
docker rm -f $(docker ps -aq)           # Remove all containers

Common Run Flags

Flag Purpose
-d Detached (background)
-it Interactive terminal
--rm Auto-remove on exit
-p 8080:80 Port mapping host:container
-v /host:/cont Bind mount
-v vol:/cont Named volume
-e KEY=VAL Environment variable
--env-file .env Env file
--network net Attach to network
--restart unless-stopped Restart policy
--memory 512m Memory limit
--cpus 0.5 CPU limit

Images

docker build -t myapp:v1 .             # Build from Dockerfile
docker build -t myapp:v1 -f Dockerfile.prod .
docker tag myapp:v1 registry.io/myapp:v1
docker push registry.io/myapp:v1
docker pull nginx:1.25
docker images                           # List images
docker image prune -a                   # Remove unused images
docker history myapp:v1                 # Show layers

Dockerfile Best Practices

# Use specific base image (pin digest for prod)
FROM golang:1.22-alpine AS builder
WORKDIR /app

# Cache dependencies (copy go.mod first)
COPY go.mod go.sum ./
RUN go mod download

# Copy source and build
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .

# Minimal runtime image
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
CMD ["/server"]
DO:
  ✓ Use multi-stage builds
  ✓ Order layers by change frequency (deps before code)
  ✓ Use .dockerignore
  ✓ Run as non-root
  ✓ Use COPY not ADD (unless extracting tar)
  ✓ Pin base image versions

DON'T:
  ✗ Use :latest in production
  ✗ Store secrets in image layers
  ✗ Install unnecessary packages
  ✗ Run as root
  ✗ Use ADD for remote URLs

Under the hood: Each Dockerfile instruction creates a new image layer. Layers are cached and reused across builds. This is why ordering matters: put COPY go.mod go.sum ./ and RUN go mod download before COPY . . — dependencies change rarely, so the download layer stays cached across code changes. Changing layer order can turn a 10-second build into a 5-minute rebuild.

Gotcha: docker stop sends SIGTERM and waits 10 seconds (default --stop-timeout), then sends SIGKILL. If your app does not handle SIGTERM, it always takes the full 10 seconds to stop. The same PID 1 problem exists here as in Kubernetes — use exec form in ENTRYPOINT or a lightweight init like tini.

Debugging

docker logs app --tail=50 -f           # Tail logs
docker exec -it app bash               # Shell into container
docker exec app env                    # Check env vars
docker inspect app                     # Full metadata
docker inspect -f '{{.State.Status}}' app
docker stats                           # Live resource usage
docker top app                         # Processes in container
docker diff app                        # Changed files
docker cp app:/path/file ./local       # Copy file out

Networking

docker network ls
docker network create mynet
docker network inspect mynet
docker run --network mynet --name api myapp
docker run --network mynet --name db postgres
# api can reach db at hostname "db"

# Expose specific IP
docker run -p 127.0.0.1:8080:80 nginx
Network Use Case
bridge (default) Container-to-container on same host
host Container uses host networking directly
none No networking
custom bridge Containers communicate by name

Volumes

docker volume create data
docker volume ls
docker volume inspect data
docker volume rm data
docker volume prune                    # Remove unused

# Mount types:
docker run -v mydata:/data app         # Named volume
docker run -v /host/path:/data app     # Bind mount
docker run --tmpfs /tmp app            # tmpfs (in-memory)

Docker Compose

services:
  api:
    build: .
    ports: ["8080:8080"]
    environment:
      DB_HOST: db
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: pg_isready -U postgres
      interval: 5s
      retries: 5

volumes:
  pgdata:
docker compose up -d                   # Start all
docker compose down                    # Stop + remove
docker compose logs -f api             # Follow logs
docker compose ps                      # Service status
docker compose exec api bash           # Shell into service
docker compose build                   # Rebuild images

Cleanup

docker system prune                    # Remove stopped + unused
docker system prune -a --volumes       # Remove everything unused
docker system df                       # Show disk usage