You’ve heard, read or otherwise know that Docker containers are only meant to run a single command. When building an image it’s difficult (by design) to run multiple commands.

When using ENTRYPOINT, CMD turns into additional arguments to the command in ENTRYPOINT. Despite what the name implies, you can’t run a command in CMD if it comes after ENTRYPOINT.

If you list more than one command in ENTRYPOINT

ENTRYPOINT "somecommand && othercommand" or
ENTRYPOINT ["somecommand", "&&", "othercommand"]

you’ll be out of luck. The container will exit after the first command exits.

Exactly the same thing happens with CMD.

What if you need to initialize some sort of state at run time instead of one time during the build? I ran into this problem. I was running redundant web servers in a swarm. I wanted to download something from remote storage that would be the content of the static website on these servers. If the content changes, all I should have to do is “docker update –force” on the service and they will each restart with the new content. That would be easy enough to script from my Continuous Integration service. There are certainly many more scenarios requiring similar functionality.

The solution that I finally arrived at was to launch a shell script in ENTRYPOINT. The script sets up the state I need then launches a shell with the arguments from ENTRYPOINT or CMD.

#!/bin/bash
#
#do some stuff
#
bash -c "$@"

If the build has

ENTRYPOINT ["somecommand.sh","nginx"]
CMD ["-g 'no-daemon']

then at the end of somecommand.sh, bash will execute nginx -g 'no daemon'. If you override CMD on the command line, then that will execute in place of -g 'no-daemon'.

Of course you could run any command instead of nginx, that’s just my example. You could move nginx or your command to CMD and specify only the setup script in ENTRYPOINT if you want to have a default command that you can override from the command line. For example, if you want to debug some unexpected situation, you could have this in the build:

ENTRYPOINT ["somecommand.sh"]
CMD ["nginx", "-g 'no-daemon']

and override the CMD with /bin/bash from the command line so that you can poke around in the container.

In either scenario, if CMD is not overridden from the command line, then the exact same arguments will get passed into somecommand.sh.

Lee Nelson

Long time Programmer, Systems Administrator and Network Engineer. Lifetime tinkerer.

nelsonov nelnet_org


Published