Dockerfile: Building a container image
In short: A Dockerfile is a text file with steps from which Docker builds an image, the template for containers.
In more detail: An image contains program, runtime and dependencies; containers are started from it. The Dockerfile describes how the image is created, command by command. Each command creates a layer that is cached, so that a rebuild only recreates changed layers.
In Depth
Example for a Node.js app
FROM node:22-alpine # base image
WORKDIR /app # working directory
COPY package*.json ./ # first only package lists
RUN npm ci --omit=dev # install dependencies
COPY . . # then copy the rest
EXPOSE 3000 # note about the port
USER node # do not run as root
CMD ["node", "server.js"] # start commandBuilding and starting:
docker build -t my-app:1.0 .
docker run -p 3000:3000 my-app:1.0
Important instructions
FROM: base image (as small as possible and with a fixed version).RUN: execute a command at build time.COPY/ADD: copy files in (preferCOPY).ENV,ARG: variables.CMD,ENTRYPOINT: command at start of the container.HEALTHCHECK: checks whether the app is running.
Good practice
- Use the order: rarely changed things (dependencies) first, source code last so the cache works.
.dockerignorelike.gitignore: do not putnode_modules,.git, secrets into the image.- Multi-stage builds: build in one stage, copy only the result into a slim final image.
- Do not run as root, keep the base image up to date and check for vulnerabilities (
docker scout, Trivy). - No passwords in the Dockerfile: they stay visible in the layers.
Multi-stage example
FROM golang:1.23 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app
FROM gcr.io/distroless/static
COPY --from=build /app /app
CMD ["/app"]The final image contains only the finished program, not the compiler, and is therefore small and less exposed to attack.
See also: Docker, Container security, Podman and Docker, Environment variables