Docker has almost 4 years now. However some developers, especially newbies, still get confused when looking at the instructions that are available for use in a Dockerfile, because there are a few that may initially appear to be redundant (or, at least, have significant overlap) . RUN, CMD and ENTRYPOINT are a good example of this, and in this post I will explain the difference between CMD, RUN, and ENTRYPOINT on examples.
RUNexecutes the command(s) that you give in a new layer and creates a new image. This is mainly used for installing a new package.
CMDis the default command to be run by the entrypoint. It sets default command and/or parameters, however, we can overwrite those commands or pass in and bypass the default parameters from the command line when docker runs
ENTRYPOINTis the program to run the given command. It is used when yo want to run a container as an executable.
1- Layering of Docker images
When Docker runs a container, it runs an image inside it. This image is usually built by executing a series of Docker instructions, which add layers on top of existing image or OS distribution. OS distribution is the initial image and every package is added as a new layer on top of that.
Let us consider the following Dockerfile to build a simple Ubuntu image with an Apache installation:
FROM ubuntu RUN apt-get update RUN apt-get install -y apache2 RUN touch /opt/aboullaite.txt
If we build the image by calling
docker build -t med/aboullaite . we get an image called
aboullaite, belonging to a repository called
med. We can see the history of your image by calling
docker history med/aboullaite:
$ docker history med/aboullaite IMAGE CREATED CREATED BY SIZE COMMENT e1198abcf6ac 8 seconds ago /bin/sh -c touch /opt/aboullaite.txt 0 B ea693c852138 10 seconds ago /bin/sh -c apt-get install -y apache2 99.1 MB c4448790b3b2 About a minute ago /bin/sh -c apt-get update 39.49 MB 2fa927b5cdd3 6 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
The final image
aboullaite consists of six intermediate images as we can see. The first three layers belongs to the Ubuntu base image and the rest is ours: one layer for every build instruction.
2- Shell vs. Exec
All three instructions
ENTRYPOINT support two different forms: the
shell form and the
When using the shell form, the specified binary is executed with an invocation of the shell using /bin/sh -c.
< instruction > < command >
For example let's consider the following Dockerfile:
FROM ubuntu:trusty CMD ping localhost
You can see this clearly if you run a container and then look at the docker ps output:
$ docker run -d med/aboullaite2 98aa7c371139d81d376abdc9ce01ea53cfac1f87506d9e758fee14696a0fa621 $ docker ps -l CONTAINER ID IMAGE COMMAND CREATED 98aa7c371139 med/aboullaite2 "/bin/sh -c 'ping loc" 5 seconds ago
Here we've run the
aboullaite2 image and you can see that the command which was executed was /bin/sh -c 'ping localhost'.
You may 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:
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 run -d med/aboullaite2 fc9e3c759ea8f9793c1be8695d43e04050c9f14a4b0c723c95f2b76ee29c7628 $ docker ps -l CONTAINER ID IMAGE COMMAND CREATED fc9e3c759ea8 med/aboullaite2 "/bin/ping localhost" 2 seconds ago
Now /bin/ping is being run directly without the intervening shell process.
As mentioned above, the RUN command is mainly used to install a new package on top of the main OS distribution. When you use the RUN command, it will execute the instruction and will create a new layer.
RUN command can be used in two forms:
Shell form RUN <command> Exec form RUN ["executable", "param1", "param2"]
CMD instruction allows you to set a default command and default parameters which will be executed when docker is run. But these commands and parameters can be overwritten by passing the values over the command line.
CMD can be specified in three forms:
exec form, preferred way CMD ["executable","param1","param2"] (sets additional default parameters for ENTRYPOINT in exec form) CMD ["param1","param2"] Shell form CMD command param1 param2
Again, the first and third forms should look familar to you as they were already covered above. The second one is used together with ENTRYPOINT instruction in exec form. It sets default parameters that will be added after ENTRYPOINT parameters if container runs without command line arguments.
Let's have a look how
CMD instruction works. The following snippet in Dockerfile
CMD echo "Hello world"
when container runs as docker
run -it <image> will produce output
but when container runs with a command, e.g., docker
run -it <image> /bin/bash,
CMD is ignored and bash interpreter runs instead:
ENTRYPOINT instruction should be used when you need your container to be run as an executable.
I might look similar to
CMD, but in fact, it is different and should be used in a different context
The difference is
ENTRYPOINT is that unlike
CMD, the command and parameters are not ignored when Docker container runs with command line parameters.
ENTRYPOINT instructions too can be written in two forms:
Executable form preferred way ENTRYPOINT ["executable", "param1", "param2"] Shell form ENTRYPOINT command param1 param2
Exec form of
ENTRYPOINTallows you to set commands and parameters and then use either form of
CMDto set additional parameters that are more likely to be changed.
ENTRYPOINTarguments are always used while
CMDones can be overwritten by command line arguments provided when Docker container runs. For example, the following snippet in Dockerfile
ENTRYPOINT ["/bin/echo", "Hello"] CMD ["world"]
when container runs as docker
run -it <image> will produce output
but when container runs as
docker run -it <image> Manu will result in
- Shell form
Shell form of
ENTRYPOINT ignores any
CMD or docker run command line arguments.
If you want your image to actually do anything when it is run, you should definitely configure some sort of
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.
RUN instructions to build your image by adding layers on top of the initial image.
CMD when building executable Docker image and you need a command always to be executed, and use
CMD if you need to provide extra default arguments that could be overwritten from the command line when docker container runs.
CMD if you need to provide a default command and/or arguments that can be overwritten from the command line when docker container runs.