DevTools Logo

Dockerfile Cheat Sheet

Quick reference for writing Dockerfiles: base images, instructions, multi-stage builds, best practices, and .dockerignore.

Containers & Cloud
docker
dockerfile
containers

A Dockerfile is a text file that describes how to assemble a container image. Each instruction creates a layer; the order of instructions determines the build steps and heavily influences cache efficiency.

Core Instructions

Table
InstructionPurposeExample
FROM image[:tag]Base image for the build stageFROM node:22-alpine
WORKDIR /appSet the working directory for RUN/COPY/CMDWORKDIR /usr/src/app
COPY src dstCopy files from the build context into the imageCOPY package.json ./
ADD src dstLike COPY, plus URL and tar auto-extractionADD https://ex.com/x.tgz /opt/
RUN commandExecute a shell command during buildRUN npm ci --omit=dev
ENV K=VSet an environment variable for the containerENV NODE_ENV=production
ARG nameBuild-time variable (not persisted)ARG NODE_VERSION=22
EXPOSE portDocument the port the app listens onEXPOSE 3000
CMD [...]Default command; overridable at run timeCMD ["node", "server.js"]
ENTRYPOINT [...]Fixed command; args appended by CMD or CLIENTRYPOINT ["docker-entrypoint.sh"]
USER userSwitch to a non-root userUSER node
HEALTHCHECK ...Health probe for orchestrationHEALTHCHECK CMD curl -f http://localhost/

A Minimal Node.js Dockerfile

dockerfile
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile

FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build

FROM node:22-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/.next ./.next
COPY --from=deps /app/node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
USER node
CMD ["node_modules/.bin/next", "start"]

Multi-Stage Builds

Multi-stage builds let you use one image to compile and a smaller one to run. The AS name gives each stage a label; COPY --from=<stage> copies files between stages.

dockerfile
FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /bin/app .

FROM gcr.io/distroless/static-debian12
COPY --from=build /bin/app /app
ENTRYPOINT ["/app"]

Benefits: the runtime image contains only the binary — no compiler, no source, no go toolchain.

Layer Caching & .dockerignore

Order matters for caching. Copy dependency manifests first, install, then copy source — a source change then reuses the cached dependency layer.

bash
# Copy dependency manifests first
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# Source changes below reuse the install layer above
COPY . .
dockerignore
node_modules
.git
.next
dist
*.log
.env

Common Pitfalls

Table
PitfallFix
Running as rootCreate a user and USER it before the final CMD.
RUN npm install instead of npm cinpm ci --frozen-lockfile is reproducible and faster in CI.
COPY . . before installing depsBreaks cache on every source change; copy manifests first.
Installing dev dependencies in runtimeUse multi-stage builds; keep dev deps only in the build stage.
Large base image (node:latest)Pin a slim/alpine or distroless base.
Storing secrets in ENVUse build secrets (RUN --mount=type=secret) instead.

References