Always use multi-stage Docker builds. Build stage installs dependencies
and compiles. Final stage copies only the built artifact into a minimal
base image (alpine, distroless, or scratch). Never install build tools
in the runtime stage.
Always use multi-stage Docker builds. Build stage installs dependencies
and compiles. Final stage copies only the built artifact into a minimal
base image (alpine, distroless, or scratch). Never install build tools
in the runtime stage.
Always use multi-stage Docker builds. Build stage installs dependencies
and compiles. Final stage copies only the built artifact into a minimal
base image (alpine, distroless, or scratch). Never install build tools
in the runtime stage.
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY . .
RUN npm run build FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY . .
RUN npm run build FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY . .
RUN npm run build FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]
Never run containers as root. Create a non-root user in the Dockerfile
or use the built-in non-root user for the base image (e.g., node user
for node images). Set USER before CMD. Set file ownership explicitly.
Never run containers as root. Create a non-root user in the Dockerfile
or use the built-in non-root user for the base image (e.g., node user
for node images). Set USER before CMD. Set file ownership explicitly.
Never run containers as root. Create a non-root user in the Dockerfile
or use the built-in non-root user for the base image (e.g., node user
for node images). Set USER before CMD. Set file ownership explicitly.
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
FROM python:3.12-slim
RUN groupadd --gid 1001 appgroup && \ useradd --uid 1001 --gid appgroup --shell /bin/false appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN pip install --no-cache-dir -r requirements.txt
USER appuser
CMD ["python", "main.py"]
FROM python:3.12-slim
RUN groupadd --gid 1001 appgroup && \ useradd --uid 1001 --gid appgroup --shell /bin/false appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN pip install --no-cache-dir -r requirements.txt
USER appuser
CMD ["python", "main.py"]
FROM python:3.12-slim
RUN groupadd --gid 1001 appgroup && \ useradd --uid 1001 --gid appgroup --shell /bin/false appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN pip install --no-cache-dir -r requirements.txt
USER appuser
CMD ["python", "main.py"]
Always pin base image tags to a specific version including the variant
(e.g., node:20.11-alpine, python:3.12.2-slim). Never use :latest.
For maximum reproducibility, pin to the SHA256 digest in production.
Always pin base image tags to a specific version including the variant
(e.g., node:20.11-alpine, python:3.12.2-slim). Never use :latest.
For maximum reproducibility, pin to the SHA256 digest in production.
Always pin base image tags to a specific version including the variant
(e.g., node:20.11-alpine, python:3.12.2-slim). Never use :latest.
For maximum reproducibility, pin to the SHA256 digest in production.
FROM node:latest
FROM python:3
FROM ubuntu
FROM node:latest
FROM python:3
FROM ubuntu
FROM node:latest
FROM python:3
FROM ubuntu
FROM node:20.11-alpine3.19
FROM python:3.12.2-slim-bookworm
FROM ubuntu:24.04
FROM node:20.11-alpine3.19
FROM python:3.12.2-slim-bookworm
FROM ubuntu:24.04
FROM node:20.11-alpine3.19
FROM python:3.12.2-slim-bookworm
FROM ubuntu:24.04
Copy dependency manifests (package.json, requirements.txt, go.mod) first
and install dependencies in a separate layer. Copy source code after.
Use .dockerignore to exclude node_modules, .git, and build artifacts.
Combine related RUN commands with && to reduce layers.
Copy dependency manifests (package.json, requirements.txt, go.mod) first
and install dependencies in a separate layer. Copy source code after.
Use .dockerignore to exclude node_modules, .git, and build artifacts.
Combine related RUN commands with && to reduce layers.
Copy dependency manifests (package.json, requirements.txt, go.mod) first
and install dependencies in a separate layer. Copy source code after.
Use .dockerignore to exclude node_modules, .git, and build artifacts.
Combine related RUN commands with && to reduce layers.
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]
Never put secrets in ENV, ARG, or COPY directives in Dockerfiles.
Use runtime environment variables, Docker secrets, or mounted config files.
Use --mount=type=secret for build-time secrets that must not persist in layers.
Never hardcode credentials, tokens, or connection strings.
Never put secrets in ENV, ARG, or COPY directives in Dockerfiles.
Use runtime environment variables, Docker secrets, or mounted config files.
Use --mount=type=secret for build-time secrets that must not persist in layers.
Never hardcode credentials, tokens, or connection strings.
Never put secrets in ENV, ARG, or COPY directives in Dockerfiles.
Use runtime environment variables, Docker secrets, or mounted config files.
Use --mount=type=secret for build-time secrets that must not persist in layers.
Never hardcode credentials, tokens, or connection strings.
FROM node:20-alpine
ENV DATABASE_URL=postgresql://admin:s3cret@db:5432/prod
ENV STRIPE_KEY=sk_live_abc123
COPY . .
RUN npm install
CMD ["node", "index.js"]
FROM node:20-alpine
ENV DATABASE_URL=postgresql://admin:s3cret@db:5432/prod
ENV STRIPE_KEY=sk_live_abc123
COPY . .
RUN npm install
CMD ["node", "index.js"]
FROM node:20-alpine
ENV DATABASE_URL=postgresql://admin:s3cret@db:5432/prod
ENV STRIPE_KEY=sk_live_abc123
COPY . .
RUN npm install
CMD ["node", "index.js"]
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npmrc,target=/app/.npmrc npm ci
COPY . .
RUN npm run build FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
# Secrets passed at runtime: docker run -e DATABASE_URL=... -e STRIPE_KEY=...
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npmrc,target=/app/.npmrc npm ci
COPY . .
RUN npm run build FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
# Secrets passed at runtime: docker run -e DATABASE_URL=... -e STRIPE_KEY=...
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npmrc,target=/app/.npmrc npm ci
COPY . .
RUN npm run build FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
# Secrets passed at runtime: docker run -e DATABASE_URL=... -e STRIPE_KEY=...
Every production Dockerfile must include a HEALTHCHECK instruction.
Use curl, wget, or a custom binary to check the app's health endpoint.
Set appropriate interval, timeout, and retries. Install only the minimal
tool needed for the check (wget is smaller than curl on Alpine).
Every production Dockerfile must include a HEALTHCHECK instruction.
Use curl, wget, or a custom binary to check the app's health endpoint.
Set appropriate interval, timeout, and retries. Install only the minimal
tool needed for the check (wget is smaller than curl on Alpine).
Every production Dockerfile must include a HEALTHCHECK instruction.
Use curl, wget, or a custom binary to check the app's health endpoint.
Set appropriate interval, timeout, and retries. Install only the minimal
tool needed for the check (wget is smaller than curl on Alpine).
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci
EXPOSE 3000
CMD ["node", "index.js"]
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci
EXPOSE 3000
CMD ["node", "index.js"]
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci
EXPOSE 3000
CMD ["node", "index.js"]
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY . .
EXPOSE 3000
USER node
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "index.js"]
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY . .
EXPOSE 3000
USER node
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "index.js"]
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY . .
EXPOSE 3000
USER node
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "index.js"]