Dockerfile Image build recipe

Version 1 · source

Start here

FROM <image>:<tag>
Pick a base image
WORKDIR /app
Set the working directory
COPY <src> <dest>
Add files from the context
RUN <command>
Run a command at build time
ENV <key>=<value>
Set an environment variable
EXPOSE <port>
Document a listening port
USER <user>
Drop root
CMD ["exe", "arg"]
Default start command

Concepts

  1. Base image FROM
  2. Layers one per step
  3. Final stage what ships
  4. Container running process

Base and stages

FROM <image>:<tag>
Start from a base image
Without a tag you get latest. Pin a tag or an @sha256 digest for repeatable builds.
FROM <image> AS <name>
Name a build stage
COPY --from=<stage> <src> <dest>
Copy files out of another stage
FROM scratch
Start from an empty image
FROM --platform=$BUILDPLATFORM <image>
Run the stage on the build host
FROM <image>@sha256:<digest>
Pin a base by digest
docker build --target <stage> .
Build only up to one stage
COPY --from=<image> <src> <dest>
Copy from an external image
ARG <name>=<default>
Before FROM: parametrize the base
A global ARG is only usable in FROM lines. Redeclare it without a value inside a stage to use it there.

Build steps

RUN <command>
Run a command in a new layer
RUN ["exe", "arg"]
Run without a shell (exec form)
COPY <src> <dest>
Copy files from the build context
COPY --chown=<user>:<group> <src> <dest>
Copy and set owner
COPY --chmod=<mode> <src> <dest>
Copy and set permissions
COPY --link <src> <dest>
Copy into an independent layer
COPY --exclude=<pattern> <src> <dest>
Skip matching files
COPY --parents <src>... <dest>
Keep the source directory structure
ADD <url> <dest>
Download a remote file
ADD <archive>.tar.gz <dest>
Copy and auto-extract a local tar
For plain files prefer COPY. ADD is for tar auto-extract and URLs.
ADD --checksum=sha256:<hash> <url> <dest>
Verify a download
ADD --keep-git-dir <git-url> <dest>
Clone a repo and keep .git
RUN --mount=type=cache,target=<dir> <command>
Reuse a package cache between builds
RUN --mount=type=secret,id=<id> <command>
Use a secret without baking it in
Read it from /run/secrets/<id>. Pass it with docker build --secret id=<id>,src=<file>.
RUN --mount=type=bind,source=<src>,target=<dir> <command>
Read context files without copying
RUN --mount=type=ssh <command>
Use the host SSH agent
RUN --mount=type=tmpfs,target=<dir> <command>
Scratch memory-backed directory
RUN --network=none <command>
Run with no network
RUN a && b && c
Chain steps in one layer

Configuration

ENV <key>=<value>
Set a variable for build and runtime
ARG <name>[=<default>]
Declare a build-time variable
Set with docker build --build-arg <name>=<value>. Not for secrets: values show up in image history.
WORKDIR <path>
Set the directory for later steps
Creates the directory if needed. Prefer it over RUN cd.
USER <user>[:<group>]
Run later steps as this user
LABEL <key>=<value>
Attach image metadata
LABEL org.opencontainers.image.source=<url>
Standard source label
SHELL ["/bin/bash", "-c"]
Change the shell for shell-form steps
RUN useradd -r -u 10001 <user>
Create an unprivileged user
ARG TARGETARCH
Target CPU arch (BuildKit)

Runtime

EXPOSE <port>[/tcp|/udp]
Document a port
This does not publish anything. Use -p or -P at docker run.
CMD ["exe", "arg"]
Default command (exec form)
ENTRYPOINT ["exe"]
Fixed executable for the container
CMD ["arg1", "arg2"]
Default arguments for ENTRYPOINT
CMD <command> <arg>
Default command via /bin/sh -c
Shell form runs under sh -c, so signals do not reach your process. Prefix with exec or use exec form.
VOLUME ["<path>"]
Declare a mount point
HEALTHCHECK CMD <command>
Probe container health
HEALTHCHECK --interval=<d> --timeout=<d> --retries=<n> CMD <command>
Tune the probe
Defaults: interval 30s, timeout 30s, retries 3. Exit 0 is healthy, 1 unhealthy.
HEALTHCHECK --start-period=<d> CMD <command>
Grace time for slow starts
HEALTHCHECK NONE
Disable an inherited check
STOPSIGNAL <signal>
Signal sent by docker stop
ONBUILD <instruction>
Run a step when used as a base
ENTRYPOINT and CMD
Dockerfile Runs
CMD ["a"] a
ENTRYPOINT ["e"] e
ENTRYPOINT ["e"] + CMD ["a"] e a
docker run <image> x replaces CMD
docker run --entrypoint <p> <image> replaces ENTRYPOINT

Syntax and context

# syntax=docker/dockerfile:1
Parser directive: current stable frontend
Must be the very first line of the file.
# escape=`
Use backtick as escape char (Windows)
# check=error=true
Fail the build on lint warnings
RUN <<EOF
Multi-line script with a heredoc
COPY <<EOF <dest>
Write a file from a heredoc
<command> \
Continue a line
# comment
Comment line
docker build -f <file> <context>
Use another Dockerfile
docker build -t <name> .
Build with a tag
node_modules
In .dockerignore: skip a path
**/*.log
In .dockerignore: match at any depth
!<pattern>
In .dockerignore: re-include a match
The last matching line wins, so put exceptions after the pattern they carve out.
<name>.Dockerfile.dockerignore
Ignore file for one Dockerfile
Sits beside the Dockerfile and takes precedence over the root .dockerignore.

Cache and size

Recipes

Cache-friendly Node image
FROM node:22-slimWORKDIR /appCOPY package*.json ./RUN --mount=type=cache,target=/root/.npm npm ciCOPY . .CMD ["node", "server.js"]
Multi-stage Go binary
FROM golang:1.23 AS buildWORKDIR /srcCOPY . .RUN CGO_ENABLED=0 go build -o /out/app .FROM gcr.io/distroless/staticCOPY --from=build /out/app /appUSER 65532ENTRYPOINT ["/app"]
Entrypoint script that forwards signals
COPY entrypoint.sh /entrypoint.shENTRYPOINT ["/entrypoint.sh"]CMD ["serve"]# last line of entrypoint.sh:# exec "$@"
Heredoc script with a build secret
RUN --mount=type=secret,id=token <<EOF  set -e  curl -fsS -H "Authorization: Bearer $(cat /run/secrets/token)" <url> -o /tmp/pkgEOF