
The RUN, ENTRYPOINT, and CMD, instructions all have two different forms they can be written in, and those forms change how each of those instructions behaves.
The two forms
Shell form:
Commands are written without
[]brackets and are run by the container's shell, such as/bin/sh -c. Example:dockerfile1FROM alpine:latest 2 3# /bin/sh -c 'echo $HOME' 4RUN echo $HOME 5 6# /bin/sh -c 'echo $PATH' 7CMD echo $PATHDepending on the shell, commands will execute as child processes of the shell, which has some potentially negative consequences at the cost of some features, described below.
Exec form:
Commands are written with
[]brackets and are run directly, not through a shell. Example:dockerfile1FROM alpine:latest 2 3RUN ["pwd"] 4 5CMD ["sleep", "1s"]This removes a potentially extra process in the process tree, which may be desirable.
Recommended forms
These are the recommended forms to use for each instruction:
RUN: shell form, because of the shell features described belowENTRYPOINT: exec form, because of the signal trapping described belowCMD: exec form, because of the signal trapping described below
The general idea is to use the exec form unless you need shell features - and if you need shell features in the ENTRYPOINT or CMD, then consider writing a shell script and executing it with the exec form.
Use cases
There are real use cases where you may not want to follow the above recommendations, so let's explore the main consequences of the two forms.
Variable substitution
In the shell form, commands will inherit environment variables from the shell, such as $HOME and $PATH:
1FROM alpine:latest
2
3# Shell: echoes "/root" as set by the shell
4RUN echo $HOME
5
6# Exec: echoes "$HOME" because nothing is set
7RUN ["echo", "$HOME"]
However, both forms behave the same when it comes to environment variables set by the ENV instruction:
1FROM alpine:latest
2
3ENV VERSION=1.0.0
4
5# Shell: echoes "1.0.0" because Docker does the substitution
6RUN echo $VERSION
7
8# Exec: echoes "1.0.0" because Docker does the substitution
9RUN ["echo", "$VERSION"]
Shell features
The main thing you lose with the exec form is all the useful shell features: sub commands, piping output, chaining commands, I/O redirection, and more. These kinds of commands are only possible with the shell form:
1FROM ubuntu:latest
2
3# Shell: run a speed test
4RUN apt-get update \
5 && apt-get install -y wget \
6 && wget -O /dev/null http://speedtest.wdc01.softlayer.com/downloads/test10.zip \
7 && rm -rf /var/lib/apt/lists/*
8
9# Shell: output the default shell location
10CMD which $(echo $0)
Most Dockerfiles are written with the shell form for RUN for the niceties as well as layer reduction.

Reducing Docker Layers
Jun 29, 2020 · 6 min read
Reducing the size of your Docker images is important for a number of reasons, and while there are newer tools such as multi-stage builds, reducing the number of layers in your image may help.
Signal trapping & forwarding
Most shells do not forward process signals to child processes, which means the SIGINT generated by pressing CTRL-C may not stop a child process:
1# Note: Alpine's `/bin/sh` is really BusyBox `ash`, and when
2# `/bin/sh -c` is run it replace itself with the command rather
3# than spawning a new process, unlike Ubuntu's `bash`, meaning
4# Alpine doesn't exhibit the forwarding problem
5FROM ubuntu:latest
6
7# Shell: `bash` doesn't forward CTRL-C SIGINT to `top`
8ENTRYPOINT top -b
9
10# Exec: `top` traps CTRL-C SIGINT and stops
11ENTRYPOINT ["top", "-b"]
This is the main reason to use the exec form for both ENTRYPOINT and SHELL.
CMD as ENTRYPOINT parameters
There's a special version of the exec form for CMD where if the first item in the array isn't a command then all items will be used as parameters for the ENTRYPOINT:
1FROM alpine:latest
2
3# Exec: default container command
4ENTRYPOINT ["sleep"]
5
6# Exec: not a command, so it becomes a parameter
7CMD ["1s"]
This is not possible with the shell form.
Conclusion
In general, use the shell form for RUN, and the exec form for everything else.




