Dockerfile cheat sheet
hello every body , i want to share common commands that i use in my Dockerfiles ! .
this is a quick cheat sheet for Dockerfile commands . in the next article we will learn about Dockerfile’s best practices .
so , let’s get started :
FROM
Usage:
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
Information:
FROM
must be the first non-comment instruction in the Dockerfile.FROM
can appear multiple times within a single Dockerfile in order to create multiple images. Simply make a note of the last image ID output by the commit before each newFROM
command.- The
tag
ordigest
values are optional. If you omit either of them, the builder assumes alatest
by default. The builder returns an error if it cannot match thetag
value.
MAINTAINER
Usage:
MAINTAINER <name>
The MAINTAINER
instruction allows you to set the Author field of the generated images.
RUN
Usage:
RUN <command>
(shell form, the command is run in a shell, which by default is/bin/sh -c
on Linux orcmd /S /C
on Windows)RUN ["<executable>", "<param1>", "<param2>"]
(exec form)
Information:
- The exec form makes it possible to avoid shell string munging, and to
RUN
commands using a base image that does not contain the specified shell executable. - The default shell for the shell form can be changed using the
SHELL
command. - Normal shell processing does not occur when using the exec form. For example,
RUN ["echo", "$WORKDIR"]
will not do variable substitution on$WORKDIR
.
Shell vs. Exec
Both the ENTRYPOINT and CMD instructions support two different forms: the shell form and the exec form. In the example above, I used the shell form which looks like this:
CMD executable param1 param2
When using the shell form, the specified binary is executed with an invocation of the shell using /bin/sh -c
. You can see this clearly if you run a container and then look at the docker ps
output:
$ docker run -d demo
15bfcddb11b5cde0e230246f45ba6eeb1e6f56edb38a91626ab9c478408cb615$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED
15bfcddb4312 demo:latest "/bin/sh -c 'ping localhost'" 2 seconds ago
Here I’ve run the “demo” image again and you can see that the command which was executed was /bin/sh -c 'ping localhost'
.
This appears to work just fine, but there are some subtle issues that can occur when using the shell form of either the ENTRYPOINT or CMD instruction. If we peek inside our running container and look at the running processes we will see something like this:
$ docker exec 15bfcddb ps -f
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 20:14 ? 00:00:00 /bin/sh -c ping localhost
root 9 1 0 20:14 ? 00:00:00 ping localhost
root 49 0 0 20:15 ? 00:00:00 ps -f
Note how the process running as PID 1 is not our ping command, but is the /bin/sh executable. This can be problematic if we need to send any sort of POSIX signals to the container since /bin/sh won’t forward signals to child processes .
Beyond the PID 1 issue, you may also run into problems with the shell form if you’re building a minimal image which doesn’t even include a shell binary. When Docker is constructing the command to be run it doesn’t check to see if the shell is available inside the container — if you don’t have /bin/sh in your image, the container will simply fail to start.
A better option is to use the exec form of the ENTRYPOINT/CMD instructions which looks like this:
CMD ["executable","param1","param2"]
Note that the content appearing after the CMD instruction in this case is formatted as a JSON array.
When the exec form of the CMD instruction is used the command will be executed without a shell.
Let’s change our Dockerfile from the example above to see this in action:
FROM ubuntu:trusty
CMD ["/bin/ping","localhost"]
Rebuild the image and look at the command that is generated for the running container:
$ docker build -t demo .
[truncated]$ docker run -d demo
90cd472887807467d699b55efaf2ee5c4c79eb74ed7849fc4d2dbfea31dce441$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED
90cd47288780 demo:latest "/bin/ping localhost" 4 seconds ago
Now /bin/ping is being run directly without the intervening shell process (and, as a result, will end up as PID 1 inside the container).
Whether you’re using ENTRYPOINT or CMD (or both) the recommendation is to always use the exec form so that’s it’s obvious which command is running as PID 1 inside your container.
CMD
Usage:
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)
Information:
- The main purpose of a
CMD
is to provide defaults for an executing container. These defaults can include an executable, or they can omit the executable, in which case you must specify anENTRYPOINT
instruction as well. - There can only be one
CMD
instruction in a Dockerfile. If you list more than oneCMD
then only the lastCMD
will take effect. - If
CMD
is used to provide default arguments for theENTRYPOINT
instruction, both theCMD
andENTRYPOINT
instructions should be specified with the JSON array format. - If the user specifies arguments to
docker run
then they will override the default specified inCMD
. - Normal shell processing does not occur when using the exec form. For example,
CMD ["echo", "$WORKDIR"]
will not do variable substitution on$WORKDIR
.
ENTRYPOINT
Usage:
ENTRYPOINT ["<executable>", "<param1>", "<param2>"]
(exec form, preferred)ENTRYPOINT <command> <param1> <param2>
(shell form)
Information:
- Allows you to configure a container that will run as an executable.
- Command line arguments to
docker run <image>
will be appended after all elements in an exec formENTRYPOINT
and will override all elements specified usingCMD
. - The shell form prevents any
CMD
or run command line arguments from being used, but theENTRYPOINT
will start via the shell. This means the executable will not be PID 1 nor will it receive UNIX signals. Prependexec
to get around this drawback. - Only the last
ENTRYPOINT
instruction in the Dockerfile will have an effect.
Notice about some differences between ENTRYPOINT and CMD :
Both ENTRYPOINT and CMD allow you to specify the startup command for an image, but there are subtle differences between them. There are many times where you’ll want to choose one or the other, but they can also be used together. We’ll explore all these scenarios in the sections below.
ENTRYPOINT or CMD
Ultimately, both ENTRYPOINT and CMD give you a way to identify which executable should be run when a container is started from your image. In fact, if you want your image to be runnable (without additional docker run
command line arguments) you must specify an ENTRYPOINT or CMD.
Trying to run an image which doesn’t have an ENTRYPOINT or CMD declared will result in an error
$ docker run alpine
FATA[0000] Error response from daemon: No command specified
Many of the Linux distro base images that you find on the Docker Hub will use a shell like /bin/sh or /bin/bash as the the CMD executable. This means that anyone who runs those images will get dropped into an interactive shell by default (assuming, of course, that they used the -i
and -t
flags with the docker run
command). This makes sense for a general-purpose base image, but you will probably want to pick a more specific CMD or ENTRYPOINT for your own images.
The ENTRYPOINT or CMD that you specify in your Dockerfile identify the default executable for your image. However, the user has the option to override either of these values at run time.
For example, let’s say that we have the following Dockerfile
FROM ubuntu:trusty
CMD ping localhost
If we build this image (with tag “demo”) and run it we would see the following output:
$ docker run -t demo
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.051 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.038 ms
^C
--- localhost ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.026/0.032/0.039/0.008 ms
You can see that the ping executable was run automatically when the container was started. However, we can override the default CMD by specifying an argument after the image name when starting the container:
$ docker run demo hostname
6c1573c0d4c0
In this case, hostname was run in place of ping
The default ENTRYPOINT can be similarly overridden but it requires the use of the --entrypoint
flag:
$ docker run --entrypoint hostname demo
075a2fa95ab7
Given how much easier it is to override the CMD, the recommendation is use CMD in your Dockerfile when you want the user of your image to have the flexibility to run whichever executable they choose when starting the container. For example, maybe you have a general Ruby image that will start-up an interactive irb session by default (CMD irb
) but you also want to give the user the option to run an arbitrary Ruby script (docker run ruby ruby -e 'puts "Hello"'
)
In contrast, ENTRYPOINT should be used in scenarios where you want the container to behave exclusively as if it were the executable it’s wrapping. That is, when you don’t want or expect the user to override the executable you’ve specified.
There are many situations where it may be convenient to use Docker as portable packaging for a specific executable. Imagine you have a utility implemented as a Python script you need to distribute but don’t want to burden the end-user with installation of the correct interpreter version and dependencies. You could package everything in a Docker image with an ENTRYPOINT referencing your script. Now the user can simply docker run
your image and it will behave as if they are running your script directly.
Of course you can achieve this same thing with CMD, but the use of ENTRYPOINT sends a strong message that this container is only intended to run this one command. The utility of ENTRYPOINT will become clearer when i show how you can combine ENTRYPOINT and CMD together .
ENTRYPOINT and CMD
Up to this point, we’ve discussed how to use ENTRYPOINT or CMD to specify your image’s default executable. However, there are some cases where it makes sense to use ENTRYPOINT and CMD together.
Combining ENTRYPOINT and CMD allows you to specify the default executable for your image while also providing default arguments to that executable which may be overridden by the user. Let’s look at an example:
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
Let’s build and run this image without any additional docker run
arguments:
$ docker build -t ping .
[truncated]$ docker run ping
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.025 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.038 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.051 ms--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.025/0.038/0.051/0.010 ms$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED
82df66a2a9f1 ping:latest "/bin/ping -c 3 localhost" 6 seconds ago
Note that the command which was executed is a combination of the ENTRYPOINT and CMD values that were specified in the Dockerfile. When both an ENTRYPOINT and CMD are specified, the CMD string(s) will be appended to the ENTRYPOINT in order to generate the container’s command string. Remember that the CMD value can be easily overridden by supplying one or more arguments to `docker run` after the name of the image. In this case we could direct our ping to a different host by doing something like this:
$ docker run ping docker.io
PING docker.io (162.242.195.84) 56(84) bytes of data.
64 bytes from 162.242.195.84: icmp_seq=1 ttl=61 time=76.7 ms
64 bytes from 162.242.195.84: icmp_seq=2 ttl=61 time=81.5 ms
64 bytes from 162.242.195.84: icmp_seq=3 ttl=61 time=77.8 ms--- docker.io ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 76.722/78.695/81.533/2.057 ms$ docker ps -l --no-trunc
CONTAINER ID IMAGE COMMAND CREATED
0d739d5ea4e5 ping:latest "/bin/ping -c 3 docker.io" 51 seconds ago
Running the image starts to feel like running any other executable — you specify the name of the command you want to run followed by the arguments you want to pass to that command.
Note how the -c 3
argument that was included as part of the ENTRYPOINT essentially becomes a "hard-coded" argument for the ping command (the -c
flag is used to limit the ping count to the specified number). It's included in each invocation of the image and can't be overridden in the same way as the CMD parameter.
If you want your image to actually do anything when it is run, you should definitely configure some sort of ENTRYPOINT or CMD in you Dockerfile. However, remember that they aren’t mutually exclusive. In many cases you can improve the user experience of your image by using them in combination.
No matter how you use these instructions you should always default to using the exec form.
EXPOSE
Usage:
EXPOSE <port> [<port> ...]
Information:
- Informs Docker that the container listens on the specified network port(s) at runtime.
EXPOSE
does not make the ports of the container accessible to the host.
ENV
Usage:
ENV <key> <value>
ENV <key>=<value> [<key>=<value> ...]
Information:
- The
ENV
instruction sets the environment variable<key>
to the value<value>
. - The value will be in the environment of all “descendant” Dockerfile commands and can be replaced inline as well.
- The environment variables set using
ENV
will persist when a container is run from the resulting image. - The first form will set a single variable to a value with the entire string after the first space being treated as the
<value>
- including characters such as spaces and quotes.
ADD
Usage:
ADD <src> [<src> ...] <dest>
ADD ["<src>", ... "<dest>"]
(this form is required for paths containing whitespace)
Information:
- Copies new files, directories, or remote file URLs from
<src>
and adds them to the filesystem of the image at the path<dest>
. <src>
may contain wildcards and matching will be done using Go’s filepath.Match rules.- If
<src>
is a file or directory, then they must be relative to the source directory that is being built (the context of the build). <dest>
is an absolute path, or a path relative toWORKDIR
.- If
<dest>
doesn’t exist, it is created along with all missing directories in its path.
COPY
Usage:
COPY <src> [<src> ...] <dest>
COPY ["<src>", ... "<dest>"]
(this form is required for paths containing whitespace)
Information:
- Copies new files or directories from
<src>
and adds them to the filesystem of the image at the path<dest>
. <src>
may contain wildcards and matching will be done using Go’s filepath.Match rules.<src>
must be relative to the source directory that is being built (the context of the build).<dest>
is an absolute path, or a path relative toWORKDIR
.- If
<dest>
doesn’t exist, it is created along with all missing directories in its path.
ADD vs COPY
If you’re not interested in the nuances of ADD and COPY and just want an answer to “which one should I use?”, all you need to know is: use COPY !!!! .
ADD : one of features of ADD is the ability to automatically unpack compressed files. 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.
ADD /foo.tar.gz /tmp/
The command above would result in the contents of the foo.tar.gz archive being unpacked into the container’s /tmp
directory.
Interestingly, the URL download and archive unpacking features cannot be used together. Any archives copied via URL will NOT be automatically unpacked.
COPY : doesn’t support URLs as a <src>
argument so it can't be used to download files from remote locations. Anything that you want to COPY into the container must be present in the local build context.
Also, COPY doesn’t give any special treatment to archives. If you COPY an archive file it will land in the container exactly as it appears in the build context without any attempt to unpack it.
COPY is really just a stripped-down version of ADD that aims to meet the majority of the “copy-files-to-container” use cases without any surprises.
Which to Use?
In case it isn’t obvious by now, the recommendation from the Docker team is to use COPY in almost all cases.
Really, the only reason to use ADD is when you have an archive file that you definitely want to have auto-extracted into the image. Ideally, ADD would be renamed to something like EXTRACT to really drive this point home (again, for backward-compatibility reasons, this is unlikely to happen).
OK, but what about fetching packages from remote URLs, isn’t ADD still useful for that? Technically, yes, but in most cases you’re probably better off RUNning a curl
or wget
. Consider the following example:
ADD http://package.tar.bz /tmp/
RUN tar -xjf /tmp/package.tar.bz2 \
&& make -C /tmp/package \
&& rm /tmp/package.tar.bz2
Here we have an ADD instruction which retrieves a package from a URL followed by a RUN instruction which unpacks it, builds it and then attempts to clean-up the downloaded archive.
Unfortunately, since the package retrieval and the rm
command are in separate image layers we don't actually save any space in our final image
In this case you’re better off doing something like this:
RUN curl http://package.tar.bz2 \
| tar -xjC /tmp/package \
&& make -C /tmp/package
Here we curl
the package and pipe it right into the tar
command for extraction. This way we aren't left with an archive file on the filesystem that we need to clean-up.
There may still be valid reasons to ADD a remote file to your image, but that should be an explicit decision and not your default choice.
Ultimately, the rule is this: use COPY (unless you’re absolutely sure you need ADD).
VOLUME
Usage:
VOLUME ["<path>", ...]
VOLUME <path> [<path> ...]
Creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containers.
VOLUME
Usage:
VOLUME ["<path>", ...]
VOLUME <path> [<path> ...]
Creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containers.
WORKDIR
Usage:
WORKDIR </path/to/workdir>
Information:
- Sets the working directory for any
RUN
,CMD
,ENTRYPOINT
,COPY
, andADD
instructions that follow it. - It can be used multiple times in the one Dockerfile. If a relative path is provided, it will be relative to the path of the previous
WORKDIR
instruction.
ARG
Usage:
ARG <name>[=<default value>]
Information:
- Defines a variable that users can pass at build-time to the builder with the
docker build
command using the--build-arg <varname>=<value>
flag. - Multiple variables may be defined by specifying
ARG
multiple times. - 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.
- Environment variables defined using the
ENV
instruction always override anARG
instruction of the same name. - Docker has a set of predefined
ARG
variables that you can use without a corresponding ARG instruction in the Dockerfile. HTTP_PROXY
andhttp_proxy
HTTPS_PROXY
andhttps_proxy
FTP_PROXY
andftp_proxy
NO_PROXY
andno_proxy
ONBUILD
Usage:
ONBUILD <Dockerfile INSTRUCTION>
Information:
- Adds to the image a trigger instruction to be executed at a later time, when the image is used as the base for another build. The trigger will be executed in the context of the downstream build, as if it had been inserted immediately after the
FROM
instruction in the downstream Dockerfile. - Any build instruction can be registered as a trigger.
- Triggers are inherited by the “child” build only. In other words, they are not inherited by “grand-children” builds.
- The
ONBUILD
instruction may not triggerFROM
,MAINTAINER
, orONBUILD
instructions.
STOPSIGNAL
Usage:
STOPSIGNAL <signal>
The STOPSIGNAL
instruction sets the system call signal that will be sent to the container to exit. This signal can be a valid unsigned number that matches a position in the kernel’s syscall table, for instance 9
, or a signal name in the format SIGNAME, for instance SIGKILL
.
HEALTHCHECK
Usage:
HEALTHCHECK [<options>] CMD <command>
(check container health by running a command inside the container)HEALTHCHECK NONE
(disable any healthcheck inherited from the base image)
Information:
- Tells Docker how to test a container to check that it is still working
- Whenever a health check passes, it becomes
healthy
. After a certain number of consecutive failures, it becomesunhealthy
. - The
<options>
that can appear are... --interval=<duration>
(default: 30s)--timeout=<duration>
(default: 30s)--retries=<number>
(default: 3)- The health check will first run
interval
seconds after the container is started, and then againinterval
seconds after each previous check completes. If a single run of the check takes longer thantimeout
seconds then the check is considered to have failed. It takesretries
consecutive failures of the health check for the container to be consideredunhealthy
. - There can only be one
HEALTHCHECK
instruction in a Dockerfile. If you list more than one then only the lastHEALTHCHECK
will take effect. <command>
can be either a shell command or an exec JSON array.- The command’s exit status indicates the health status of the container.
0
: success - the container is healthy and ready for use1
: unhealthy - the container is not working correctly2
: reserved - do not use this exit code- The first 4096 bytes of stdout and stderr from the
<command>
are stored and can be queried withdocker inspect
. - When the health status of a container changes, a
health_status
event is generated with the new status.
SHELL
Usage:
SHELL ["<executable>", "<param1>", "<param2>"]
Information:
- Allows the default shell used for the shell form of commands to be overridden.
- Each
SHELL
instruction overrides all previousSHELL
instructions, and affects all subsequent instructions. - Allows an alternate shell be used such as
zsh
,csh
,tcsh
,powershell
, and others.
so , this is our first story about Dockerfile , in the next article I will introduce some best practices to write our own Dockerfile .
hope this article will help you .
stay in touch with me in Linkedin : https://www.linkedin.com/in/omid-asadpoor-423370172/