Docker uses a combination of 'Endpoint' and 'Command' values to compose the command it uses to launch images. Exactly which values are used depends on which are set and their format.
The documentation gives all the necessary information, but it is spread across a number of entries.
In this post, I'll try to reduce the bewildering list of possible permutations (see below) to a couple of simple choices.
I ran these tests on Docker version 20.10.12 on Ubuntu, but as far as I know, this behaviour is pretty stable.
Running a Docker Image
When Docker launches images, it starts a single process via a single command.
If I run an Alpine Linux image without any extra arguments, this is what happens:
$ docker run -ti alpine:3.17 / #
Docker has run
/bin/sh thanks to the
CMD instruction in the Alpine Linux Dockerfile.
If we append the
ls command this is what we get:
$ docker run -ti alpine:3.17 ls bin etc lib mnt proc run srv tmp var dev home media opt root sbin sys usr
ls is the "run argument". It has taken precedence over
/bin/sh specified in the Dockerfile.
Knowing exactly what command and arguments get executed depends on a combination of what's in the Dockerfile (and, recursively, the Dockerfiles that it is based on) plus the command line arguments passed to
By the way, we can also confirm the idea that the command Docker executes is the only process that is started:
$ docker run -ti alpine:latest ps afux PID USER TIME COMMAND 1 root 0:00 ps afux
So Docker has run my
ps command as
PID 1 - the only process running in the container.
Dockerfiles can contain the
ENTRYPOINT instruction and the CMD instruction.
ENTRYPOINT should be included per Dockerfile.
If more than one is present, only the last will be used.
Base Image Dockerfiles
If a Dockerfile uses a base image (via
FROM), it inherits that image's
Most popular base images do not set
Here's a list:
An exception to this is Redis, which launches a script called docker-entrypoint.sh, which does some setup and then executes whatever was passed to
So, in the most common case, when we need to override stuff, it's only the
CMD instruction that will already have been set, or, if
ENTRYPOINT is set, there will be little need to change it.
CMD have two formats,
As we will see below, the format that is used changes the rules about how the final command line is composed.
When a simple text is supplied, it is called 'shell' format:
ENTRYPOINT echo Ciao
CMD echo Ciao
JSON Array format
If a well formed JSON array is supplied, it is called the 'exec' form or 'array' form:
ENTRYPOINT ["echo", "Ciao"]
CMD ["echo", "Ciao"]
N.B. Well-formed JSON Array is required - quotes must be
' quotes are used, the value is treated as a shell format.
docker run Options and Arguments
--entrypoint parameter is passed to
docker run, the value overrides any
ENDPOINT instruction that has been specified in a Dockerfile. Note that is also clears any Command that has been supplied.
You can't use the 'JSON Array' format when specifying
--entrypoint, the value you pass is always interpreted as being in 'shell' format.
Passing an empty string to
--entrypoint is a handy way of nullifying any
CMD) that may have been set, in order to fall back to using
docker run arguments exclusively.
If we don't specify arguments, we get an error:
$ docker run -ti --entrypoint="" busybox:latest docker: Error response from daemon: No command specified.
Any arguments passed to
docker run after the image name override any
How the Entrypoint and Command Interact
Based on the above, from now on, I will only refer to the Entrypoint and Command, however they have been indicated.
If an Entrypoint is supplied in shell format, any Command is ignored.
If, on the other hand, there is a JSON format Entrypoint, any JSON format Command is appended.
But, if the Command is in shell format,
/bin/sh -c is prepended to the Command.
I think this last case, of all the combinations available, is the most surprising to the user, and only makes sense in cases where the Entrypoint is a setup script, as in the case of the Redis image above.
When a JSON Entrypoint is supplied, the command line which Docker builds from any Entrypoint and Command is executed directly, not via
As the final command is not run via a shell, this format does not do variable substitution. This is a point to remember - and often causes confusion. A subtle change, from shell format to JSON format, can cause errors when starting a container.
Running commands directly fails if the first array entry is not in the image's
$PATH and is not a full path to an executable.
Running a Shell
When the 'shell' format is used, the executable and arguments obtained from combining any Entrypoint and Command are passed to a command shell, e.g.
Running commands via
sh fails if the first array entry is not in the shell's
$PATH and is not a full path to an executable.
Permutations of Entrypoint and Command
Below is a list of the permutations of Entrypoint and Command, indicating how Docker interprets them.
'JSON' refers to the 'exec'/'array' JSON format.
|Entrypoint type||Command type||Command used?||Outcome|
|-||-||n||Uses base image's `ENTRYPOINT`. If there is none, tries to run '/bin/sh'.|
|-||shell||y||Executes Command via `sh`.|
|-||JSON||y||Executes Command command directly.|
|shell||-||n||Passes Entrypoint command to `sh`.|
|shell||shell||n||Passes Entrypoint command to `sh`, **ignoring** Command.|
|shell||JSON||n||Passes Entrypoint command to `sh`, **ignoring** Command.|
|JSON||-||n||Executes Entrypoint directly.|
|JSON||shell||y||Executes Entrypoint, appending '/bin/sh -c', then Command|
|JSON||JSON||y||Executes Entrypoint directly, appending Command|
What Should I Use?
After trawling through all this, my conclusion is as follows:
in general, avoid using an Entrypoint,
only use an Entrypoint if you need to do setup before running the container,
if necessary, use
CMDin your Dockerfiles to override what is set in your base image, and pass arguments to
docker runif your own
CMDneeds to be overridden,
use 'shell' format for
CMDif you want shell pre-processing (e.g. environment variable substitution), otherwise, use the 'JSON' format,
--entrypoint=""plus a Command to Docker
runif your base image has an
ENTRYPOINTinstruction that you don't want to run.