Updated on April 20, 2025
When creating a Docker container, the goal is generally that anyone could simply execute docker run <containername>
and launch the container. In today's article, we are going to explore two key Dockerfile
instructions that enable us to do just that. Let's explore the differences between the CMD
and ENTRYPOINT
instructions.
On the surface, the CMD
and ENTRYPOINT
instructions look like they perform the same function. However, once you dig deeper, it's easy to see that these two instructions perform completely different tasks.
ENTRYPOINT overview
The Docker ENTRYPOINT
instruction sets the executable command that is run each time a container starts, effectively transforming the container into an executable program. This is especially beneficial when the container is intended to function consistently as a single-purpose application. By using the ENTRYPOINT
instruction, any arguments passed during the container startup via the docker
run command are appended to the defined executable, streamlining container usage.
Utilizing the ENTRYPOINT
instruction also allows the container to execute its main process as PID 1, providing efficient handling of operating system signals such as SIGTERM and SIGINT. This ensures a more stable and manageable lifecycle for containers, facilitating smoother shutdowns and interruptions. It is important to note that overriding the ENTRYPOINT
command at runtime requires explicit use of Docker's --entrypoint
option, highlighting its intended use for fixed execution behaviors.
CMD overview
The Docker CMD
instruction specifies default commands or parameters that execute when the container starts. It provides users flexibility, allowing these defaults to be easily overridden during container runtime. This makes the CMD
instruction particularly useful for scenarios in which a container requires flexible command execution or parameterization.
However, commands set with the CMD
instruction typically do not run as PID 1 unless used in conjunction with the ENTRYPOINT
instruction, which can result in less efficient handling of system signals. Therefore, the CMD
instruction is better suited for providing adjustable defaults rather than defining fixed commands intended for robust system signal management.
In essence, the CMD
instruction is ideal for situations that demand flexibility and adaptability, whereas ENTRYPOINT
excels in enforcing consistent behavior with the option to parameterize through command-line arguments.
Docker container example: ApacheBench
To help serve as an example, we're going to create a Docker container that simply executes the ApacheBench utility.
In earlier articles, we discovered the simplicity and usefulness of the ApacheBench load testing tool. However, this type of command-line utility is not generally the type of application one would "Dockerize." The general usage of Docker is focused more on creating services rather than single execution tools like ApacheBench.
The main reason behind this is that, typically, Docker containers are not built to accept additional parameters when launching. This makes it tricky to use a command-line tool within a container.
Let's see this in action by creating a Docker container that can be used to execute ApacheBench against any site.
FROM ubuntu:latest RUN apt-get update && \ apt-get install -y apache2-utils && \ rm -rf /var/lib/apt/lists/* CMD ab
In the Dockerfile
, we are simply using the ubuntu:latest
image as our base container image, installing the apache2-utils
package, and then defining that the command for this container is the ab
command.
Since this Docker container is planned to be used as an executor for the ab
command, it makes sense to set the CMD
instruction value to the ab
command. However, if we run this container, we will start to see an interesting difference between it and other application containers.
However, before we can run this container, we first need to build it. We can do so with the docker build
command.
$ docker build -t ab . Sending build context to Docker daemon 2.048 kB Step 1/3 : FROM ubuntu:latest ---> ebcd9d4fca80 Step 2/3 : RUN apt-get update && apt-get install -y apache2-utils && rm -rf /var/lib/apt/lists/* ---> Using cache ---> d9304ff09c98 Step 3/3 : CMD ab ---> Using cache ---> ecfc71e7fba9 Successfully built ecfc71e7fba9
When building this container, I tagged the container with the name of ab
. This means we can simply launch this container via the name ab
.
$ docker run ab ab: wrong number of arguments Usage: ab [options] [http[s]://]hostname[:port]/path Options are: -n requests Number of requests to perform -c concurrency Number of multiple requests to make at a time -t timelimit Seconds to max. to spend on benchmarking This implies -n 50000 -s timeout Seconds to max. wait for each response Default is 30 seconds
When we run the ab
container, we get back an error from the ab
command as well as usage details. The reason for this is that we defined the CMD
instruction to the ab
command without specifying any flags or target host to load test against. This CMD
instruction is used to define what command the container should execute when launched. Since we defined that as the ab
command without arguments, it executed the ab
command without arguments.
However, like most command-line tools, that simply isn't how ab
works. With ab
, you need to specify what URL
you wish to test against.
What we can do in order to make this work is override the CMD
instruction when we launch the container. We can do this by adding the command and arguments we wish to execute at the end of the docker run
command.
$ docker run ab ab http://bencane.com/ Benchmarking bencane.com (be patient).....done Concurrency Level: 1 Time taken for tests: 0.343 seconds Complete requests: 1 Failed requests: 0 Total transferred: 98505 bytes HTML transferred: 98138 bytes Requests per second: 2.92 [#/sec] (mean) Time per request: 342.671 [ms] (mean) Time per request: 342.671 [ms] (mean, across all concurrent requests) Transfer rate: 280.72 [Kbytes/sec] received
When we add ab http://bencane.com
to the end of our docker run
command, we are able to override the CMD
instruction and execute the ab
command successfully. However, while we were successful, this process of overriding the CMD
instruction is rather clunky.
!Sign up for a free Codeship Account
Using ENTRYPOINT instructions
This is where the ENTRYPOINT
instruction shines. The ENTRYPOINT
instruction works very similarly to CMD
in that it is used to specify the command executed when the container is started. However, where it differs is that ENTRYPOINT
doesn't allow you to override the command.
Instead, anything added to the end of the docker run
command is appended to the command. To understand this better, let's go ahead and change our CMD
instruction to the ENTRYPOINT
instruction.
FROM ubuntu:latest RUN apt-get update && \ apt-get install -y apache2-utils && \ rm -rf /var/lib/apt/lists/* ENTRYPOINT ["ab"]
After editing the Dockerfile
, we will need to build the image once again.
$ docker build -t ab . Sending build context to Docker daemon 2.048 kB Step 1/3 : FROM ubuntu:latest ---> ebcd9d4fca80 Step 2/3 : RUN apt-get update && apt-get install -y apache2-utils && rm -rf /var/lib/apt/lists/* ---> Using cache ---> d9304ff09c98 Step 3/3 : ENTRYPOINT ab ---> Using cache ---> aa020cfe0708 Successfully built aa020cfe0708
Now, we can run the ab
container once again; however, this time, rather than specifying ab http://bencane.com
, we can simply add http://bencane.com
to the end of the docker run command.
$ docker run ab http://bencane.com/ Benchmarking bencane.com (be patient).....done Concurrency Level: 1 Time taken for tests: 0.436 seconds Complete requests: 1 Failed requests: 0 Total transferred: 98505 bytes HTML transferred: 98138 bytes Requests per second: 2.29 [#/sec] (mean) Time per request: 436.250 [ms] (mean) Time per request: 436.250 [ms] (mean, across all concurrent requests) Transfer rate: 220.51 [Kbytes/sec] received
As the above example shows, we have now essentially turned our container into an executable. If we wanted, we could add additional flags to the ENTRYPOINT
instruction to simplify a complex command-line tool into a single-argument Docker container.
The importance of syntax when using ENTRYPOINT
One important thing to note about the ENTRYPOINT
instruction is that syntax is critical. Technically, ENTRYPOINT
supports both the ENTRYPOINT ["command"]
syntax and the ENTRYPOINT command
syntax. However, while both of these are supported, they have two different meanings and change how ENTRYPOINT
works.
Let's change our Dockerfile
to match this syntax and see how it changes our container's behavior.
FROM ubuntu:latest RUN apt-get update && \ apt-get install -y apache2-utils && \ rm -rf /var/lib/apt/lists/* ENTRYPOINT ab
With the changes made, let's build the container.
$ docker build -t ab . Sending build context to Docker daemon 2.048 kB Step 1/3 : FROM ubuntu:latest ---> ebcd9d4fca80 Step 2/3 : RUN apt-get update && apt-get install -y apache2-utils && rm -rf /var/lib/apt/lists/* ---> Using cache ---> d9304ff09c98 Step 3/3 : ENTRYPOINT ab ---> Using cache ---> bbfe2686a064 Successfully built bbfe2686a064
With the container built, let's run it again using the same options as before.
$ docker run ab http://bencane.com/ ab: wrong number of arguments Usage: ab [options] [http[s]://]hostname[:port]/path Options are: -n requests Number of requests to perform -c concurrency Number of multiple requests to make at a time -t timelimit Seconds to max. to spend on benchmarking This implies -n 50000 -s timeout Seconds to max. wait for each response Default is 30 seconds
It looks like we are back to the same behavior as the CMD
instruction. However, if we try to override the ENTRYPOINT
we will see different behavior than when we overrode the CMD
instruction.
$ docker run ab ab http://bencane.com/ ab: wrong number of arguments Usage: ab [options] [http[s]://]hostname[:port]/path Options are: -n requests Number of requests to perform -c concurrency Number of multiple requests to make at a time -t timelimit Seconds to max. to spend on benchmarking This implies -n 50000 -s timeout Seconds to max. wait for each response Default is 30 seconds
With the ENTRYPOINT
instruction, it is not possible to override the instruction during the docker run
command execution like we are with CMD
. This highlights another usage of ENTRYPOINT
, as a method of ensuring that a specific command is executed when the container in question is started, regardless of attempts to override the ENTRYPOINT
.
Final thoughts on using CMD and ENTRYPOINT in Dockerfiles
Understanding the differences between Docker's CMD
and ENTRYPOINT
instructions is essential for creating flexible, reliable, and efficient containers. While CMD
offers versatility by allowing default commands to be overridden easily, ENTRYPOINT
provides consistency by ensuring a specified command runs every time a container starts.
Mastering these instructions enables developers to optimize their Docker containers effectively for various use cases. For more detailed examples and further customization possibilities, explore Docker's official Dockerfile reference documentation.