Skip to main content

Dockerfile Best Practices Cheatsheet

This cheatsheet covers the most commonly used Dockerfile instructions and best practices, including base image selection, build instructions, file operations, startup configuration, multi-stage builds, and optimization tips.

Updated: 2026-07-16·32 commands

Base Image (FROM)

Choosing the right base image is the first step in writing a Dockerfile.

``dockerfile # Use a specific version (recommended) FROM node:20-alpine

# Name stages for multi-stage builds FROM golang:1.22 AS builder

# Use slim base images FROM python:3.12-slim

# Use distroless images (runtime only) FROM gcr.io/distroless/base-debian12 `

Build Instructions (RUN)

Execute commands during image build.

`dockerfile # Merge multiple commands to reduce layers RUN apt-get update && apt-get install -y \ curl \ git \ && rm -rf /var/lib/apt/lists/*

# Use exec form (recommended) RUN ["npm", "install"]

# Cache mount for faster builds RUN --mount=type=cache,target=/root/.npm \ npm install

# Use HERE document RUN <`

File Operations (COPY / ADD)

Copy files from the build context into the image.

`dockerfile # Copy specific files (recommended) COPY package.json package-lock.json ./

# Copy entire directory COPY src/ /app/src/

# Set ownership COPY --chown=node:node config/ /app/config/

# Copy from previous stage (multi-stage) COPY --from=builder /app/dist /usr/share/nginx/html

# ADD with auto-extraction ADD app.tar.gz /opt/app/ `

Working Directory & Environment (WORKDIR / ENV / ARG)

`dockerfile # Set working directory (use absolute paths) WORKDIR /app

# Set environment variables (available at runtime) ENV NODE_ENV=production \ PORT=3000

# Build arguments (overridable at build time) ARG APP_VERSION=1.0.0 ARG BUILD_DATE

# Pass build arg to env var ENV APP_VERSION=$APP_VERSION `

Expose Ports & Volumes (EXPOSE / VOLUME)

`dockerfile # Declare container listening ports EXPOSE 8080 EXPOSE 443/tcp 80/udp

# Declare mount points (anonymous volumes) VOLUME ["/data", "/var/log"] `

Startup Configuration (CMD / ENTRYPOINT)

`dockerfile # CMD as default command (overridable) CMD ["node", "server.js"] CMD npm start

# ENTRYPOINT as fixed entry point ENTRYPOINT ["docker-entrypoint.sh"] ENTRYPOINT ["nginx", "-g", "daemon off;"]

# Combined: ENTRYPOINT + CMD ENTRYPOINT ["python"] CMD ["app.py"]

# SHELL vs exec form # exec form (recommended): CMD ["executable", "param1"] # shell form: CMD command param1 `

User & Labels (USER / LABEL)

`dockerfile # Create user and switch (security best practice) RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser

# Set image metadata LABEL maintainer="team@example.com" LABEL version="1.0.0" LABEL description="Production web application" `

Health Check (HEALTHCHECK)

`dockerfile # Container health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1

# Disable inherited health check HEALTHCHECK NONE `

Triggers & Shell (ONBUILD / SHELL)

`dockerfile # Trigger: executes when this image is used as a base ONBUILD COPY . /app/src ONBUILD RUN make build

# Custom SHELL (Windows scenario) SHELL ["powershell", "-Command"] SHELL ["/bin/bash", "-c"] `

Multi-stage Builds

Separate build environment from runtime environment to significantly reduce image size.

`dockerfile # Stage 1: Build FROM golang:1.22 AS builder WORKDIR /build COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o app .

# Stage 2: Runtime FROM alpine:3.20 RUN apk add --no-cache ca-certificates tzdata COPY --from=builder /build/app /app EXPOSE 8080 USER 1001 ENTRYPOINT ["/app"]

# Named stages for conditional builds FROM node:20-alpine AS base WORKDIR /app COPY package*.json ./

FROM base AS dev RUN npm install --only=development COPY . .

FROM base AS prod RUN npm install --only=production COPY . . `

Best Practices

`dockerfile # 1. Use .dockerignore to exclude unnecessary files # .dockerignore contents: # node_modules # .git # *.md # Dockerfile # .env

# 2. Order instructions by change frequency (cache optimization) FROM node:20-alpine # Least changing: package files COPY package*.json ./ RUN npm install # Most changing: source code COPY . .

# 3. Use specific tags, not latest # Wrong: FROM node:latest # Right: FROM node:20.11-alpine3.19

# 4. Minimize image layers # Merge multiple RUN commands RUN apt-get update && \ apt-get install -y curl git && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*

# 5. Don't run as root RUN adduser -D myuser USER myuser

# 6. Use --no-install-recommends RUN apt-get install -y --no-install-recommends curl

# 7. Set timezone and locale ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime ``

Base Image(4)

CommandLevel
FROM node:20-alpine
Specify base image with a specific version tag
Basic
FROM golang:1.22 AS builder
Name a build stage for multi-stage builds
Intermediate
FROM python:3.12-slim
Use a slim base image to reduce size
Basic
FROM gcr.io/distroless/base-debian12
Use distroless image containing only runtime
Expert

Build(9)

CommandLevel
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
Merge RUN commands, clean apt cache after installing packages
Basic
RUN ["npm", "install"]
Use exec form to run commands (avoids shell escaping issues)
Intermediate
RUN --mount=type=cache,target=/root/.npm npm install
Use BuildKit cache mount to speed up builds
Expert
RUN <<EOF set -e\npip install --no-cache-dir -r requirements.txt\nEOF
Use HERE document format for multi-line RUN commands
Intermediate
WORKDIR /app
Set working directory (use absolute paths)
Basic
ENV NODE_ENV=production PORT=3000
Set environment variables (available at runtime)
Basic
ARG APP_VERSION=1.0.0
Declare build-arg, overridable with --build-arg at build time
Intermediate
LABEL version="1.0.0" description="Production web application"
Add metadata labels to the image
Basic
SHELL ["/bin/bash", "-c"]
Change the default shell
Expert

File Ops(4)

CommandLevel
COPY package.json package-lock.json ./
Copy specific files to leverage Docker layer caching
Basic
COPY src/ /app/src/
Copy an entire directory into the image
Basic
COPY --chown=node:node config/ /app/config/
Copy files and set ownership
Intermediate
ADD app.tar.gz /opt/app/
Add and auto-extract tar archive
Intermediate

Multi-stage(2)

CommandLevel
COPY --from=builder /app/dist /usr/share/nginx/html
Copy artifacts from a previous build stage
Intermediate
# 多阶段构建示例
Multi-stage build: separate build and runtime environments
Expert

Startup(6)

CommandLevel
EXPOSE 8080
Declare container listening port (documentation only)
Basic
VOLUME ["/data"]
Declare an anonymous mount volume
Intermediate
CMD ["node", "server.js"]
Provide default command (overridable by docker run)
Basic
ENTRYPOINT ["nginx", "-g", "daemon off;"]
Set container entry point (not overridable without --entrypoint)
Intermediate
ENTRYPOINT ["python"]\nCMD ["app.py"]
Combine ENTRYPOINT and CMD
Intermediate
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -f http://localhost:8080/health || exit 1
Set container health check
Expert

Best Practices(7)

CommandLevel
RUN addgroup -S appgroup && adduser -S appuser -G appgroup\nUSER appuser
Create non-root user and switch (security best practice)
Intermediate
ONBUILD COPY . /app/src
Set trigger instruction executed when this image is used as a base
Expert
# .dockerignore 文件内容
Use .dockerignore to exclude unnecessary files from build context
Basic
ENV TZ=Asia/Shanghai
Set container timezone
Basic
RUN apt-get install -y --no-install-recommends curl
Install system packages without recommended dependencies
Intermediate
# 按变更频率排序指令
Place less-frequently-changing instructions first to maximize cache usage
Intermediate
FROM node:20.11-alpine3.19
Use precise version tags instead of latest
Basic

FAQ

This cheatsheet is compiled from official tool documentation. Last updated: 2026-07-16.