iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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"]

Test yourself

Q1. Dockerfile keyword to set the start command is…
Q2. For build-time-only commands use…
Q3. WORKDIR sets…

Discussion

Loading…