The Docker ENTRYPOINT vs CMD is a question that gets asked a lot. This article will first go over the differences between the two Dockerfile options, move to how they can be used together, and then finally go over when to use each one.
Docker ENTRYPOINT vs CMD Differences
Most people use ENTRYPOINT & CMD interchangeably, because for most use cases they will function in a similar manner. The goal of each is as specified:
- ENTRYPOINT: Indicates a command that will ALWAYS be run when the container starts.
- CMD: Specifies the arguments that will be passed to ENTRYPOINT.
Looking at those definitions you wouldn’t think they would act in a similar manner. The reason why ENTRYPOINT and CMD feel like they can be used interchangeable is because the default ENTRYPOINT on most images is ‘/bin/sh -c’. For example, if you set ENTRYPOINT to run `echo “hello”`, when your container starts up it will run: `echo “hello”`. If you set CMD to `echo “hello”` your container would run `/bin/sh -c ‘echo “hello”‘`. Both which produce the same result as shown in the bash terminal below.
$ echo "hello" hello $ /bin/sh -c 'echo "hello"' hello
Now that you have the differences down, let’s move on to see how they can work together in harmony.
Using Docker ENTRYPOINT & CMD Together
In the last section we outlined that ENTRYPOINT is designed to receive arguments from CMD. Let’s a build a little docker image that follows those rules and scrapes a web page.
1.) First thing first let’s create a Dockerfile that has curl installed. For this tutorial we’ll just use the latest alpine linux image and add curl using the apk package manager.
FROM alpine:latest RUN apk add curl
2.) Next up, let’s add our ENTRYPOINT. Since we know that this container’s sole purpose is to run curl for us we’ll set the ENTRYPOINT to the curl binary (/usr/bin/curl). You’ll notice that I put our curl command in a string within an array. This is called Docker ‘exec’ form and is a best practice to ensuring your ENTRYPOINT and CMD run as you intend them to.
FROM alpine:latest RUN apk add curl ENTRYPOINT ["/usr/bin/curl"]
3.) Now it’s time to add our CMD. Remembering the purpose of CMD it’s designed to be sent as an argument to our ENTRYPOINT, but can be overridden on docker run. For now we’ll set it to ‘https://swissarmydevops.com’ as that will be the default curl URL.
FROM alpine:latest RUN apk add curl ENTRYPOINT ["/usr/bin/curl"] CMD ["https://swissarmydevops.com"]
4.) Time to build our docker image!
$ docker build -t curl-box:latest . Sending build context to Docker daemon 4.096kB Step 1/4 : FROM alpine:latest ---> d6e46aa2470d Step 2/4 : RUN apk add curl ---> Using cache ---> 4ffcd2a0ab23 Step 3/4 : ENTRYPOINT ["/usr/bin/curl"] ---> Running in 115da3a26d7c Removing intermediate container 115da3a26d7c ---> c82c9a30e1aa Step 4/4 : CMD ["https://swissarmydevops.com"] ---> Running in 0899fd7d88fa Removing intermediate container 0899fd7d88fa ---> b078cb9c4508 Successfully built b078cb9c4508 Successfully tagged curl-box:latest
5.) The moment of truth. Let’s run our container. For the sake of not overwhelming your screen with all of the html from the swissarmydevops.com homepage I’ve cut down the output. But we can see that it indeed scraped our site using ENTRYPOINT and CMD!
$ docker run curl-box:latest % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0<!DOCTYPE html><html class="no-js" lang="en-US"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="profile" href="https://gmpg.org/xfn/11" />
6.) Here’s where the flexible nature of these two Docker options come in handy. Let’s say I want to scrape a different page. Docker let’s you override the CMD option with the first argument you pass when you run the container. For example if I wanted to curl the URL ‘https://swissarmydevops.com/containers/docker/docker-cli-cheat-sheet‘ it would look like this.
$ docker run curl-box:latest https://swissarmydevops.com/containers/docker/docker-cli-cheat-sheet
When to use Docker CMD vs ENTRYPOINT
There are lots of opinions when it comes to this, so bear with me as I go off the books for a second. I generally follow the paradigm that if you’re building a container that is designed to run like a binary then use ENTRYPOINT & CMD together. Every other instance I recommend using CMD.
When I say that your app “runs like a binary” I mean it is designed with one purpose and expects to receive arguments from the user. Our minimalist curl app was a great example, it had one purpose and expected an argument. Other examples might be if you have a database backup script you want to run in a docker container. You could point ENTRYPOINT to the location of your script and then pass the database name in as a CMD when you run the container (i.e. `docker run db-backup:latest my-db-name`).
Thanks for reading! Hopefully this clarified any Docker CMD vs ENTRYPOINT questions you had. If you’re an avid Docker user I’d highly recommend checking out our Docker Cheat Sheet that outlines common Docker commands in an organized way.