Start with a minimal, specific base image
FROM node:18-alpine
NOTE: Always use specific version tags rather than latest to ensure reproducible builds.
Place instructions that change least at the top
FROM node:18-alpine # Tools that rarely change RUN apk add --no-cache python3 make g++ # Dependencies that change occasionally COPY package*.json ./ RUN npm ci # Application code that changes frequently COPY . .
NOTE: The Docker build cache invalidates all subsequent layers when a layer changes.
Use && to chain commands and reduce layers.
# Bad practice (creates 3 layers) RUN apt-get update RUN apt-get install -y curl RUN rm -rf /var/lib/apt/lists/* # Good practice (creates 1 layer) RUN apt-get update && \ apt-get install -y curl && \ rm -rf /var/lib/apt/lists/*
NOTE: Each RUN instruction creates a new layer.
</file>
Exclude unnecessary files from the build context.
cat .dockerignore
node_modules
npm-debug.log
Dockerfile
.git
.gitignore
README.md
NOTE: A .dockerignore file prevents the specified files from being sent to the Docker daemon during build.
Separate build and runtime environments.
FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:18-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY package*.json ./ CMD ["npm", "start"]
NOTE: Multi-stage builds lets you use one image for building (with all build tools) and another for running your application.
In the example above:
Multi-stage builds are particularly valuable for compiled languages like Go, Rust, or Java, where the final binary can be copied to a minimal image.
FROM golang:1.20 AS builder WORKDIR /app COPY go.* ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server FROM alpine:3.18 RUN apk --no-cache add ca-certificates COPY --from=builder /app/server /usr/local/bin/ CMD ["server"]
Avoid running containers as root.
RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser
NOTE: Running containers as root is a security risk.
Understand their differences.
<file bash> # For applications ENTRYPOINT [“node”, “app.js”] CMD [“–production”]
# For utilities ENTRYPOINT [“aws”] CMD [“–help”] </file?
NOTE: ENTRYPOINT defines the executable that runs when the container starts, while CMD provides default arguments to that executable.
Understand build failures
docker build -t myapp .
NOTE: Common build errors include:
When working with large applications, consider these additional optimizations.
These techniques can reduce build times by up to 80% for complex applications.