Dockerfile Basics
This page covers the core Dockerfile instructions you use in almost every build. Each example is small and self-contained so you can copy, run, and adapt it.
Search across all documentation pages
This page covers the core Dockerfile instructions you use in almost every build. Each example is small and self-contained so you can copy, run, and adapt it.
docker compose if you build via Compose.docker version
# confirm Engine 29.x; BuildKit is on by defaultFROM sets the parent image every later instruction builds on.
FROM node:22-slimARG).latest so builds are predictable.node:22-slim@sha256:...) for reproducible builds.WORKDIR sets the directory for later RUN, CMD, COPY, and ENTRYPOINT.
WORKDIR /appRUN cd, which does not persist across instructions.COPY moves files from your context into the image.
COPY package.json package-lock.json ./COPY over ADD unless you need ADD's extra behavior.RUN runs a command and commits the result as a new layer.
RUN npm ci --omit=devRUN creates one layer, so chain related commands with &&.RUN so junk never enters a layer.RUN ["executable", "arg"]) skips the shell.ENV sets variables baked into the image and visible at runtime.
ENV NODE_ENV=productionENV invalidates the cache for later steps.EXPOSE records which port the container process listens on.
EXPOSE 3000-p or in Kubernetes.CMD sets the default process the container runs.
CMD ["node", "server.js"]CMD node server.js) wraps the process in /bin/sh -c.CMD in a file takes effect.docker run myapp arg overrides CMD entirely.ENTRYPOINT fixes the executable; CMD supplies overridable default arguments.
ENTRYPOINT ["python", "-m", "myservice"]
CMD ["--port=8080"]ENTRYPOINT, replacing CMD.docker run img --port=9090 runs python -m myservice --port=9090.USER switches the identity that later instructions and the container run as.
RUN adduser --system --uid 10001 appuser
USER 10001
CMD ["node", "server.js"]USER after installing packages, which usually needs root.runAsNonRoot can verify it.ARG declares a build-time variable; a builder stage keeps tools out of the final image.
ARG GO_VERSION=1.23
FROM golang:${GO_VERSION} AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/server
FROM gcr.io/distroless/static:nonroot
COPY --from=build /app /app
USER nonroot
ENTRYPOINT ["/app"]ARG values exist only during build, unlike ENV.docker build --build-arg GO_VERSION=1.24 ..COPY --from=build pulls only the compiled binary forward.Chain related commands in one RUN and clean caches in the same layer so nothing extra ships.
FROM debian:12-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl \
&& rm -rf /var/lib/apt/lists/*RUN is one layer, so chaining avoids a separate layer per command.RUN keeps that junk out of the layer.--no-install-recommends skips optional packages you rarely need.RUN would not shrink the earlier layer.HEALTHCHECK tells Docker how to test whether the process is actually serving.
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -fsS http://localhost:3000/healthz || exit 1healthy or unhealthy from the exit code.HEALTHCHECK and uses its own liveness and readiness probes instead.LABEL records key-value metadata on the image, such as source and version.
LABEL org.opencontainers.image.source="https://github.com/acme/api" \
org.opencontainers.image.version="1.0"org.opencontainers.image.* keys so registries and tools recognize them.docker inspect.Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026