Lesson 15 of 30
A Dockerfile that builds successfully and a Dockerfile that's safe to run in production are two different bars. Hardening is the checklist that closes the gap.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
A Dockerfile never declares a `USER` instruction. What user does the container run as by default?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A default Dockerfile makes a few choices for you, silently, and every one of them is a small security tradeoff:
root unless you say otherwise. If an attacker finds a way to execute code inside your container (a dependency vulnerability, an injection bug — the application layer, not Docker itself, is usually the entry point), running as root means whatever that exploit can do is bounded only by the container's own isolation, not by any additional privilege restriction inside it..dockerignore, docker build . ships everything in the directory — including .git, node_modules, and any local .env file — to the Docker daemon, and a stray COPY . . can bake any of it into the image.FROM node:20-alpine
# Run as a dedicated non-root user, not root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["node", "dist/server.js"]
USER appuser on its own line, near the end, switches every subsequent instruction (and the running container itself) to that unprivileged user — COPY --chown ensures the files it needs are actually readable by that user, since files copied before the USER switch are owned by root by default.
.dockerignore is the build-context equivalent of .gitignore, and both need their own rules. A .gitignore entry doesn't apply to what docker build sends to the daemon — a .env file correctly excluded from git can still leak into a build context and get baked into an image if it isn't separately listed in .dockerignore.--no-install-recommends (Debian-based) or skipping unnecessary apk add packages reduces the set of CVEs that can ever apply to your image, independent of the base image choice itself.USER still needs to be set explicitly. Using a slim runtime stage doesn't automatically mean non-root — the final stage needs its own USER instruction just like a single-stage build would, since each stage's default user is root unless declared otherwise."Walk me through what you'd change about this Dockerfile before it goes to production" is a real, common exercise, and naming non-root USER, .dockerignore completeness, and the build-time-secret layer-history trap specifically — rather than a vague "make it more secure" — is what signals hands-on hardening experience rather than a general security-awareness answer.