Docker Tip #63: Difference between an Array and String Based CMD
![blog/cards/docker-tips-and-tricks.jpg](/assets/blog/cards/docker-tips-and-tricks-92efa4f3cccffb29a7369417a3f9e884a77a918d152a0d2c1636c064a4dd2169.jpg)
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.