Docker Cheatsheet
Best Practices
Use this Docker reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Image Size
- Use minimal base images:
alpine,slim,distroless, orscratch. - Multi-stage builds — never ship compilers, test deps, or source to production.
- Combine
RUNcommands with&&and clean up in the same layer:
RUN apt-get update && apt-get install -y --no-install-recommends \ curl ca-certificates \ && rm -rf /var/lib/apt/lists/*
- Use
.dockerignoreto exclude everything not needed in the build context.
Layer Cache Efficiency
# Copy dependency manifests BEFORE source code
COPY package*.json ./
RUN npm ci --omit=dev
# Source changes only bust layers from here
COPY . .- Order instructions from least to most frequently changed.
- Use
--mount=type=cache(BuildKit) for package manager caches:
# syntax=docker/dockerfile:1
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=devSecurity Hardening
# Never run as root RUN addgroup -S app && adduser -S -G app app USER app # Read-only root filesystem at runtime # docker run --read-only --tmpfs /tmp myapp # Drop all Linux capabilities and add only what's needed # docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp
| Practice | Command / Config |
|---|---|
| Non-root user | USER app in Dockerfile |
| Read-only rootfs | docker run --read-only |
| No new privileges | docker run --security-opt no-new-privileges |
| Drop capabilities | docker run --cap-drop ALL |
| Pin image digests | FROM nginx@sha256:abc... |
| Scan images | docker scout cves, trivy image |
Reproducibility
# Pin base image tags (never use bare "latest" in production Dockerfiles) FROM node:22.14-alpine3.21 # Or pin by digest for true immutability FROM node:22@sha256:abc123...
- Pin dependency versions in
package-lock.json,requirements.txt,go.sum. - Pass
GIT_COMMITandBUILD_DATEasARG/LABELfor traceability.
Health Checks
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ CMD wget -qO- http://localhost:3000/health || exit 1
- Use
/healthor/healthzendpoint — check DB connectivity too. depends_on: condition: service_healthyin Compose waits for this.
Secrets
- Never put secrets in
ENVinstructions orARG— they appear indocker history. - Inject at runtime:
-e SECRET=...,--env-file .env, Docker secrets, or secret mounts.
# BuildKit secret mount (never stored in layer) # syntax=docker/dockerfile:1 RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ npm ci
docker build --secret id=npmrc,src=$HOME/.npmrc .
Signals and Graceful Shutdown
# Exec form ensures PID 1 = your process, receives signals directly CMD ["node", "server.js"] # If you need a shell wrapper, use tini FROM node:22-alpine RUN apk add --no-cache tini ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "server.js"]
Logging
- Write logs to stdout/stderr — let Docker (and the orchestrator) collect them.
- Never write logs to files inside the container; the filesystem is ephemeral.
- Set
--log-opt max-size=10m --log-opt max-file=3to cap disk usage.
Compose for Local Dev
services:
app:
build:
context: .
target: dev # dev stage includes hot-reload tooling
volumes:
- .:/app
- /app/node_modules # anonymous volume hides host node_modules
environment:
- NODE_ENV=developmentGeneral Rules
| Do | Avoid |
|---|---|
| Use multi-stage builds | Single-stage images with build tools |
| Pin image tags/digests | FROM ubuntu:latest in production |
| Run as non-root | USER root |
| One process per container | Bundling app + database in one image |
| Use named volumes for state | Storing DB data in container layer |
Validate with docker scout / trivy | Shipping unscanned images |
Set WORKDIR explicitly | Relying on / as working directory |
| Label images with OCI metadata | No traceability |
Use --init or tini | PID 1 zombie reaping problems |
Useful One-liners
# Remove all stopped containers, unused networks, dangling images, build cache docker system prune -af --volumes # Show which processes are using the most disk docker system df -v # Get a shell in a distroless/scratch container via a debug image docker run -it --pid=container:myapp --network=container:myapp \ busybox sh # Check open ports inside a container without exec docker inspect -f '{{.NetworkSettings.Ports}}' mycontainer # Follow logs for multiple services in Compose docker compose logs -f web api