ENTRYPOINT defines the main executable that Docker will use. If entrypoint is not given, then CMD
will be used as a command that is executed.
CMD defines the default arguments for the ENTRYPOINT
. Note that CMD
argument can be overridden when running the container with alternative arguments.
We can think them as pair that are concatenated ENTRYPOINT + CMD
.
For example, let's build container that pings three times a given network host. If no host is given, then it should ping localhost as a default.
FROM alpine:latest
ENTRYPOINT ["/bin/ping", "-c", "3"]
CMD ["localhost"]
(As a base image, we use alpine:latest
which is a minimalistic Linux image)
So first we replace Docker's default entrypoint /bin/sh -c
, as we want to use /bin/ping
.
Then we will set default parameter for our entrypoint to be localhost CMD ["localhost"]
.
Now we can build our Dockerfile to an image using tag pinger:latest
:
docker build -t pinger:latest .
Then we can run our container by taking advantage of the default CMD
argument. As we can see, our pinger is using the localhost as specified in CMD
❯ docker run pinger:latest
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.042 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.093 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.154 ms
--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.042/0.096/0.154 ms
To test overriding the default CMD
argument, we can give argument for docker run. For example we can ping google.fi with following command:
❯ docker run pinger:latest google.fi
PING google.fi (172.217.21.131): 56 data bytes
64 bytes from 172.217.21.131: seq=0 ttl=37 time=70.218 ms
64 bytes from 172.217.21.131: seq=1 ttl=37 time=69.093 ms
64 bytes from 172.217.21.131: seq=2 ttl=37 time=73.889 ms
--- google.fi ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 69.093/71.066/73.889 ms
Seems to be working as expected.
Now comes the interesting part. Both CMD and ENTRYPOINT supports EXEC
and SHELL
modes and there's a serious caveat to mess things up with them.
In EXEC mode, command itself is executed. In Dockerfiles, EXEC mode uses []-parenthesis syntax. For example ENTRYPOINT ["/bin/ping","-c","3"]
.
In SHELL mode, command is wrapped with /bin/bash -c
. It's useful when you need to evaluate for example environment variables. Docker SHELL
mode is used when not wrapping the command with []-parenthesis. For example ENTRYPOINT /bin/ping -c 3
.
So it's not the same thing to write ENTRYPOINT and CMD values with or without []-parenthesis.
Let see an example for the Dockerfile and their corresponding output commands:
ENTRYPOINT /bin/ping -c 3
CMD localhost
=> /bin/sh -c '/bin/ping -c 3' /bin/sh -c localhost
ENTRYPOINT ["/bin/ping","-c","3"]
CMD localhost
=> /bin/ping -c 3 /bin/sh -c localhost
ENTRYPOINT /bin/ping -c 3
CMD ["localhost"]
=> /bin/sh -c '/bin/ping -c 3' localhost
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
=> /bin/ping -c 3 localhost
For our case, only the last example will work. It will actually produce the output that combines the command correctly with the arguments.