Docker CMD and ENTRYPOINT - blitterated/docker-dev-env GitHub Wiki

Quick experiments to learn how the Dockerfile's CMD and ENTRYPOINT work alone and together.

CMD

CMD is overridable, and is intended to provide a default executable and/or default arguments for a container.

CMD forms

From the Docker documentation for CMD:

The CMD instruction has three forms:

  • CMD ["executable","param1","param2"] (exec form, this is the preferred form)
  • CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
  • CMD command param1 param2 (shell form)

The first and second forms are Exec Form and are both parsed as JSON. The third form is Shell Form. Its command gets passed to /bin/sh -c by default.

I'll cover the first and third forms, CMD exec and CMD shell, here. The second form, ENTRYPOINT default params, I'll cover in its own section below, "ENTRYPOINT and CMD together".

CMD Exec Form

First, build a default Ubuntu image that, by default, runs bash with the exec form.

cat <<"EOF" > Dockerfile
FROM ubuntu

CMD ["/bin/bash"]
EOF

Build the image.

docker build -t cmd_exec .

Run the image.

docker run -it --rm cmd_exec

Check that the shell is bash.

ps p $$

Notice in the output that bash is PID 1. This will be different for shell form.

  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /bin/bash

Exit out of the container.

Start a new one but specify dash instead.

docker run -it --rm cmd_exec /usr/bin/dash

Check that the shell is now dash.

ps p $$

It's still PID 1.

  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /usr/bin/dash

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi cmd_exec
rm ./Dockerfile

CMD Shell Form

Now try it with shell form

cat <<"EOF" > Dockerfile
FROM ubuntu

CMD /bin/bash
EOF

Build the image.

docker build -t cmd_shell .

Run the image.

docker run -it --rm cmd_shell

Check that the shell is bash.

ps p $$

Notice in the output that bash is NOT PID 1.

  PID TTY      STAT   TIME COMMAND
    9 pts/0    S      0:00 /bin/bash

Take a look at all the running processes.

ps a

We can see in the out put that shell form CMD wraps it's executable in /bin/sh -c.

  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /bin/sh -c /bin/bash
    9 pts/0    S      0:00 /bin/bash
   16 pts/0    R+     0:00 ps a

Exit out of the container.

Start a new one but specify dash instead.

docker run -it --rm cmd_shell /usr/bin/dash

Check that the shell is now dash and see what its PID is.

ps a

Interestingly, it's PID 1 when CMD is overridden

  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /usr/bin/dash
    8 pts/0    R+     0:00 ps a

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi cmd_shell
rm ./Dockerfile

CMD Shell Form with exec usage

Finally, try it with shell form but using exec. This isn't the combination of ENTRYPOINT and CMD. Rather it's specifying exec as the actual executable to run. It just so happens that the first argument to exec is another executable. Here exec will lift the second executable into process that's running /bin/sh -c. It just so happens that that process is PID 1. It's an interesting little hack on Docker's CMD shell form.

cat <<"EOF" > Dockerfile
FROM ubuntu

CMD exec /bin/bash
EOF

Build the image.

docker build -t cmd_shell_exec .

Run the image.

docker run -it --rm cmd_shell_exec

Check that the shell is bash.

ps p $$

Notice in the output that bash is PID 1. With exec we force bash to take over the process running sh used by CMD shell form to wrap executables.

  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /bin/bash

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi cmd_shell_exec
rm ./Dockerfile

ENTRYPOINT

ENTRYPOINT's executable, unlike CMD, is not overridable. Its intended use is for creating containers that will be run as executables.

Anything extra passed into docker run will be interpreted as extra arguments to the executable, and will be appended to any arguments given in the ENTRYPOINT forms.

ENTRYPOINT forms

From the Docker documentation for ENTRYPOINT:

ENTRYPOINT has two forms:

The exec form, which is the preferred form:

ENTRYPOINT ["executable", "param1", "param2"]

The shell form:

ENTRYPOINT command param1 param2

ENTRYPOINT Exec Form

Build an Ubuntu image that runs bash with the exec form. It specifies the -s option to bash that will allow arguments to be loaded into positional variables.

cat <<"EOF" > Dockerfile
FROM ubuntu

ENTRYPOINT ["/bin/bash", "-s", "foo", "bar"]
EOF

Build the image.

docker build -t entrypoint_exec .

Run the image.

docker run -it --rm entrypoint_exec

Check that bash is PID 1.

ps a
  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /bin/bash -s
    9 pts/0    R+     0:00 ps a

See if there's any values passed into positional parameters with bash -s.

echo $1 $2

You should see foo bar as a result

Exit out of the container, and start a new one with extra arguments.

docker run -it --rm entrypoint_exec ping pong

Check the positional parameters to see that the new arguments were appended to the ones specifiend in the Dockerfile's ENTRYPOINT.

echo $1 $2 $3 $4
foo bar ping pong

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi entrypoint_exec
rm ./Dockerfile

ENTRYPOINT Shell Form

Build an Ubuntu image that runs bash with the shell form. It also specifies the -s option to bash that allows arguments to be loaded into positional variables.

cat <<"EOF" > Dockerfile
FROM ubuntu

ENTRYPOINT /bin/bash -s foo bar
EOF

Build the image.

docker build -t entrypoint_shell .

Run the image.

docker run -it --rm entrypoint_shell

Check what PID bash is running as.

ps a

Here we can see that bash is running as a child of /bin/sh -c just like with CMD's shell format.

  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /bin/sh -c /bin/bash -s foo bar
    8 pts/0    S      0:00 /bin/bash -s foo bar
   11 pts/0    R+     0:00 ps a

We'll work around this in the "ENTRYPOINT Shell Form with exec usage" section below.

See if there's any values passed into positional parameters with bash -s.

echo $1 $2

You should see foo bar as a result.

Exit out of the container, and start a new one with extra arguments.

docker run -it --rm entrypoint_shell ping pong

Check the positional parameters to see that the new arguments were appended to the ones specifiend in the Dockerfile's ENTRYPOINT.

echo $1 $2 $3 $4

And here we don't get the additional parameters appended.

foo bar

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi entrypoint_shell
rm ./Dockerfile

ENTRYPOINT Shell Form with exec usage

Build an Ubuntu image that runs bash with the shell form. It also uses exec to have bash take over /bin/sh -c's process (PID).

It also specifies the -s option to bash that allows arguments to be loaded into positional variables.

cat <<"EOF" > Dockerfile
FROM ubuntu

ENTRYPOINT exec /bin/bash -s foo bar
EOF

Build the image.

docker build -t entrypoint_shell_exec .

Run the image.

docker run -it --rm entrypoint_shell_exec

Check what PID bash is running as.

ps a

And we can see that its PID is 1. We can also see that -s foo bar were all passed in as arguments, so no need to test that again.

  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 /bin/bash -s foo bar
    9 pts/0    R+     0:00 ps a

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi entrypoint_shell_exec
rm ./Dockerfile

ENTRYPOINT and CMD together

When using ENTRYPOINT and CMD together, CMD acts as a way to supply default arguments to the executable and initial options/arguments supplied by ENTRYPOINT.

This only appears to work with Exec Form.

Exec Form

Build an Ubuntu image that runs bash with the ENTRYPOINT exec form. Add arguments with the CMD exec form.

cat <<"EOF" > Dockerfile
FROM ubuntu

ENTRYPOINT ["/bin/bash", "-s"]
CMD ["foo", "bar"]
EOF

Build the image.

docker build -t entrypoint_cmd_exec .

Run the image.

docker run -it --rm entrypoint_cmd_exec

See if there's any values passed into positional parameters via CMD's exec form.

echo $1 $2

You should see foo bar as a result.

Exit out of the container, and start a new one but with different arguments.

docker run -it --rm entrypoint_cmd_exec ping pong

See what values were passed into the first two positional parameters.

echo $1 $2
ping pong

So we see that the arguments supplied to docker run overrode the default ones supplied by the Dockerfile's CMD entry.

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi entrypoint_cmd_exec
rm ./Dockerfile

Shell Form

Build an Ubuntu image that runs bash with the ENTRYPOINT shell form. Add arguments with the CMD shell form.

cat <<"EOF" > Dockerfile
FROM ubuntu

ENTRYPOINT /bin/bash -s
CMD foo bar
EOF

Build the image.

docker build -t entrypoint_shell_exec .

Run the image.

docker run -it --rm entrypoint_shell_exec

See if there's any values passed into positional parameters via CMD's exec form.

echo $1 $2

Well, there's nothing passed in.

Exit out of the container, and start a new one but with supply arguments to docker run.

docker run -it --rm entrypoint_shell_exec ping pong

See what values were passed into the first two positional parameters.

Still nothing.

Exit out of the container, and then delete the image and the Dockerfile.

docker rmi entrypoint_shell_exec
rm ./Dockerfile
⚠️ **GitHub.com Fallback** ⚠️