What is the difference between CMD and ENTRYPOINT in a Dockerfile?
In Dockerfiles there are two commands that look similar to me: CMD and ENTRYPOINT. But I guess that there is a (subtle?) difference between them - otherwise it would not make any sense to have two commands for the very same thing.
The documentation states for CMD
The main purpose of a CMD is to provide defaults for an executing container.
and for ENTRYPOINT:
An ENTRYPOINT helps you to configure a container that you can run as an executable.
So, what's the difference between those two commands?
Docker has a default entrypoint which is /bin/sh -c but does not have a default command.
When you run docker like this: docker run -i -t ubuntu bash the entrypoint is the default /bin/sh -c, the image is ubuntu and the command is bash.
The command is run via the entrypoint. i.e., the actual thing that gets executed is /bin/sh -c bash. This allowed Docker to implement RUN quickly by relying on the shell's parser.
Later on, people asked to be able to customize this, so ENTRYPOINT and --entrypoint were introduced.
Everything after ubuntu in the example above is the command and is passed to the entrypoint. When using the CMD instruction, it is exactly as if you were doing docker run -i -t ubuntu <cmd>. <cmd> will be the parameter of the entrypoint.
You will also get the same result if you instead type this command docker run -i -t ubuntu. You will still start a bash shell in the container because of the ubuntu Dockerfile specified a default CMD: CMD ["bash"]
As everything is passed to the entrypoint, you can have a very nice behavior from your images. @Jiri example is good, it shows how to use an image as a "binary". When using ["/bin/cat"] as entrypoint and then doing docker run img /etc/passwd, you get it, /etc/passwd is the command and is passed to the entrypoint so the end result execution is simply /bin/cat /etc/passwd.
Another example would be to have any cli as entrypoint. For instance, if you have a redis image, instead of running docker run redisimg redis -H something -u toto get key, you can simply have ENTRYPOINT ["redis", "-H", "something", "-u", "toto"] and then run like this for the same result: docker run redisimg get key.
The ENTRYPOINT specifies a command that will always be executed when the container starts.
The CMD specifies arguments that will be fed to the ENTRYPOINT.
If you want to make an image dedicated to a specific command you will use ENTRYPOINT ["/path/dedicated_command"]
Otherwise, if you want to make an image for general purpose, you can leave ENTRYPOINT unspecified and use CMD ["/path/dedicated_command"] as you will be able to override the setting by supplying arguments to docker run.
For example, if your Dockerfile is:
FROM debian:wheezy ENTRYPOINT ["/bin/ping"] CMD ["localhost"]
Running the image without any argument will ping the localhost:
$ docker run -it test PING localhost (127.0.0.1): 48 data bytes 56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.096 ms 56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.088 ms 56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.088 ms ^C--- localhost ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.088/0.091/0.096/0.000 ms
Now, running the image with an argument will ping the argument:
$ docker run -it test google.com PING google.com (22.214.171.124): 48 data bytes 56 bytes from 126.96.36.199: icmp_seq=0 ttl=55 time=32.583 ms 56 bytes from 188.8.131.52: icmp_seq=2 ttl=55 time=30.327 ms 56 bytes from 184.108.40.206: icmp_seq=4 ttl=55 time=46.379 ms ^C--- google.com ping statistics --- 5 packets transmitted, 3 packets received, 40% packet loss round-trip min/avg/max/stddev = 30.327/36.430/46.379/7.095 ms
For comparison, if your Dockerfile is:
FROM debian:wheezy CMD ["/bin/ping", "localhost"]
Running the image without any argument will ping the localhost:
$ docker run -it test PING localhost (127.0.0.1): 48 data bytes 56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.076 ms 56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.087 ms 56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.090 ms ^C--- localhost ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.076/0.084/0.090/0.000 ms
But running the image with an argument will run the argument:
docker run -it test bash root@e8bb7249b843:/#
See this article from Brian DeHamer for even more details: https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
According to docker docs,
Both CMD and ENTRYPOINT instructions define what command gets executed when running a container. There are few rules that describe their co-operation.
- Dockerfile should specify at least one of CMD or ENTRYPOINT commands.
- ENTRYPOINT should be defined when using the container as an executable.
- CMD should be used as a way of defining default arguments for an ENTRYPOINT command or for executing an ad-hoc command in a container.
- CMD will be overridden when running the container with alternative arguments.
The tables below shows what command is executed for different ENTRYPOINT / CMD combinations:
-- No ENTRYPOINT
╔════════════════════════════╦═════════════════════════════╗ ║ No CMD ║ error, not allowed ║ ╟────────────────────────────╫─────────────────────────────╢ ║ CMD [“exec_cmd”, “p1_cmd”] ║ exec_cmd p1_cmd ║ ╟────────────────────────────╫─────────────────────────────╢ ║ CMD [“p1_cmd”, “p2_cmd”] ║ p1_cmd p2_cmd ║ ╟────────────────────────────╫─────────────────────────────╢ ║ CMD exec_cmd p1_cmd ║ /bin/sh -c exec_cmd p1_cmd ║ ╚════════════════════════════╩═════════════════════════════╝
-- ENTRYPOINT exec_entry p1_entry
╔════════════════════════════╦═══════════════════════════════════════════════════════════╗ ║ No CMD ║ /bin/sh -c exec_entry p1_entry ║ ╟────────────────────────────╫───────────────────────────────────────────────────────────╢ ║ CMD [“exec_cmd”, “p1_cmd”] ║ /bin/sh -c exec_entry p1_entry exec_cmd p1_cmd ║ ╟────────────────────────────╫───────────────────────────────────────────────────────────╢ ║ CMD [“p1_cmd”, “p2_cmd”] ║ /bin/sh -c exec_entry p1_entry p1_cmd p2_cmd ║ ╟────────────────────────────╫───────────────────────────────────────────────────────────╢ ║ CMD exec_cmd p1_cmd ║ /bin/sh -c exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd ║ ╚════════════════════════════╩═══════════════════════════════════════════════════════════╝
-- ENTRYPOINT [“exec_entry”, “p1_entry”]
╔════════════════════════════╦═════════════════════════════════════════════════╗ ║ No CMD ║ exec_entry p1_entry ║ ╟────────────────────────────╫─────────────────────────────────────────────────╢ ║ CMD [“exec_cmd”, “p1_cmd”] ║ exec_entry p1_entry exec_cmd p1_cmd ║ ╟────────────────────────────╫─────────────────────────────────────────────────╢ ║ CMD [“p1_cmd”, “p2_cmd”] ║ exec_entry p1_entry p1_cmd p2_cmd ║ ╟────────────────────────────╫─────────────────────────────────────────────────╢ ║ CMD exec_cmd p1_cmd ║ exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd ║ ╚════════════════════════════╩═════════════════════════════════════════════════╝
Yes, that is a good question. I don't understand it fully yet, but:
I understand that ENTRYPOINT is the binary that is being executed. You can overide entrypoint by --entrypoint="".
docker run -t -i --entrypoint="/bin/bash" ubuntu
CMD is the default argument to container. Without entrypoint, default argument is command that is executed. With entrypoint, cmd is passed to entrypoint as argument. You can emulate a command with entrypoint.
# no entrypoint docker run ubuntu /bin/cat /etc/passwd # with entry point, emulating cat command docker run --entrypoint="/bin/cat" ubuntu /etc/passwd
So, main advantage is that with entrypoint you can pass arguments (cmd) to your container. To accomplish this, you need to use both:
# Dockerfile FROM ubuntu ENTRYPOINT ["/bin/cat"]
docker build -t=cat .
then you can use:
docker run cat /etc/passwd # ^^^^^^^^^^^ # CMD # ^^^ # image (tag)- using the default ENTRYPOINT
Difference between CMD and ENTRYPOINT by intuition:
- ENTRYPOINT: command to run when container starts.
- CMD: command to run when container starts or arguments to ENTRYPOINT if specified.
Yes, it's mixing up.
You can override any of them when running docker run.
Difference between CMD and ENTRYPOINT by example:
docker run -it --rm yourcontainer /bin/bash <-- /bin/bash overrides CMD <-- /bin/bash does not override ENTRYPOINT docker run -it --rm --entrypoint ls yourcontainer <-- overrides ENTRYPOINT with ls docker run -it --rm --entrypoint ls yourcontainer -la <-- overrides ENTRYPOINT with ls and overrides CMD with -la
More on difference between CMD and ENTRYPOINT:
Argument to docker run such as /bin/bash overrides any CMD command we wrote in Dockerfile.
ENTRYPOINT cannot be overriden at run time with normal commands such as docker run [args]. The args at the end of docker run [args] are provided as arguments to ENTRYPOINT. In this way we can create a container which is like a normal binary such as ls.
So CMD can act as default parameters to ENTRYPOINT and then we can override the CMD args from [args].
ENTRYPOINT can be overriden with --entrypoint.
In a nutshell:
- CMD sets default command and/or parameters, which can be overwritten from command line when docker container runs.
- ENTRYPOINT command and parameters will not be overwritten from command line. Instead, all command line arguments will be added after ENTRYPOINT parameters.
If you need more details or would like to see difference on example, there is a blog post that comprehensively compare CMD and ENTRYPOINT with lots of examples - http://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/
The accepted answer is fabulous in explaining the history. I find this table explain it very well from official doc on 'how CMD and ENTRYPOINT interact':
Comments on EntryPoint function in code
// ENTRYPOINT /usr/sbin/nginx.
// Set the entrypoint (which defaults to sh -c) to /usr/sbin/nginx.
// Will accept the CMD as the arguments to /usr/sbin/nginx.
Another reference from documents
You can use the exec form of ENTRYPOINT to set fairly stable default commands and arguments and then use CMD to set additional defaults that are more likely to be changed.
FROM ubuntu:14.04.3 ENTRYPOINT ["/bin/ping"] CMD ["localhost", "-c", "2"]
Build: sudo docker build -t ent_cmd .
CMD arguments are easy to override. NO argument (sudo docker -it ent_cmd) : ping localhost argument (sudo docker run -it ent_cmd google.com) : ping google.com
To override EntryPoint argument, you need to supply entrypoint sudo docker run -it --entrypoint="/bin/bash" ent_cmdd
p.s: In presence of EntryPoint, CMD will hold arguments to fed to EntryPoint. In absense of EntryPoint, CMD will be the command which will be run.
- CMD ["executable","param1","param2"]: ["executable","param1","param2"] is the first process.
- CMD command param1 param2: /bin/sh -c CMD command param1 param2 is the first process. CMD command param1 param2 is forked from the first process.
- CMD ["param1","param2"]: This form is used to provide default arguments for ENTRYPOINT.
ENTRYPOINT (The following list does not consider the case where CMD and ENTRYPOINT are used together):
- ENTRYPOINT ["executable", "param1", "param2"]: ["executable", "param1", "param2"] is the first process.
- ENTRYPOINT command param1 param2: /bin/sh -c command param1 param2 is the first process. command param1 param2 is forked from the first process.
As creack said, CMD was developed first. Then ENTRYPOINT was developed for more customization. Since they are not designed together, there are some functionality overlaps between CMD and ENTRYPOINT, which often confuse people.
CMD command mentioned inside Dockerfile file can be overridden via docker run command while ENTRYPOINT can not be.
Most people explain it perfectly here, so I won't repeat all the answers. But to get a good feeling I would suggest testing it yourself by looking at the processes in the container.
Create a tiny Dockerfile of the form:
FROM ubuntu:latest CMD /bin/bash
Build it, run it in with docker run -it theimage and run ps -eo ppid,pid,args in the container. Compare this output to the output you receive from ps when using:
- docker run -it theimage bash
- Rebuilding the image but with ENTRYPOINT /bin/bash and running it in both ways
- Using CMD ["/bin/bash"]
This way you will easily see the differences between all possible methods for yourself.