RUN performs the step during the image build,
CMD is the default command when you start a container. If you define
CMD and then start your container with a different command (e.g.
docker run $my_image /bin/bash), you'll never see it run since you replaced your
chmod command with an empty
The detailed explanation:
RUN command creates a temporary container based off of the previous step of the build, executes your selected command, and when the command returns it captures the filesystem changes as a new layer of your image. Run is the equivalent of doing a
docker run +
docker commit to generate your image, but with a reproducible process.
CMD command replaces the current value of the image metadata for the
command value that you can see when you inspect the image. You can only have one value for
CMD for your image. You can also easily override the value of the image command by passing any string after the image name, e.g.
docker run busybox echo hello replaces the default
/bin/sh command with an
echo hello command for that specific container.
ENTRYPOINT which defines the entrypoint for your image. When you define an entrypoint, any value for
CMD gets passed as arguments to the entrypoint when starting your container (a container is only started with a single process as pid 1 and exits when that process exits).
Shell vs Exec syntax: Note that each of these accepts a command to run in two forms, the shell/string form, and the exec/json form. The shell form is anything that isn't a json list, and the result command is run as
/bin/sh -c "$your_string_here". A shell is useful for IO redirection, chaining commands, and variable expansion. But a shell will also intercept signals which can cause issues when the container is stopped. The exec form runs your command directly in the OS without a shell, which is better for signals, but lacking all the other shell features. When you chain an entrypoint and cmd together, you almost always want the exec syntax since
/bin/sh -c only accepts one argument. It's common to run a shell script but have the last command run be an
exec which replaces the shell. E.g. a common entrypoint structure is:
CMD ["server", "arg1", "arg2"]
# ... steps to setup the host, make configs, fix permissions, copy files, etc ...
# now run the value of CMD, replacing this shell as pid 1: