Search code examples
linuxdockerubuntudockerfiledocker-multi-stage-build

Dockerfile not executing second stage


I'm getting some weird behavior from Docker and I can't find it mentioned anywhere. It is skipping stages seemingly at random, even with multi-stage Dockerfiles that I've simply copy-pasted from forums around the net.

My dockerfile is:

FROM alpine as base
RUN echo "1"

# SKIPPED
FROM base as mid
RUN echo "2"

FROM base as final
RUN echo "3"

OUTPUT:

docker build -t test .\ --no-cache
[+] Building 2.0s (7/7) FINISHED
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 34B                                                                                   0.0s
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 143B                                                                               0.0s
 => [internal] load metadata for docker.io/library/alpine:latest                                                   1.3s
 => CACHED [base 1/2] FROM docker.io/library/alpine@sha256:...  0.0s
 => [base 2/2] RUN echo "1"                                                                                        0.3s
 => [final 1/1] RUN echo "3"                                                                                       0.4s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:...                      0.0s
 => => naming to docker.io/library/...                                                                 0.0s

What causes this?


Solution

  • Buildkit uses a dependency graph. It looks at the target stage, which by default is the last one:

    FROM base as final
    RUN echo "3"
    

    From there it sees that base is needed to build this stage so it pulls in the base stage:

    FROM alpine as base
    RUN echo "1"
    

    And from there it's done, it's not needed to build the mid stage to create your target image. There's no dependencies in the FROM or a COPY --from that would require it. This behavior differs from the classic docker build which performed steps in order until the target stage was reached, and is one of the reasons buildkit is much faster.