Docker Tip #63: Difference between an Array and String Based CMD
The official terms for this are exec form and shell form commands. Both do nearly the same thing, but there's an important difference.
Using [] is considered “exec form” and the plain string command is considered “shell form”.
Exec Form
Runs your CMD’s binary as is, along with any arguments you optionally pass in.
Example CMD:
CMD ["gunicorn", "-c", "python:config.gunicorn", "hello.app:create_app()"]
Shell Form
Runs your CMD’s binary through a shell which has the added benefit of using any
shell functionality you want (such as using pipes and &&
, etc.).
Example CMD:
CMD gunicorn -c "python:config.gunicorn" "hello.app:create_app()"
Which One Should You Use?
Shell form sounds better in theory, but it will prevent Unix signals from
reaching your container correctly, such as SIGTERM
. It also means the shell
process ends up being PID 1 instead of whatever binary you’re running in your
CMD.
Showing what PID 1 is using both methods:
# The output of `ps` when you use exec form:
PID USER TIME COMMAND
1 root 0:00 {gunicorn} /usr/local/bin/python /usr/local/bin/gunicorn
15 root 0:02 {gunicorn} /usr/local/bin/python /usr/local/bin/gunicorn
188 root 0:00 ps
# The output of `ps` when you use shell form:
PID USER TIME COMMAND
1 root 0:00 /bin/sh -c gunicorn -c python:config.gunicorn hello.app:create_app()
6 root 0:00 {gunicorn} /usr/local/bin/python /usr/local/bin/gunicorn
16 root 0:00 {gunicorn} /usr/local/bin/python /usr/local/bin/gunicorn
29 root 0:00 ps
Docker (and I) both recommend that you use exec form whenever possible, which
really is most of the time. If you need to do complicated shell scripting when
a container starts, you should probably use an ENTRYPOINT
script.
But what about Docker Compose:
If you’re curious, Docker Compose appears to convert shell form into exec form
by splitting your command:
by spaces. I didn’t look at the source code to see
if this is the full story, but I did notice that PID 1 is still gunicorn when
using the custom command listed below:
command: >
gunicorn --reload -c "python:config.gunicorn" "hello.app:create_app()"
The official Docker Compose documentation says it supports the [] syntax too if you want to be more explicit. If anyone knows the exact story behind this, please leave a comment below and I’ll update the post.
Here’s a few more examples of exec form in the context of web apps written in a few different frameworks.