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.

Last updated on

Spotted something unclear or wrong on this page?

On this page