Dockerfile
A Dockerfile is the text-based build specification for a container image. It declares the base image, file copies, dependency installation, build steps, and startup command in order. `docker build` reads that file and turns each step into image layers.
βΆArchitecture Diagram
π ProcessDashed line animations indicate the flow direction of data or requests
To deploy an application correctly, the runtime, libraries, config files, and environment variables all need to line up. Manual server setup drifts because people follow different orders, forget steps, and leave little trace of what the final machine state really was. The larger the fleet, the less sustainable that approach becomes.
Before Dockerfile, teams often used shell scripts or configuration management tools to automate parts of server setup. Those helped, but they still depended on the state of the machine they were running on. Dockerfile changed the model by always starting from a known base image and layering explicit steps on top, which made immutable build outputs much easier to reproduce.
Each Dockerfile instruction maps to one image layer. `FROM` chooses the base image, `COPY` adds files, and `RUN` executes install or build commands that change the filesystem. The layer model matters because unchanged steps can be reused from cache. That is why dependency installation usually appears before copying the full source tree. `CMD` and `ENTRYPOINT`, by contrast, describe what should run when the container starts rather than what happens during build.
Basic structure
# 1. Base image
FROM node:20-alpine
# 2. Working directory
WORKDIR /app
# 3. Copy dependency files first (for cache reuse)
COPY package.json package-lock.json ./
RUN npm ci
# 4. Copy source code
COPY . .
# 5. Build
RUN npm run build
# 6. Expose port and define startup command
EXPOSE 3000
CMD ["node", "dist/server.js"]Copying dependency files before the full source tree is the critical optimization. If only source code changes, the dependency install layer can still be reused from cache.
CMD vs ENTRYPOINT
# CMD β default command that docker run can override
CMD ["npm", "start"]
# ENTRYPOINT β fixed entry command
ENTRYPOINT ["node", "server.js"]
# Combined β ENTRYPOINT stays fixed, CMD becomes default args
ENTRYPOINT ["node"]
CMD ["server.js"]CMD provides the default runtime command, while ENTRYPOINT defines the fixed executable. Combining them is a common way to separate command and default arguments.
Dockerfile and Docker Compose both describe container-related behavior as code, but they work at different scopes. Dockerfile defines how to build one image. Compose defines how multiple images or build results should run together. Dockerfile can also resemble shell scripting, but the crucial difference is that it starts from a fixed base image rather than mutating an unknown server state.
A Dockerfile is the handoff point that carries one build rule from local development into CI and production. When teams build from the same Dockerfile everywhere, the files, dependencies, and startup command stop living in docs and become fixed inside the artifact itself. The practical decisions are which steps change often and which tools should be absent from the final image. That is why a Dockerfile is less like a syntax exercise and more like a design document for build reproducibility and deployment boundaries.