Docker difference between run, cmd, entrypoint commands

If you have built a docker image, you would be familiar with the commands RUN, CMD, ENTRYPOINT. While some of you know what these means, where to use those and when to use those, there are some who might not know the exact difference between those commands. These commands are quite similar and can cause confusion to the newbies. So I thought of scribbling it down to give a clear idea of what these commands are and when to use what.

TL;DR: In short

  • RUN executes the command(s) that you give in a new layer and creates a new image. This is mainly used for installing a new package.

  • CMD sets default command and/or parameters, however, we can overwrite those commands or pass in and bypass the default parameters from the command line when docker runs

  • ENTRYPOINT is used when yo want to run a container as an executable.

If you are interested in the longer version, describing the details in detail, please read on. Before I explain the commands in detail, it would be good to mention a few basics about docker.

Docker images and layers

When Docker runs a container, it runs an image inside it. This image is usually built by executing a series of Docker instructions, which add layers on top of existing image or OS distribution. OS distribution is the initial image and every package is added as a new layer on top of that.

Shell and Exec forms

Commands in docker can be specified either in shell form or the Exec form.

All three instructions (RUN, CMD, and ENTRYPOINT) too can be specified in shell form or exec form.

Shell form
<instruction> <command>

Examples:

RUN apk --update add install ruby  
CMD echo "Hello world"  
ENTRYPOINT echo "Hello world"

When an instruction is executed in shell form it calls /bin/sh -c <command> under the hood and normal shell processing happens. For example, the following snippet in Dockerfile

ENV name Manu  
ENTRYPOINT echo "Hello, $name"  

when container runs as docker run -it <image> will produce output

Hello, Manu

Note that variable name is replaced with its value.

Exec form

This is the preferred form for CMD and ENTRYPOINT instructions.

<instruction> ["executable", "param1", "param2", ...]

Examples:

RUN ["apk", "add", "ruby"]  
CMD ["/bin/echo", "Hello world"]  
ENTRYPOINT ["/bin/echo", "Hello world"]  

When an instruction is executed in exec form it calls executable directly, and shell processing does not happen. For example, the following snippet in Dockerfile

ENV name Manu
ENTRYPOINT ["/bin/echo", "Hello, $name"]  

when container runs as docker run -it <image> will produce output

Hello, $name  

Note that variable name is not substituted.

How to run bash/zsh?

If you need to run bash (or any other interpreter like zsh but sh), use exec form with /bin/bash as executable. In this case, normal shell processing will take place. For example, the following snippet in Dockerfile

ENV name Manu
ENTRYPOINT ["/bin/bash", "-c", "echo Hello, $name"]  

when container runs as docker run -it <image> will produce output

Hello, Manu  

So hope you now have a clear idea of the 2 forms that can be used for specifying our commands. Now let us see the different commands and how to specify those using the 2 forms shell and exec.

RUN

As mentioned above, the RUN command is mainly used to install a new package on top of the main OS distribution. When you use the RUN command, it will execute the instruction and will create a new layer.

RUN command can be used in two forms:

1. Shell form
   RUN <command>

2. Exec form
   RUN ["executable", "param1", "param2"]

A good example demonstrating the RUN instruction would be to install multiple system packages that are needed for your image.

RUN apk --update && \
    apt add ruby \  
            ruby-json \
            ruby-nokogiri \
            git

Note that apk --update and apt add are executed in a single RUN instruction. This is done to make sure that the latest packages will be installed. If apk add were in a separate RUN instruction, then it would reuse a layer added by apt --update, which could had been created a long time ago.

CMD

CMD instruction allows you to set a default command and default parameters which will be executed when docker is run. But these commands and parameters can be overwritten by passing the values over the command line.

CMD can be specified in three forms:

1. exec form, preferred way
   CMD ["executable","param1","param2"]

2. (sets additional default parameters for ENTRYPOINT in exec form)
   CMD ["param1","param2"] 

3. Shell form
   CMD command param1 param2

Again, the first and third forms should look familar to you as they were already covered above. The second one is used together with ENTRYPOINT instruction in exec form. It sets default parameters that will be added after ENTRYPOINT parameters if container runs without command line arguments.

Let's have a look how CMD instruction works. The following snippet in Dockerfile

CMD echo "Hello world"  

when container runs as docker run -it <image> will produce output

Hello world  

but when container runs with a command, e.g., docker run -it <image> /bin/bash, CMD is ignored and bash interpreter runs instead:

root@7de4bed89922:/#  

ENTRYPOINT

ENTRYPOINT instruction should be used when you need your container to be run as an executable.
I might look similar to CMD, but in fact, it is different and should be used in a different context

The difference is ENTRYPOINT is that unlike CMD, the command and parameters are not ignored when Docker container runs with command line parameters. (There is a way to ignore ENTTRYPOINT, but it is unlikely that you will do it.)

ENTRYPOINT instructions too can be written in two forms:

1. Executable form preferred way
   ENTRYPOINT ["executable", "param1", "param2"] 

2. Shell form
   ENTRYPOINT command param1 param2

Exec form

Exec form of ENTRYPOINT allows you to set commands and parameters and then use either form of CMD to set additional parameters that are more likely to be changed. ENTRYPOINT arguments are always used while CMD ones can be overwritten by command line arguments provided when Docker container runs. For example, the following snippet in Dockerfile

ENTRYPOINT ["/bin/echo", "Hello"]  
CMD ["world"]  

when container runs as docker run -it <image> will produce output

Hello world  

but when container runs as docker run -it <image> Manu will result in

Hello Manu  
Shell form

Shell form of ENTRYPOINT ignores any CMD or docker run command line arguments.

Conclusion

Use RUN instructions to build your image by adding layers on top of the initial image.

Prefer ENTRYPOINT to CMD when building executable Docker image and you need a command always to be executed, and use CMD if you need to provide extra default arguments that could be overwritten from the command line when docker container runs.

Choose CMD if you need to provide a default command and/or arguments that can be overwritten from the command line when docker container runs.