Docker CMD Instruction Can Be Tricky in Multi-Stage Builds
In Dockerfiles, the CMD
instruction defines the command to be executed in containers created from a Docker image. There can only be one CMD
instruction in a Dockerfile, if more than one is defined, only the last one takes effect. As example:
FROM alpine
CMD ["echo", "first instruction"]
CMD ["echo", "second instruction"]
In this case the second CMD
instruction will be executed. The output of running a container will be:
bash-5.2$ docker run multiple-cmd:latest
second instruction
In single-stage builds it doesn’t matter where the CMD
instruction is defined in Dockerfile, as example, in the below file although the CMD
instruction is defined in the middle of the file, it will still be used as the default command to be executed by containers:
FROM alpine
CMD ["echo", "Hello!"]
RUN apk update
However, in multi-stage builds, if the same command will be executed in all stages, the CMD
instruction should be defined before all the stages:
FROM alpine AS base
--> CMD ["echo", "Hello!"] <--
FROM base AS stage1
RUN echo "stage1 instructions"
FROM base AS stage2
RUN echo "stage2 instructions"
In this case, the images built for stage1
and stage2
will execute the same command:
-
For
stage1
:> docker build -f Dockerfile --target stage1 --tag docker-image:stage1 . > docker run docker-image:stage1 Hello!
-
For
stage2
:> docker build -f Dockerfile --target stage2 --tag docker-image:stage2 . > docker run docker-image:stage2 Hello!
The tricky case happens when the CMD
instruction is defined after one of the stages, and there is only one CMD
instruction in the file, as example:
FROM alpine AS base
FROM base AS stage1
RUN echo "stage1 instructions"
--> CMD ["echo", "Hello!"] <--
FROM base AS stage2
RUN echo "stage2 instructions"
In this case, building an image for stage2
will skip the CMD
instruction and inherit the CMD
instruction of the base
image (alpine
):
> docker build -f Dockerfile --target stage2 --tag docker-image:tricky .
> docker run docker-image:tricky
>
The instruction was skipped because docker builder executes the instructions in the dependent stages (in this case base
stage) in addition to the instructions of the specified stage (stage2
). But doesn’t execute any of the instructions in other stages.