Dockerfile Instructions Cheat Sheet & Build Examples
An overview of the Dockerfile format and instructions, plus image-build examples and operations.
Copyright notice: This is an original article by xwi88, licensed under CC BY-NC 4.0. Commercial use is prohibited; please cite the source when reposting. Follow at https://github.com/xwi88
Dockerfile overview
A Dockerfile is a text file used to build an image, made up of the instructions (instruction) and notes needed to build it. Each instruction builds one layer.
By default docker build . looks for a file named Dockerfile in the current directory; you can also pass a path and filename: docker build -f xxx/path/DockerfileName .
.dockerignore
.dockerignore file in the build context to exclude files and directories. If your project has many large files that don’t need to be in the image, excluding them really helps build speed and image size.Basic structure
Lines starting with
#are comments and may appear anywhere; avoid extra spaces before#.
- Base image info
- Maintainer info
- Image-operation instructions
- Container-startup instruction
All instructions
FROM
FROM [--platform=<platform>] <image> [AS <name>]FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
The FROM instruction initializes a new build stage and sets the base image for subsequent instructions. Therefore a valid Dockerfile must start with a FROM instruction.
ARGis the only instruction that may appear beforeFROM.FROMmay appear multiple times in a singleDockerfileto create multiple images or to use one build stage as a dependency of another. Just record the last image ID committed before each newFROM. EachFROMclears any state created by previous instructions.- Adding
AS <name>toFROMnames the new build stage. You can reference it in laterFROMandCOPY --from=<name>instructions. taganddigestare optional. If omitted, the builder defaults to thelatesttag. If the builder can’t find the tag, it errors.--platform=<platform>specifies which platform’s imageFROMrefers to, e.g.linux/amd64,linux/arm64,windows/amd64. It defaults to the target platform of the build request. You can use global build args in this flag, e.g. automatic platform args in the global scope, letting you force the build stage’s native platform(--platform=$BUILDPLATFORM)and cross-compile to the target within it.
ARG & FROM interaction
Variables declared by
ARGmay appear before the firstFROM.
| |
ARG scope
| |
RUN
RUN <command>(shell form; default on linux:/bin/sh -c, on Windows:cmd /S /C)RUN ["executable", "param1", "param2"](exec form)
RUN runs any command on top of the current image in a new layer and commits the result. The committed image is used for the next step in the Dockerfile.
The exec form avoids shell-string munging and lets you run commands in a base image that doesn’t contain a shell.
- Use the
SHELLcommand to change the default shell - Use a
\backslash to span a singleRUNacross lines for long commands
Replacing the default shell
| |
The
execform is parsed as a JSON array, which means that you must use double-quotes (") around words not single-quotes (').
| |
RUN cache
- The
RUNcache is not invalidated automatically on the next build - A command like
RUN apt-get dist-upgrade -yhas its cache reused on the next build --no-cacheinvalidates theRUNcache:docker build --no-cacheADDandCOPYcan invalidate theRUNcache
CMD
CMD ["executable","param1","param2"](exec form, this is the preferred form)CMD ["param1","param2"](as default parameters to ENTRYPOINT)CMD command param1 param2(shell form)
Notes
- A
Dockerfilemay have only oneCMD; if there are several, only the last takes effect. CMD’s main purpose is to provide defaults for an executing container. The defaults may include an executable, or omit it — in which case you must also specifyENTRYPOINT.- If you use
CMDto provide default args toENTRYPOINT, both should use the JSON-array form. - If you want the container to run the same executable every time, combine
ENTRYPOINTandCMD - If the user passes arguments to
docker run, they override theCMDdefaults - Don’t confuse
RUNwithCMD.RUNactually runs a command and commits the result;CMDdoes nothing at build time but specifies the intended command for the image.
LABEL
LABEL <key>=<value> <key>=<value> <key>=<value> ...
| |
View image labels:
docker image inspect --format='' <image_id | image_name>
EXPOSE
EXPOSE <port> [<port>/<protocol>...]
EXPOSE tells Docker the container listens on the specified network ports at runtime. You can specify TCP or UDP; if no protocol is given, TCP is the default.
EXPOSE does not actually publish the port. It’s documentation between the person building the image and the person running the container about which ports to publish. To actually publish at runtime, use -p on docker run to publish and map one or more ports, or -P to publish all exposed ports to high ports.
By default EXPOSE assumes TCP. You can also specify UDP:
| |
expose for run
PPublish all exposed ports to the host interfacesp=[]Publish a container’s port or a range of ports to the hostdocker run -p 80:80/tcp -p 80:80/udp ...docker run -p 80-81:80-81/tcp
ENV
ENV <key>=<value> ...
ENV MY_NAME="John Doe"ENV MY_DOG=Rex\ The\ DogENV MY_CAT=fluffy- multiple
<key>=<value> ... ENV key valueavoid — may be removed later; error-prone
| |
- Takes effect during the build stage; can be overridden
- The value is in the environment of all subsequent instructions in the build stage and can be substituted inline. Substitution uses the same value for each variable across the whole instruction.
- Values set by
ENVare persisted into the output image.- Viewable via
docker inspect - Overridable at run time:
docker run --env <key>=<value>
- Viewable via
- Side effect: env vars you set may affect later build behavior — use carefully.
- Use
RUN <key>=<value> [<command>]to scope a value to a single instruction - Use
ARGfor build-time values that are not persisted into the image
- Use
| |
ADD
ADD [--chown=<user>:<group>] <src>... <dest>ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]required when paths contain spaces
Notes
--chownapplies only toLinuxcontainers- needs
/etc/passwd//etc/groupto resolveuser,group
- needs
- Copies from sources that may be
files,directories, or remoteURLs- Copies from outside into the image being built
srcsupports multiple sources into one targetsrcmay contain wildcards (regex matching)
destmay be absolute or relative toWORKDIR
ADD hom* /mydir/ADD hom?.txt /mydir/ADD test.txt relativeDir/ADD test.txt /absoluteDir/ADD arr[[]0].txt /mydir/arr[0].txtfollowing theGolang rules
Without --chown, UID=GID=0 by default; UID/GID may be username, groupname, or a UID/GID combo
| |
- If you use
username/groupnameand no matchinguser/groupexists, the instruction fails - Numeric
IDsare not checked and don’t depend on the container’s rootfs contents docker build - < somefilehas no build context;ADDonly supports URLs in this mode- If the
URLfile requires auth, useRUN wget,RUN curlor another in-container tool —ADDdoes not support authentication <src>paths must be inside the build context;docker buildsends the context path to the docker daemon- When
srcis a URL, a trailing slash ondest(dir/) vs no slash (filename) behaves differentlytrailing slash, e.g.dir/creates the file inside directorydir/no trailing slash, e.g.filenamecreates a file namedfilename
- A recognized
local tar archive(identity,gzip,bzip2orxz) is extracted as a directory - If
destdoes not end with a slash, it’s treated as a regular file andsrc’s contents are written into it - If
destdoesn’t exist, it’s created along with any missing directories in its path
COPY
COPY [--chown=<user>:<group>] <src>... <dest>COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]required when paths contain spaces
Notes
--chownapplies only toLinuxcontainers- needs
/etc/passwd//etc/groupto resolveuser,group
- needs
- Copies from sources that may be
filesordirectories- Copies from outside into the image being built
srcsupports multiple sources into one targetsrcmay contain wildcards (regex matching)- Unlike
ADD, URLs are not supported
destmay be absolute or relative toWORKDIR
ADD hom* /mydir/ADD hom?.txt /mydir/ADD test.txt relativeDir/ADD test.txt /absoluteDir/ADD arr[[]0].txt /mydir/arr[0].txtfollowing theGolang rules
Without --chown, UID=GID=0 by default; UID/GID may be username, groupname, or a UID/GID combo
| |
- If you use
username/groupnameand no matchinguser/groupexists, the instruction fails - Numeric
IDsare not checked and don’t depend on the container’s rootfs contents - Without a build context,
COPYis unavailable - Supports
COPY --from=<name>namecomes fromFROM .. AS <name>- If no build stage named
nameis found, an image of the same name is tried instead
<src>paths must be inside the build context;docker buildsends the context path to the docker daemon- If
destdoes not end with a slash, it’s treated as a regular file andsrc’s contents are written into it - If
destdoesn’t exist, it’s created along with any missing directories in its path
ENTRYPOINT
ENTRYPOINT ["executable", "param1", "param2"]exec formrecommendedENTRYPOINT command param1 param2shell formcannot accept args passed via CLI,CMD, orrun
Notes
ENTRYPOINTlets you configure the container as an executable- Only the last
ENTRYPOINTtakes effect docker run <image>args are appended to the exec-formENTRYPOINTand overrideCMDargs, so you can pass args toENTRYPOINTfrom the CLI- Override with
docker run --entrypoint - The exec form does not invoke a shell; if you need one, specify
sh -cinENTRYPOINT, e.g.ENTRYPOINT [ "sh", "-c", "echo $HOME" ] - The shell form runs as a subcommand of
/bin/sh -cand doesn’t pass signals. The executable won’t bePID 1and won’t receiveUnixsignals, so it won’t receiveSIGTERMfromdocker stop <container>
exec form ENTRYPOINT example
| |
Build the
topimage:docker build -t top .Start the container:
docker run -it --rm --name test top -H
| |
To examine the result further, you can use docker exec
docker exec -it test ps aux
| |
And you can gracefully request top to shut down using docker stop test
/usr/bin/time docker stop test
| |
shell form ENTRYPOINT example
shell form with exec
| |
docker run -it --rm --name test top
| |
docker exec -it test ps aux
| |
/usr/bin/time docker stop test
| |
shell form (no exec)
| |
docker run -it --rm --name test top param_outer
| |
docker stop test doesn’t exit immediately — it exits after the timeout; verify with
docker exec -it test ps aux
| |
/usr/bin/time docker stop test
| |
CMD and ENTRYPOINT interaction
Both define the command to run when the container starts.
- A
Dockerfilemust have at least oneCMDorENTRYPOINT ENTRYPOINTwhen using the container as an executableCMDshould be the way to define default parameters for theENTRYPOINTcommand, or a transient command to run in the container- When the container is run with other args,
CMDis overridden
CMD and ENTRYPOINT interaction rules
If the base image defines a
CMD, settingENTRYPOINTresetsCMDto empty.
| No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
|---|---|---|---|
| No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
| CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
| CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd |
| CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
VOLUME
VOLUME ["/data"]
The
VOLUMEinstruction creates a mount point with the specified name and marks it as holding externally-mounted volumes from the host or other containers. The value may be aJSONarray,VOLUME [”/var/log/”], or a plain string with multiple args, e.g.VOLUME /var/logorVOLUME /var/log /var/db.
| |
This creates a mount directory
/myvolinside the container
| |
- Changing a volume from the Dockerfile: if any build step changes data in a volume after it’s declared, those changes are discarded.
- Declare volumes at the end of the file!
- JSON formatting: the list is parsed as a JSON array. You must use double-quotes (
") not single-quotes ('). - Host directories at runtime: host directories (mount points) are inherently host-dependent. This keeps images portable, since a given host directory isn’t guaranteed on every host. For this reason you cannot mount a host directory from the Dockerfile.
VOLUMEdoes not support ahost-dirargument. You must specify the mount point when creating/running the container.
USER
USER <user>[:<group>]USER <UID>[:<GID>]
USER sets the username (or UID) and optional group (or GID) to use when running the image, and for any RUN, CMD, and ENTRYPOINT that follow it in the Dockerfile.
When you specify a group for the user, the user has only that group’s membership. Any other configured group memberships are ignored.
WORKDIR
WORKDIR /path/to/workdirSets the working directory for any
RUN,CMD,ENTRYPOINT,COPY, andADDthat follow it. IfWORKDIRdoesn’t exist, it’s created — even if it’s not used in any later instruction.WORKDIRcan be used multiple times. A relative path is relative to the previousWORKDIR.WORKDIRresolvesENVvariables set before itIf unset,
WORKDIRdefaults to/
| |
ARG
ARG <name>[=<default value>]ARGdefines a variable users can pass at build time with the--build-arg <varname>=<value>flag ofdocker build. If a user passes a build arg not defined in theDockerfile, the build prints a warning.Don’t use build-time variables to pass secrets like
github keysoruser credentials. Withdocker history, build-time values are visible to anyone with the image.
| |
Default values
| |
Scope
- An
ARGvariable is in effect from the line it’s declared on, downward in theDockerfile; nowhere else. docker build --build-arg <name>=<value>overrides<arg-name>- An
ARGgoes out of scope at the end of the build stage that declares it. To use anARGacross stages, each stage must declare it.- Each
FROMstarts a build stage
- Each
| |
Using ARG variables
- For the same-named
ARGandENV, anENVdefined after theARGoverrides the nearest precedingARGvalue. ARG VAR_NAMEmay be repeated; each acts as a reset and can be overridden by the nearest following same-namedENV.- Avoid:
- declaring
ARG VAR_NAMEmultiple times - using the same name for an ARG and an ENV
- naming a variable the same as a system one, unless you mean to
- declaring
| |
docker build --build-arg CONT_IMG_VER=v2.0.1 .outputs v1.0.0
Built-in args
docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .
- HTTP_PROXY
- http_proxy
- HTTPS_PROXY
- https_proxy
- FTP_PROXY
- ftp_proxy
- NO_PROXY
- no_proxy
Platform-related global args
docker build --platform=
- Requires BuildKit
18.09+DOCKER_BUILDKIT=1
- TARGETPLATFORM - platform of the build result. Eg linux/amd64, linux/arm/v7, windows/amd64.
- TARGETOS - OS component of TARGETPLATFORM
- TARGETARCH - architecture component of TARGETPLATFORM
- TARGETVARIANT - variant component of TARGETPLATFORM
- BUILDPLATFORM - platform of the node performing the build.
- BUILDOS - OS component of BUILDPLATFORM
- BUILDARCH - architecture component of BUILDPLATFORM
- BUILDVARIANT - variant component of BUILDPLATFORM
Cache invalidation by ARG
docker build --build-arg CONT_IMG_VER=v2.0.1 .
| |
- exec count 1: no use cache
- exec count 2: use cache
- exec count 3, but change
CONT_IMG_VER=v2.0.2: everything beforeARG CONT_IMG_VERused cache; everything after is cache-invalidated
ONBUILD
ONBUILD <INSTRUCTION>
Notes
- Any build instruction may be registered as a trigger except
FROM,MAINTAINER,ONBUILD - Standardize the base-image build flow; downstream users just follow your trigger setup
- Example: copy source/files into a given dir by default
ONBUILD ADD . /app/srcorONBUILD COPY . /app/src- Note the
Dockerfilemust then be at your project root or beside your resource files - Exclude files that don’t need to be in the build via
.dockerignore
- Note the
ONBUILD adds a trigger instruction to the image, to run later when this image is used as the base for another build. The trigger runs in the downstream build context as if inserted right after the downstream Dockerfile’s FROM.
This is very useful when you’re building an image meant to be a base for others — e.g. an app build environment or a daemon customizable with user-specific config.
For example, if your image is a reusable Python app builder, you need to add the app source to a specific directory and maybe call a build script afterwards. You can’t just call ADD and RUN now because you don’t have the app source yet, and it differs per app build. You could hand app developers a boilerplate Dockerfile to copy-paste, but that’s inefficient, error-prone, and hard to update, since it’s mixed into app-specific code.
The solution is ONBUILD to register pre-run instructions for the next build stage.
How ONBUILD works
- On encountering
ONBUILD, the builder adds a trigger to the image’s metadata. Otherwise it doesn’t affect the current build.
- On encountering
- At the end of the build, all triggers are stored under the
OnBuildkey in the image manifest. They can be inspected viadocker inspect.
- At the end of the build, all triggers are stored under the
- Later, that image can be used as a base via
FROM. As part ofFROM, the downstream builder looks up theONBUILDtriggers and runs them in registration order. If any trigger fails,FROMaborts, which aborts the build. If all succeed,FROMcompletes and the build continues.
- Later, that image can be used as a base via
- Triggers are cleared from the final image after execution — they are not inherited by “child” builds.
STOPSIGNAL
STOPSIGNAL signal
STOPSIGNAL sets the system-call signal sent to the container to exit. It may be a signal name in SIG<name> form (e.g. SIGKILL) or an unsigned number matching a position in the kernel’s syscall table (e.g. 9). If unset, defaults to SIGTERM.
The --stop-signal flag on docker run and docker create overrides the image’s default stop signal per container.
HEALTHCHECK
HEALTHCHECK [OPTIONS] CMD command(check container health by running a command inside the container)HEALTHCHECK NONE(disable any healthcheck inherited from the base image)
Used to detect whether the service in the container is healthy — e.g. stuck in an infinite loop unable to handle new requests while the service still appears “up”.
OPTIONS (all optional):
--interval=DURATION (default: 30s)--timeout=DURATION (default: 30s)--start-period=DURATION (default: 0s)--retries=N (default: 3)
The command’s
exit statusindicates health. Values:
- 0:
success- the container is healthy and ready for use - 1:
unhealthy- the container is not working correctly - 2:
reserved- do not use this exit code
HEALTHCHECK example
| |
SHELL
SHELL ["executable", "parameters"]
SHELLoverrides the default shell used for shell-form commands- Default shell on Linux is
["/bin/sh", "-c"] - Default shell on Windows is
["cmd", "/s", "/c"] SHELLmust be written as JSON in theDockerfile
- Default shell on Linux is
SHELLis especially useful on Windows, which has two common, quite different native shells:cmdandpowershell, plus optional shells includingshSHELLmay appear multiple times. EachSHELLoverrides all previous ones and affects all later instructions- If you need another shell —
zsh,csh,tcsh, etc. — you can useSHELLon Linux too
| |
Example
go1.18base-image build; for more base images see docker-compose-resources
| |
Best practices
- Each instruction creates a layer. For downstream images the upstream image is a read-only layer; the current build just appends layers on top.
- Prefer
COPYoverADD— more transparent and direct docker build [OPTIONS] -f PATH; for stdin input usedocker build [OPTIONS] -f- PATH.dockerignoreexcludes files that don’t need to be in the build — see dockerignore-file- Optimize image size
- Use multi-stage builds to shrink the final image
- You can target a specific stage:
docker build --target builder -t xxxx . - A stage can copy from earlier stages or existing images:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
- You can target a specific stage:
- Don’t install unnecessary packages
- Copy only the files you need (pair with .dockerignore)
- One container, one concern — avoid running multiple services in one container
- Compress executables when you can
- Minimize the layer count
- Only
RUN,COPY,ADDcreate layers — merge/trim related instructions into one or a few lines - Merge and reorder instructions; use a backslash for multi-line; clean up temp files afterwards
- For pipes, use them directly:
RUN set -o pipefail && wget -O - https://some.site | wc -l > /number
- Only
- Use multi-stage builds to shrink the final image
- Build-cache invalidation
ADD/COPYsrcchanges invalidate laterRUNcachesARG/ENVchanges invalidate the first instruction that uses themand everything after
- Cache-invalidation tips
- Push such steps to the end, or replace with other commands
- Keep
srcand other references stable
- Only
PID=1can be terminated bydocker stop; thesh -cof shell form hasPID!=1 - On
docker stop, aPID=1container exits cleanly; after the stop timeout,SIGKILLis sent - CMD and ENTRYPOINT interaction rules