Dockerfile
A Dockerfile is the recipe for an image. Each instruction (FROM, COPY, RUN, CMD) becomes a layer; layers cache; cache-friendly order is the difference between 3-second and 3-minute rebuilds.
Multi-stage Dockerfile for a Node app
EXAMPLE
# syntax=docker/dockerfile:1.7
# ===== Stage 1: deps — cache npm install =====
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=dev
# ===== Stage 2: build — compile/transpile =====
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
# ===== Stage 3: runtime — small, no toolchain =====
FROM node:20-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
# Drop privileges — never run as root
RUN addgroup -S app && adduser -S app -G app
USER app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./package.json
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -q -O- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
# ----- Key principles encoded above -----
# 1) FROM (stage): use a slim/alpine base; pin a digest in prod
# 2) WORKDIR: creates and switches; no chained mkdir + cd
# 3) COPY order: package.json BEFORE source — keeps npm install cached
# 4) RUN cache mounts: persistent npm/apt cache across builds (BuildKit)
# 5) Multi-stage: final image carries no dev deps or build tools
# 6) Non-root USER: reduces blast radius on RCE
# 7) HEALTHCHECK: container-aware; orchestrators read it
# 8) CMD vs ENTRYPOINT:
# ENTRYPOINT ["node"] CMD ["server.js"] → docker run img some-other.js works
# CMD ["node","server.js"] → typical app
# ----- .dockerignore — fast, small builds -----
# node_modules
# .git
# .env
# *.log
# coverage/
# dist/
# ----- Build + run -----
# docker build -t myapp:1.0 .
# docker run --rm -p 3000:3000 myapp:1.0
# docker image ls myapp
# ----- Things to avoid -----
# • RUN apt-get install + leave the apt cache
# • COPY . . at the top — invalidates cache on every source change
# • Single-stage with build tools shipped — fat, slow, more CVEs
# • Running as root in the final stage
Why it matters
Order COPY/RUN steps from least-changing to most-changing — the build cache rewards stable layers. package.json changes weekly; src/ changes hourly. Put the weekly thing first.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . EXPOSE 3000 CMD ["node", "src/index.js"]Try it Yourself »
Exercise
Dockerfile instruction that sets the start command.
["node", "src/index.js"]
Three letters; uppercase.
Discussion
Loading…