DOCKERFILE(5)

πŸ“¦
version 29.9.2 Β· 5 versions
, in fedora 43, fedora 44, ubuntu jammy, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸ’– DOCKERFILE(5) πŸ’–

Docker User Manuals 🌸 this one is the Dockerfile page, bestie

πŸŽ€ NAME πŸŽ€

Dockerfile - automate the steps of creating a Docker image ✨

πŸ¦‹ INTRODUCTION πŸ¦‹

okay so the Dockerfile is a config file that automates the steps of creating a Docker image πŸ”₯ it's kinda like a Makefile, if you've ever touched one. Docker reads instructions out of the Dockerfile so it can do all the steps you'd otherwise do by hand to make an image. to build an image, you just create a file called Dockerfile πŸ’…

The Dockerfile describes every step taken to assemble the image. once your Dockerfile exists, you call the docker build command and pass it the path of the directory that contains your Dockerfile as the argument 🫢

🌈 SYNOPSIS 🌈

INSTRUCTION arguments

For example:

FROM image

πŸ’œ DESCRIPTION πŸ’œ

A Dockerfile is a file that automates the steps of creating a Docker image ✨ and it's similar to a Makefile, so if that clicked for you, you're already ahead πŸ™ˆ

πŸ“ USAGE πŸ“

docker build .
  • Runs the steps and commits them, building a final image. πŸ—οΈ The path to the source repository tells the build where to find its context. The build is run by the Docker daemon, not the CLI, so the whole context has to be transferred to the daemon. The Docker CLI shows "Sending build context to Docker daemon" once that context is sent over.
docker build -t repository/tag .
  • Specifies a repository and tag to save the new image under, if the build succeeds. 🎯 The Docker daemon runs the steps one-by-one, committing the result to a new image when needed, and then finally outputs the ID of the new image. The Docker daemon automatically cleans up the context it was given 🧹

Docker reuses intermediate images whenever possible πŸ” which significantly accelerates the docker build process. literally faster builds, slay πŸ’…

🧚 FORMAT 🧚

FROM image
FROM image:tag
FROM image@digest
  • The FROM instruction sets the base image for subsequent instructions. 🌱 A valid Dockerfile must have FROM as its first instruction. The image can be any valid image, and it's super easy to start by pulling one from the public repositories.
  • FROM must be the first non-comment instruction in the Dockerfile. πŸ‘‘
  • FROM can show up multiple times in a single Dockerfile so you can make multiple images πŸ¦‹ Make a note of the last image ID output by the commit before each new FROM command.
  • If no tag is given to FROM, Docker applies the latest tag. If the tag you used doesn't exist, an error is returned πŸ™ˆ
  • If no digest is given to FROM, Docker applies the latest tag. If the tag you used doesn't exist, an error is returned πŸ’€

πŸ“ MAINTAINER πŸ“

  • MAINTAINER sets the Author field for the generated images. It's useful for giving users an email or URL for support πŸ’Œ

πŸƒ RUN πŸƒ

RUN has two forms, and they're both giving different energy ✨

# the command is run in a shell - /bin/sh -c
RUN <command>

# Executable form
RUN ["executable", "param1", "param2"]
  • The RUN instruction executes any commands in a new layer on top of the current image and commits the results. πŸ“¦ The committed image is used for the next step in the Dockerfile.
  • Layering RUN instructions and generating commits matches the core concepts of Docker 🧠 commits are cheap, and containers can be created from any point in the history of an image. It's kinda like source control. The exec form makes it possible to avoid shell string munging, and it also makes it possible to RUN commands using a base image that does not contain /bin/sh 🫢
Heads up bestie: the exec form is parsed as a JSON array, so you must use double-quotes (") around words, not single-quotes (') ⚠️

🎬 CMD 🎬

CMD has three forms, each one iconic in its own way πŸ‘‘

# Executable form
CMD ["executable", "param1", "param2"]`

# Provide default arguments to ENTRYPOINT
CMD ["param1", "param2"]`

# the command is run in a shell - /bin/sh -c
CMD command param1 param2
  • There should be only one CMD in a Dockerfile. If you list more than one, only the last CMD takes effect πŸ™ƒ
  • The main purpose of a CMD is to provide defaults for an executing container. πŸŽ€ These defaults may include an executable, or they can omit the executable. If they omit the executable, an ENTRYPOINT must be specified.
  • When used in the shell or exec formats, the CMD instruction sets the command to be executed when running the image 🎯
  • If you use the shell form of CMD, the <command> executes in /bin/sh -c
Same warning as before, babe: the exec form is parsed as a JSON array, which means you must use double-quotes (") around words, not single-quotes (') πŸ™ˆ
FROM ubuntu
CMD echo "This is a test." | wc -
  • If you run a command without a shell, you must express the command as a JSON array and give the full path to the executable. This array form is the preferred form of CMD πŸ† All additional parameters must be individually expressed as strings in the array:
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
  • To make the container run the same executable every time, use ENTRYPOINT in combination with CMD ✨
  • If the user specifies arguments to docker run, those specified commands override the default in CMD πŸ’…
  • Do not confuse RUN with CMD!! 😭 RUN runs a command and commits the result. CMD executes nothing at build time, but specifies the intended command for the image 🧚

🏷️ LABEL 🏷️

LABEL has a couple of looks:

LABEL <key>=<value> [<key>=<value> ...]

or

LABEL <key>[ <value>]
LABEL <key>[ <value>]
...
  • The LABEL instruction adds metadata to an image. πŸ’– A label is a key-value pair. To specify a label without a value, just use an empty string. To include spaces within a label value, use quotes and backslashes the same way you would in command-line parsing πŸŽ€
LABEL com.example.vendor="ACME Incorporated"
LABEL com.example.vendor "ACME Incorporated"
LABEL com.example.vendor.is-beta ""
LABEL com.example.vendor.is-beta=
LABEL com.example.vendor.is-beta=""
  • An image can have more than one label. To specify multiple labels, separate each key-value pair by a space 🌈
  • Labels are additive, including LABELs in FROM images. As the system encounters and then applies a new label, new keys override any previous labels with identical keys πŸ”„
  • To display an image's labels, use the docker inspect command πŸ”

πŸ›‘ STOPSIGNAL πŸ›‘

STOPSIGNAL <signal>
  • The STOPSIGNAL instruction sets the system call signal that will be sent to the container to exit. πŸšͺ This signal can be a signal name in the format SIG, for instance SIGKILL, or an unsigned number that matches a position in the kernel's syscall table, for instance 9. The default is SIGTERM if not defined ✨
  • The image's default stopsignal can be overridden per container 🎯 using the --stop-signal flag on docker-run(1) and docker-create(1).

πŸšͺ EXPOSE πŸšͺ

EXPOSE <port> [<port>...]
  • The EXPOSE instruction informs Docker that the container listens on the specified network ports at runtime. πŸ“‘ Docker uses this info to interconnect containers using links, and to set up port redirection on the host system 🌐

🌿 ENV 🌿

ENV <key> <value>
  • The ENV instruction sets the environment variable to the value <value>. 🌱 This value is passed to all future RUN, ENTRYPOINT, and CMD instructions. It's functionally equivalent to prefixing the command with <key>=<value> 🧠
  • Environment variables set with ENV persist when a container is run from the resulting image πŸ’Ύ Use docker inspect to inspect these values, and change them using docker run --env <key>=<value>.
Bestie, heads up: setting "ENV DEBIAN_FRONTEND=noninteractive" may cause unintended consequences, because it will persist when the container is run interactively, as with the following command 😬
docker run -t -i image bash

πŸ“¦ ADD πŸ“¦

ADD has two forms 🎁

ADD <src> <dest>

# Required for paths with whitespace
ADD ["<src>",... "<dest>"]
  • The ADD instruction copies new files, directories, or remote file URLs to the filesystem of the container at path <dest> 🚚 Multiple <src> resources may be specified, but if they are files or directories then they must be relative to the source directory that is being built (the context of the build) πŸ“
  • The <dest> is the absolute path, or a path relative to WORKDIR, into which the source is copied inside the target container 🎯
  • If the <src> argument is a local file in a recognized compression format (tar, gzip, bzip2, etc) then it is unpacked at the specified <dest> in the container's filesystem πŸ“‚
  • Only local compressed files will be unpacked. The URL download and archive unpacking features cannot be used together πŸ™…β€β™€οΈ
  • All new directories are created with mode 0755 and with the uid and gid of 0 πŸ”

okay so an ENTRYPOINT lets you set up your container like a little executable 🧚✨ once you add one, the whole container acts like it's only that program, bestie πŸ’…

The ENTRYPOINT instruction adds a command that does not get overwritten when you pass arguments to docker run. This is NOT how CMD works πŸ™ˆ so arguments you pass in can go straight to the entrypoint. For example, docker run <image> -d hands the -d argument to the ENTRYPOINT πŸ¦‹

You can set parameters two ways: in the ENTRYPOINT JSON array (the preferred exec form from above), or with a CMD statement. Parameters inside the ENTRYPOINT are safe and won't get overwritten by docker run arguments 🫢 but parameters set through CMD will get overwritten by docker run arguments. Plot twist 😭

If you give the ENTRYPOINT a plain string, it runs inside /bin/sh -c, just like a CMD instruction:

FROM ubuntu
ENTRYPOINT wc -l -

So this image always takes stdin as input (that's what - means) and prints the number of lines (that's what -l means) ✨ To make that optional but still the default, use a CMD instead:

FROM ubuntu
CMD ["-l", "-"]
ENTRYPOINT ["/usr/bin/wc"]

VOLUME

VOLUME ["/data"]

The VOLUME instruction makes a mount point with the name you give it 🌸 and marks it as holding externally-mounted volumes, either from the native host or from other containers.

USER

USER daemon

This sets the username or UID used to run the commands that come after it πŸŽ€

The USER instruction can also optionally set the group or GID. These examples are all valid:

USER [user | user:group | uid | uid:gid | user:gid | uid:group ]

Until the USER instruction shows up, everything runs as root πŸ‘‘ You can use USER as many times as you want in a Dockerfile, and it only affects the commands after it.

WORKDIR

WORKDIR /path/to/workdir

WORKDIR sets the working directory for the RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it πŸ“ You can use it multiple times in one Dockerfile. Relative paths are defined relative to the path of the previous WORKDIR instruction. For example:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

In that example, the output of pwd is a/b/c ✨ so yeah, it stacks up, periodt πŸ’–

ARG

ARG [=]

ARG defines a variable that users can pass in at build-time to the builder with the docker build command, using the --build-arg <varname>=<value> flag 🌈 If a user passes a build argument that wasn't defined in the Dockerfile, the build spits out a warning:

[Warning] One or more build-args [foo] were not consumed

The Dockerfile author can define one variable by writing ARG once, or a bunch of them by writing ARG more than once. For example, a valid Dockerfile:

FROM busybox
ARG user1
ARG buildno
...

A Dockerfile author can also give an ARG a default value:

FROM busybox
ARG user1=someuser
ARG buildno=1
...

If an ARG has a default and no value gets passed in at build-time, the builder uses the default 🫢

An ARG variable comes into effect from the line where it's defined in the Dockerfile. It does NOT come into effect from wherever the argument gets used on the command line or elsewhere 😭 Take this Dockerfile:

1 FROM busybox
2 USER ${user:-some_user}
3 ARG user
4 USER $user
...

A user builds it by calling:

$ docker build --build-arg user=what_user Dockerfile

The USER on line 2 evaluates to some_user, because the user variable isn't defined until the next line, line 3. The USER on line 4 evaluates to what_user, because user is defined by then and the what_user value was passed on the command line πŸ’… Before its ARG instruction defines it, any use of a variable gives you an empty string.

Warning: It is not recommended to use build-time variables for passing secrets like github keys, user credentials etc. Build-time variable values are visible to any user of the image with the docker history command. πŸ™ˆπŸ’€

You can use an ARG or an ENV instruction to specify variables that are available to the RUN instruction. Environment variables set with ENV always override an ARG instruction with the same name πŸ‘‘ Here's a Dockerfile with both an ENV and an ARG instruction:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER=v1.0.0
4 RUN echo $CONT_IMG_VER

Then, say this image gets built with this command:

$ docker build --build-arg CONT_IMG_VER=v2.0.1 Dockerfile

In this case, the RUN instruction uses v1.0.0 instead of the v2.0.1 ARG value the user passed in. This is similar to a shell script, where a locally scoped variable overrides variables passed as arguments or inherited from the environment, starting from the point where it's defined 🧚

Using the example above but with a different ENV specification, you can create more useful interactions between ARG and ENV instructions:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
4 RUN echo $CONT_IMG_VER

Unlike ARG, ENV values are always persisted in the built image ✨ Consider a docker build without the --build-arg flag:

$ docker build Dockerfile

With this Dockerfile, CONT_IMG_VER is still persisted in the image, but its value is v1.0.0 because that's the default set on line 3 by the ENV instruction πŸ“

The variable expansion trick in this example lets you pass arguments from the command line and persist them in the final image by leveraging the ENV instruction πŸ’– Variable expansion is only supported for a limited set of Dockerfile instructions. ⟨#environment-replacement⟩

Docker has a set of predefined ARG variables you can use without a matching ARG instruction in the Dockerfile 🌈

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy
  • ALL_PROXY
  • all_proxy

To use these, pass them on the command line with the --build-arg flag, for example:

$ docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .

ONBUILD

ONBUILD [INSTRUCTION]

ONBUILD adds a trigger instruction to an image πŸ¦‹ The trigger doesn't run right away. It runs later, when the image gets used as the base for another build. Docker runs the trigger in the context of that downstream build, as if the trigger existed right after the FROM instruction in the downstream Dockerfile.

You can register any build instruction as a trigger πŸ’œ Triggers are super useful if you're defining an image to use as a base for building other images, like an application build environment or a daemon customized with user-specific configuration.

Imagine an image meant to be a reusable python application builder. It has to add application source code into a particular directory, and it might need a build script called after that. You can't just call ADD and RUN right now, because you don't have access to the application source code yet, and it's different for every application build 😭

Giving application developers a boilerplate Dockerfile to copy-paste into their app is inefficient, error-prone, and hard to update, because it gets mixed up with application-specific code πŸ™ˆ The fix? Use ONBUILD to register instructions in advance, so they run later, during the next build stage πŸ‘‘

HISTORY

  • May 2014: Compiled by Zac Dover (zdover at redhat dot com) based on docker.com Dockerfile documentation.
  • Feb 2015: updated by Brian Goff (cpuguy83@gmail.com) for readability.
  • Sept 2015: updated by Sally O'Malley (somalley@redhat.com).
  • Oct 2016: updated by Addam Hardy (addam.hardy@gmail.com).

That's the whole page, bestie 🫢✨ It came from the Docker Community, last updated May 2014, and it's DOCKERFILE(5) forever πŸŽ€πŸ’