THN Interview Prep

Docker images & containers for Node.js

Core details

Image = immutable filesystem + metadata; container = running instance from image + writable layer + cgroups limits.

Docker image and container lifecycle diagram showing multi-stage build layers, the final immutable image, runtime writable layer, cgroup limits, stdout logging, SIGTERM drain behavior, health checks, and production hardening controls.

Problem this solves: package the Node runtime, app code, and OS dependencies into a repeatable artifact so production does not depend on a hand-built server.

Practices for Node services

PracticeWhyInterview follow-up
Multi-stage buildtiny final image; dev deps don’t shipWhat changes invalidate cache layers?
NODE_ENV=productionleaner installs, behavior flagsHow do you prevent dev-only packages from shipping?
Non-root USERblast radius on escapeWhat filesystem paths still need write access?
dumb-init / initPID 1 reaps zombies; signals reach NodeWhat happens on SIGTERM?
Read-only root where possiblereduce tamper surfaceWhere do temp files/logs go?
.dockerignorefaster builds; no node_modules copy from hostWhat secrets might accidentally enter build context?

Signals: container stop sends SIGTERM—shutdown hooks close server gracefully before SIGKILL.

Healthcheck: HTTP /health or TCP; distinct liveness vs readiness (Kubernetes split).

Understanding

Layer caching: order Dockerfile so dependency layers change less often than app code. Pin base images with digest for reproducibility.

The runtime contract matters as much as the image. A production Node container should:

  1. Start from config passed at runtime, not from baked-in environment secrets.
  2. Log to stdout/stderr so the platform collector owns persistence.
  3. Stop accepting new traffic before closing in-flight requests.
  4. Exit non-zero on unrecoverable startup failures.
  5. Expose a readiness endpoint that checks only dependencies required to serve this route class.

Practical Dockerfile shape

FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM deps AS build
COPY . .
RUN npm run build

FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/package*.json ./
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public
RUN npm ci --omit=dev
USER node
CMD ["node", "server.js"]

This is illustrative, not a universal Dockerfile. The important interview point is the separation of dependencies, build, and runtime, plus a small final image with no development toolchain.

Senior understanding

PitfallStory
Fat imagesslow pull → cold start on scale-out
Logging to file in containeruse stdout for collector
Secrets in imageuse runtime injection only
App is PID 1 without signal handlingdeploys hang until SIGKILL; requests get cut mid-flight
Health check calls every dependencydependency blip restarts healthy pods and amplifies outage

Interview drill

Question: "A Node service in containers takes too long to roll out and sometimes drops requests during deploy. What do you inspect?"

Model answer structure:

  1. Image size and layer cache: base image, dependency layer order, dev dependencies, build context.
  2. Startup path: migrations at startup, connection pool warmup, config fetch, readiness delay.
  3. Stop path: SIGTERM handling, server close, keep-alive timeout, platform termination grace period.
  4. Health semantics: readiness removes from traffic; liveness only restarts truly stuck processes.
  5. Telemetry: deploy markers, 5xx during rollout, pod termination reasons, cold-start duration.

Follow-ups to expect:

  • "Should readiness check the database?"
  • "How do you rotate secrets without rebuilding the image?"
  • "What changes if this runs on Lambda container images?"

See also

Mark this page when you finish learning it.

Spotted something unclear or wrong on this page?

On this page