I’d like to start with a disclaimer: I’m a newbie in the Docker universe and the goal of this article is to help those at the same level that may have the same difficulty I had understanding these two concepts, which at first may look the same.
Once we dig a little deeper, though, we understand the difference between one form and the other, making it easier for us to choose the best one to improve our containerized app.
To make it easier to explain the differences and similarities, I’ll use a simple use case to compare both and at the end we’ll be more informed about the advantages each one offers,
The SHELL form runs the command as a child process (on a shell).
command param1 param2
The EXEC form runs the executable on the main process (the one that has PID 1).
If you’re not quite sure how it looks, check out the images below to see how our script runs in the main process or in a child process (depending on the way we choose to launch it).
As you can see below, the SHELL form executes our script as a child on a shell (by
/bin/sh -c /loop.sh). The actual script is running on the process with PID 8.
While the EXEC form ran our script on the main process (
bash /loop.sh), the one with PID 1.
Why does the Process PID matter?
The most important thing about using the process with PID 1 is that when we stop our container, we will also stop the process that is running in it.
However, if the main process has a child process, executing “docker stop” will stop the child process, and not the actual container.
If you are not sure about this behavior, then we exemplify it.
So, let’s suppose we have this simple script that stops when it receives the “stop signal” (
When we run this script using the SHELL form inside our container and we try to stop the container, we’ll see that the container stops with a 137 code.
Why? Because we were not able to gracefully shut down the container with one “stop signal”. Instead, docker had to send a second signal to kill (
SIGKILL) our container because it didn’t stop with the first signal 😢.
What we stopped when we sent out the first signal was the child process, but not the main one. That’s why after a few seconds elapsed, docker sent another signal (the kill signal) to really stop the container (main process).
On the other hand, when we are running the EXEC form, we gracefully shut down our container. That’s why we get a 0 code back.
The good thing about the EXEC form is that Docker sends a single signal to stop our container while in the SHELL form, Docker needs to send two signals to stop it.
Which one should I use?
Short answer: Docker documentation suggests the EXEC form because it is considered the preferred one.
Keep in mind that each form has its own advantages and disadvantages, and we should explore them according to our needs.
To wrap things up, I’d like to highlight other differences and virtues of each form:
💡 The SHELL form, of course, gives you shell features like sub commands, I/O redirection, environment variables substitution, etc. that the EXEC form doesn’t offer.
💡 We can use the EXEC form combined with the ENTRYPOINT to set default parameters that will be added after the ENTRYPOINT, if we run the container without command line arguments. While the SHELL form of ENTRYPOINT ignores any CMD or docker run command line arguments.
Send params? What? Here it’s a simple example:
ENTRYPOINT ["/bin/echo", "Hello"]
When we run without params:
$ docker run <image>
When we run with params:
$ docker run <image> Pipefy
Written by Tania Zúñiga