Writing a Dockerfile for a web app
core Dockerfile instructions and layering.
FROM a base, set WORKDIR, install dependencies before app code for cache reuse, EXPOSE the port, CMD the start command.
copying everything before installing deps, or root.
What's really being asked
This checks practical Docker literacy: which instructions matter, what they do, and why instruction order affects build speed and image quality.
The full answer
Start with FROM to pick a base image, preferring a slim, version-pinned image like python:3.12-slim rather than latest for reproducibility and a smaller surface. Use WORKDIR to set the working directory so later paths are clean. Then exploit layer caching: COPY only the dependency manifest, such as requirements.txt or package.json, and RUN the install before copying the rest of the code. Because Docker caches layers and invalidates from the first change, dependencies are reinstalled only when the manifest changes, not on every code edit, which speeds up builds dramatically. After that, COPY the application source. Use EXPOSE to document the port the app listens on, and finish with CMD or ENTRYPOINT to specify the process that runs when the container starts, for example gunicorn. Mention a .dockerignore to keep junk and secrets out of the build context, and creating and switching to a non-root USER for security.
The mistakes people make
Copying the whole project before installing dependencies, so any code change busts the dependency cache and every build reinstalls everything. Using FROM image:latest, making builds non-reproducible. Running as root. Bloating the image by starting from a full OS base or leaving build tools in the final image.
What usually comes next
What is the difference between CMD and ENTRYPOINT? How does layer caching decide what to rebuild? Why a slim or distroless base? How would a multi-stage build help? What goes in .dockerignore?
A concrete example
A Flask app Dockerfile: FROM python:3.12-slim, WORKDIR /app, COPY requirements.txt ., RUN pip install --no-cache-dir -r requirements.txt, COPY . ., EXPOSE 8000, then CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]. Editing app.py rebuilds only the final COPY and CMD layers because the cached dependency layer is untouched, so iteration is fast.
Interview question
Why copy the dependency manifest and install dependencies before copying the rest of the application code in a Dockerfile?
- a.It encrypts the dependencies so they cannot be extracted from the image
- b.It is required syntax; Docker rejects a COPY of source before a dependency install
- c.It makes the final image larger but more secure
- d.It lets the dependency install layer stay cached when only application code changes, speeding rebuildsCorrect
Why? this is the answer
Docker invalidates the cache from the first changed layer, so installing deps before copying code keeps that expensive layer cached across code-only edits. It is an ordering optimization, not a syntax rule.
Just read this? Test yourself on what you have been reading.
Read the original → docs.docker.com
- #docker
- #dockerfile
- #containers
- #layer-caching
- #deployment
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on docker — each one lists the topics its interview covers.
See open roles