If you’ve ever needed to run a command or two in your Docker container on startup, this tutorial is for you. Using the Dockerfile ENTRYPOINT
and CMD
instructions, you can run as many startup commands as you’d like.
In this tutorial, you’ll learn how to use the ENTRYPOINT
and CMD
instructions to run startup commands in a Dockerfile and understand the differences between them.
Prerequisites
Since this tutorial will be a hands-on demonstration, be sure you have the following in place:
- A Windows 10 PC – Windows 10 v10.0.19042 was used in this tutorial.
- Docker Desktop – This tutorial uses Docker Desktop v3.3.1.
Creating a Dockerfile
Before you can run Docker container startup commands, you must first create a Dockerfile. A Dockerfile is a text document that contains a list of commands to build containers, Docker images and determines how a Docker image is created.
1. First, open PowerShell as administrator.
2. Create a new folder to store the Dockerfile and all associated files this tutorial will use and change to that directory. This tutorial is using ~/docker.
mkdir ~/docker
cd docker
3. Now, create a blank text file named Dockerfile with the following command.
cd > Dockerfile
Alternatively, you can create a Dockerfile with the following command if you’re on Linux or Mac OS.
touch Dockerfile
4. Finally, add the following content into the Dockerfile
FROM ubuntu:20.04
You now have created a soon-to-be Dockerfile!
Building a Docker Image
Now that you’ve created your Dockerfile, you must build a Docker image to execute the commands written in your Dockerfile ENTRYPOINT and CMD instructions. One way to build an image is by using the build
command.
While in the ~/docker directory, run the following command. The command below creates a Docker image called demo (-t demo
) from the Dockerfile in ~/docker by specifying the current working directory (.
).
docker build -t demo .
Running a Docker Container
After you’ve built the Docker image, you’ll need a container to run the Docker image that will execute the commands from the Dockerfile ENTRYPOINT and CMD instructions.
To run a Docker container, invoke the run
command to create a writeable container layer over the Docker image (demo
). The below example is using the -it
parameter to interactively connect to the container so you can see the sample output.
docker run -it demo
Exec vs. Shell Form
When you begin to work with a Dockerfile and figure out how to run startup commands, you may come across two different methods of defining these commands. Each method will invoke commands but does so a bit differently.
When Docker executes commands, it can do so directly called exec
or go through the container’s shell (/bin/sh -c
on Linux or cmd /S /C
on Windows) called shell
.
You’ll notice commands executed via exec
have an instruction followed by the executables to invoke followed by one or more command-line arguments, as shown below.
ENTRYPOINT ["executables", "parameter1", "parameter2", ...]
CMD ["executables", "parameter1", "parameter2:, ...]
Writing commands in shell
form, on the other hand, doesn’t require wrapping commands in square brackets, as shown below.
ENTRYPOINT <command> "parameter1"
CMD <command> "parameter1"
If you don’t specify a argument to
CMD
, Docker will always execute the command in exec form e.g.CMD <command>
.
If you’re just starting out, differentiating between these two command invocations won’t matter too much but as you get more advanced, you’ll soon see benefits and drawbacks to each.
Running Startup Commands
Let’s now get in the meat of this tutorial and get your hands dirty by walking through a few examples of running startup commands within a Dockerfile ENTRYPOINT
and CMD instructions.
1. Open the Dockerfile you created earlier in your preferred text editor.
2. Copy and paste the example Dockerfile contents into your Dockerfile, as shown below, and save it.
This Dockerfile creates a layer using the ubuntu:20.04
as a base image. It then tells Docker to invoke the echo
command passing it the Hello world
argument for both the Dockerfile CMD
and ENTRYPOINT
instructions using exec
and shell
form.
FROM ubuntu:20.04
# CMD Instruction
CMD ["echo", "Hello world"] # Exec Form
CMD echo "Hello world" # Shell Form
# ENTRYPOINT Instruction
ENTRYPOINT ["echo", "Hello world"] # Exec Form
ENTRYPOINT echo "Hello world" # Shell Form
3. While in the ~/docker directory, build the new image by running docker build
and call it demo
. The command below tags the image as demo
and looks for a Dockerfile in the current working directory (.
).
docker build -t demo .
4. Now, run a container using the image then run a Docker container based on the Docker image created earlier. You’ll now see that the container returns Hello world
which came from the CMD
instruction provided in the Dockerfile.
docker run -it demo
Using Variables in a Dockerfile
Sometimes you may not know the exact command-line arguments to pass to command ahead of time. The arguments you need to pass to a command are exposed only at runtime. Rather than statically assigning command arguments, you can capture and pass those arguments to commands with variables.
You can only use Dockerfile variables in
shell
form. Docker does not support variables in command invoked viaexec
form.
Open the Dockerfile in your preferred text editor again, replace everything inside with the following series of commands and save it.
You’ll notice this time, the Dockerfile uses environment variables and shown using ENV
. In the example below, the Dockerfile is defining an environment variable called name
with a value of friend
. Once created, this environment variable is then referenced via $name
.
When Docker runs a container based on this Dockerfile, it will invoke the echo
command and pass the argument of Welcome, friend
.
FROM ubuntu:20.04
ENV name friend
CMD echo "Welcome, $name"
# or
## ENTRYPOINT echo "Welcome, $name"
Now, create the Docker image and run the container again optionally providing a tag name of shellform
. You’ll notice that Docker invoked the echo
command and returned the expected output.
Combining Dockerfile ENTRYPOINT and CMD Instructions
Much of the time, you’ll be invoking startup commands either in CMD or ENTRYPOINT instruction. After all, you can invoke as many commands as you’d like using each method. But, you can also invoke a single command and “build onto it” using both instructions.
Building upon the previous examples, perhaps you have a Dockerfile that looks like the below example. As-is, if you create an image and run a container off of that image, Docker would invoke the echo
command and return Hello
.
FROM ubuntu:20.04
ENTRYPOINT ["echo", "Hello"]
Maybe you have another argument you’d like to pass to the echo
command but not right away. Maybe you’d like to do that further down the Dockerfile. By calling the CMD
instruction without a command, you can do so.
When you specify a command to run via the
ENTRYPOINT
instruction followed by theCMD
instruction, Docker automatically assumes the value passed toCMD
is an argument; not a command.
Now, add a CMD
instruction reference without a command, just an argument called world
, as shown below.
FROM ubuntu:20.04
ENTRYPOINT ["echo", "Hello"]
CMD ["world"]
Combining instructions should always be written in exec form due to its “array like” behavior of specifying values individually separated by commas vs all in one string.
After building the image and running the container from the image, you can see that instead of two lines of output (Hello
and world
), Docker only returns one meaning only a single echo
command invocation.
Conclusion
You should now have a good understanding of running Docker container startup commands via both the CMD
and ENTRYPOINT
Dockerfile instructions. Each instruction is a bit different but accomplishes the same task and can even be used together.
Can you think of a scenario where you’d prefer using CMD
over ENTRYPOINT
to run a startup command?