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 msTo 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 msSeems 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 localhostFor our case, only the last example will work. It will actually produce the output that combines the command correctly with the arguments.