Skip to content

Multistage builds do not get cached #1270

@chocolateimage

Description

@chocolateimage

Describe the bug
If you have a Dockerfile with multiple steps that is being used by multiple services like this:

Repository: https://gitlab.com/chocolateimage/multistage

compose.yaml

services:
  service1:
    build:
      target: service1
      dockerfile: Dockerfile

  service2:
    build:
      target: service2
      dockerfile: Dockerfile

  service3:
    build:
      target: service3
      dockerfile: Dockerfile

  service4:
    build:
      target: service4
      dockerfile: Dockerfile

And the Dockerfile:

FROM alpine:latest AS base
WORKDIR /app

FROM base AS deps
COPY dependency-generator.sh /app/
RUN [ "./dependency-generator.sh" ]

FROM base AS service-base
COPY service-runner.sh /app

# Pretend all of the services below do different things

FROM service-base AS service1
COPY --from=deps /app/deps /app/deps
ENTRYPOINT [ "./service-runner.sh", "service1" ]

FROM service-base AS service2
COPY --from=deps /app/deps /app/deps
ENTRYPOINT [ "./service-runner.sh", "service2" ]

FROM service-base AS service3
COPY --from=deps /app/deps /app/deps
ENTRYPOINT [ "./service-runner.sh", "service3" ]

FROM service-base AS service4
COPY --from=deps /app/deps /app/deps
ENTRYPOINT [ "./service-runner.sh", "service4" ]

The deps stage will be executed multiple times, this does not happen with Docker itself. In this example it will not do much, but in a real setting where you would download stuff and do CPU intensive work, this can slow down the build process by a large amount.

To Reproduce
Steps to reproduce the behavior:

  1. git clone https://gitlab.com/chocolateimage/multistage.git
  2. podman-compose build
  3. The line "Generating" your dependencies gets printed multiple times

Expected behavior
The stage for generating dependencies only gets executed once and cached for the other COPY --from commands, just like in Docker.

Actual behavior
What is the behavior you actually got and that should not happen.

Output

$ podman-compose version
podman-compose version 1.5.0
podman version 5.5.2

$ podman version
Client:       Podman Engine
Version:      5.5.2
API Version:  5.5.2
Go Version:   go1.24.4
Git Commit:   e7d8226745ba07a64b7176a7f128e4ef53225a0e
Built:        Fri Jun 27 10:15:44 2025
OS/Arch:      linux/amd64

$ podman-compose build
[1/7] STEP 1/2: FROM alpine:latest AS base
[1/4] STEP 1/2: FROM alpine:latest AS base
[1/5] STEP 1/2: FROM alpine:latest AS base
[1/6] STEP 1/2: FROM alpine:latest AS base
[1/4] STEP 2/2: WORKDIR /app
[1/5] STEP 2/2: WORKDIR /app
[1/6] STEP 2/2: WORKDIR /app
--> Using cache 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7
--> 95933deb20a3
--> Using cache 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7
[3/4] STEP 1/2: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS service-base
--> 95933deb20a3
[3/5] STEP 1/2: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS service-base
[1/7] STEP 2/2: WORKDIR /app
--> Using cache 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7
--> 95933deb20a3
[3/4] STEP 2/2: COPY service-runner.sh /app
[3/5] STEP 2/2: COPY service-runner.sh /app
[2/6] STEP 1/3: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS deps
[2/6] STEP 2/3: COPY dependency-generator.sh /app/
--> Using cache 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7
--> 95933deb20a3
[2/7] STEP 1/3: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS deps
[2/7] STEP 2/3: COPY dependency-generator.sh /app/
--> Using cache 034fcc266fb6114d2937ab288fa83ea807d0e66a8feb3dc82ad0c98314fd540d
--> 034fcc266fb6
[2/6] STEP 3/3: RUN [ "./dependency-generator.sh" ]
--> Using cache 034fcc266fb6114d2937ab288fa83ea807d0e66a8feb3dc82ad0c98314fd540d
--> 034fcc266fb6
[2/7] STEP 3/3: RUN [ "./dependency-generator.sh" ]
"Generating" your dependencies...
--> a56b1948e08e
[4/4] STEP 1/3: FROM a56b1948e08e85a3b8f4afe5b1f38ae741e65042e3de3bb0ac4f1fd4665232ca AS service1
[4/4] STEP 2/3: COPY --from=deps /app/deps /app/deps
[2/4] STEP 1/3: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS deps
"Generating" your dependencies...
[2/4] STEP 2/3: COPY dependency-generator.sh /app/
--> e0bd0f2bd1b2
[2/5] STEP 1/3: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS deps
[2/5] STEP 2/3: COPY dependency-generator.sh /app/
--> Using cache 034fcc266fb6114d2937ab288fa83ea807d0e66a8feb3dc82ad0c98314fd540d
--> 034fcc266fb6
[2/4] STEP 3/3: RUN [ "./dependency-generator.sh" ]
--> Using cache 034fcc266fb6114d2937ab288fa83ea807d0e66a8feb3dc82ad0c98314fd540d
--> 034fcc266fb6
[2/5] STEP 3/3: RUN [ "./dependency-generator.sh" ]
"Generating" your dependencies...
"Generating" your dependencies...
Done
Done
--> 385e7c36c476
--> dd616ccca2ac
Done
[3/7] STEP 1/2: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS service-base
--> Using cache 385e7c36c476103bb79d835758d030cf0deb5ac2a42783f480b2d27d99c694dd
--> 385e7c36c476
[3/6] STEP 1/2: FROM 95933deb20a39176801eb47bad90d1eebb5a975555ad3cc92231bdc5efec4de7 AS service-base
[3/7] STEP 2/2: COPY service-runner.sh /app
[3/6] STEP 2/2: COPY service-runner.sh /app
Done
--> Using cache 385e7c36c476103bb79d835758d030cf0deb5ac2a42783f480b2d27d99c694dd
--> 385e7c36c476
[5/5] STEP 1/3: FROM e0bd0f2bd1b2f166f2240ada9817708cc89e344e0c06d67c082fc59d82fa3a09 AS service2
[5/5] STEP 2/3: COPY --from=deps /app/deps /app/deps
--> Using cache a56b1948e08e85a3b8f4afe5b1f38ae741e65042e3de3bb0ac4f1fd4665232ca
--> a56b1948e08e
--> Using cache a56b1948e08e85a3b8f4afe5b1f38ae741e65042e3de3bb0ac4f1fd4665232ca
--> a56b1948e08e
[6/6] STEP 1/3: FROM a56b1948e08e85a3b8f4afe5b1f38ae741e65042e3de3bb0ac4f1fd4665232ca AS service3
[7/7] STEP 1/3: FROM a56b1948e08e85a3b8f4afe5b1f38ae741e65042e3de3bb0ac4f1fd4665232ca AS service4
[6/6] STEP 2/3: COPY --from=deps /app/deps /app/deps
[7/7] STEP 2/3: COPY --from=deps /app/deps /app/deps
--> 7ae956fbd264
--> 15ca8285fd4c
--> a9360b9c3962
--> 0245a875c6f2
[5/5] STEP 3/3: ENTRYPOINT [ "./service-runner.sh", "service2" ]
[4/4] STEP 3/3: ENTRYPOINT [ "./service-runner.sh", "service1" ]
[7/7] STEP 3/3: ENTRYPOINT [ "./service-runner.sh", "service4" ]
[6/6] STEP 3/3: ENTRYPOINT [ "./service-runner.sh", "service3" ]
[5/5] COMMIT multistage_service2
[6/6] COMMIT multistage_service3
[4/4] COMMIT multistage_service1
[7/7] COMMIT multistage_service4
--> 7af36609ab82
Successfully tagged localhost/multistage_service4:latest
--> 74767c5e34ff
--> f257e791174d
Successfully tagged localhost/multistage_service3:latest
Successfully tagged localhost/multistage_service2:latest
--> 108129ec59fd
Successfully tagged localhost/multistage_service1:latest
7af36609ab8204af22d5df7bdde61fbf9320a61eec2dbf17df9e3cca16b5e997
74767c5e34ffd950a0da07fbb84e788e722d90fdc389b5403d7c935a5c720ab4
f257e791174d390653459aeafc5502c929e13558c035e7ca7fe2fee37ea028c7
108129ec59fdac8160f06ae07fa8baba7726782d616fd46aa8afd86df3232705

Environment:

  • OS: Linux
  • podman version: 5.5.2
  • podman compose version: f7eeda1

Additional context

Tested in a fresh libvirt/QEMU machine with Arch Linux. I uploaded the example code on GitLab: https://gitlab.com/chocolateimage/multistage

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions