Dockerfile Hardening & Multi-Stage Image Design¶
Core Security Requirements¶
- Multi-Stage Builds: Split build-time dependencies (compilers, SDKs, devDependencies) from the final runtime image.
- Never Run as Root: Declare an explicit non-root user (
USER nonroot:nonrootorUSER 10001:10001). - Use Minimal Base Images: Prefer
gcr.io/distroless/*,scratch, or Alpine over heavy Debian/Ubuntu base images. - Deterministic Pinning: Use explicit version tags and SHA-256 digest pinning for base images.
- No Embedded Secrets: Never copy
.env, credentials, private keys, or git tokens into the build layers.
Reference Multi-Stage Dockerfile (Node.js / TypeScript Example)¶
# ----------------------------------------------------
# Stage 1: Build & Compile
# ----------------------------------------------------
FROM node:22-alpine AS builder
WORKDIR /app
# Cache dependencies
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
# Copy source and build
COPY tsconfig.json ./
COPY src/ ./src/
RUN pnpm build && pnpm prune --prod
# ----------------------------------------------------
# Stage 2: Minimal Distroless Runtime
# ----------------------------------------------------
FROM gcr.io/distroless/nodejs22-debian12:nonroot
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./
ENV NODE_ENV=production
USER nonroot
EXPOSE 8080
ENTRYPOINT ["node", "dist/index.js"]
Image Vulnerability Audit Command¶
Before pushing any image to a container registry, scan for CVEs: