Hello,
I’m currently designing functionality for our internal build and release tool Konflux, which relies heavily on Buildah for container image building.
As part of this work, I’ve encountered a potential limitation in Buildah that I’d like to discuss. I'm also prepared to contribute an implementation if we can agree on the best approach.
Problem
In Konflux, we need to access content from intermediate images (e.g., packages, binaries) after the build process of multistage-build images. The only way I’m currently aware of to preserve these intermediate images is by using the --layers flag within buildah build. This works well for retaining intermediate layers, which we can then inspect post-build.
However, enabling --layers changes the structure of the final image’s metadata. Specifically:
-
When using --layers, the rootfs.diff_ids of the final image includes all intermediate layers (undesired).
-
When not using --layers, the final image contains only two layers in rootfs.diff_ids (desired):
- One for the base image.
- One for the squashed final image layers (I assume).
But of course, no intermediate images are stored (undesired).
-
In both cases, with or without --layers, the final image preserves the full history (also desired) containing parent base image and final image history.
-
Using --squash alongside --layers does not achieve the desired result either. This ends up fully squashing everything (including the parent base image), which removes necessary layering details (parent base image layer + all other squashed). In addition to that, history is capturing only the last layer of the final image, but we would like to preserve full history as it is produced without squash.
What I cannot do
- Unfortunately, performing two separate builds (one with
--layers for analysis and one without for the final image) is not viable for performance and pipeline simplicity reasons.
- I cannot use external tools like docker-squash, as they are heavily dependent on Docker and we want to certainly avoid that.
Desired Outcome
I’m looking for a way to:
- Preserve intermediate images for inspection.
- Have only two layers in the final image’s
rootfs.diff_ids (parent base image layer + squashed final image layers).
- Retain history metadata for the final image (parent base image and final image history)
Example Containerfile && buildah build examples and outcomes
FROM docker.io/library/golang:1.21-bullseye AS builder
WORKDIR /app
RUN echo 'package main; import "fmt"; func main() { fmt.Println("Hello from Go!") }' > main.go && \
go build -o hello main.go
FROM registry.access.redhat.com/ubi9/ubi:latest
RUN yum install python3-pip -y
RUN pip3 install semver==2.13.0
COPY --from=builder /app/hello /usr/local/bin/hello
CMD ["echo"]
| Command |
Images Created (buildah images) |
rootfs.diff_ids (simplified) |
History Retained |
Drawbacks |
buildah build --layers -t final-image:0.1 |
- final-image:0.1 - intermediate: <none> - base: ubi9 - builder: golang:1.21 |
- sha256:2cef... (base image layer) - sha256:f600... (yum install) - sha256:153a... (pip install) - sha256:43cd... (COPY hello) |
✅ Yes |
❗ Too many diff_ids in final image – every instruction becomes a layer |
buildah build -t final-image:0.1 |
- final-image:0.1 - base: ubi9 - builder: golang:1.21 |
- sha256:2cef... (base image layer) - sha256:92b3... (squashed final image layers) |
✅ Yes |
❗ Intermediate image is not retained – content from stages is lost |
buildah build --squash --layers -t final-image:0.1 |
- final-image:0.1 - intermediate: <none> - base: ubi9 - builder: golang:1.21 |
- sha256:92b3... (fully squashed: parent base image + all final image layers) |
❌ No (partial) |
❗ All layers including parent base image are squashed ❗ History is mostly lost |
buildah build --new-arg(s) -t final-image:0.1 |
- final-image:0.1 - intermediate: <none> - base: ubi9 - builder: golang:1.21 |
- sha256:2cef... (base image layer) - sha256:92b3... (squashed final image layers) |
✅ Yes |
✅ Desired behavior – parent base image + final image layers, preserved history, accessible intermediate stages |
Potential Proposed Solution
Based on current functionality, I believe we could introduce a new alternative flag: e.g., --layers-stages-only which would store intermediate stages as images, but squashes all filesystem changes of the final image layer chain, effectively yielding the same layer structure as a buildah build without --layers (parent base image sha + sha containing all other changes from final image manifested in rootfs.diff_ids).
Of course, I don't insist on the name of the argument, this is just an example, up for discussion.
Questions
- Did I miss anything? Is there currently any supported combination of flags in Buildah that would achieve this behavior?
- Would such functionality (separating intermediate stage storage from final image layering) be considered a reasonable feature to add? If so, I’d be happy to submit a patch or collaborate on an implementation aligned with Buildah's design philosophy.
Thank you for your time and consideration. I’d appreciate any guidance or thoughts on how best to proceed.
Hello,
I’m currently designing functionality for our internal build and release tool Konflux, which relies heavily on Buildah for container image building.
As part of this work, I’ve encountered a potential limitation in Buildah that I’d like to discuss. I'm also prepared to contribute an implementation if we can agree on the best approach.
Problem
In Konflux, we need to access content from intermediate images (e.g., packages, binaries) after the build process of multistage-build images. The only way I’m currently aware of to preserve these intermediate images is by using the
--layersflag withinbuildah build. This works well for retaining intermediate layers, which we can then inspect post-build.However, enabling
--layerschanges the structure of the final image’s metadata. Specifically:When using
--layers, therootfs.diff_idsof the final image includes all intermediate layers (undesired).When not using
--layers, the final image contains only two layers inrootfs.diff_ids(desired):But of course, no intermediate images are stored (undesired).
In both cases, with or without
--layers, the final image preserves the full history (also desired) containing parent base image and final image history.Using
--squashalongside--layersdoes not achieve the desired result either. This ends up fully squashing everything (including the parent base image), which removes necessary layering details (parent base image layer + all other squashed). In addition to that, history is capturing only the last layer of the final image, but we would like to preserve full history as it is produced without squash.What I cannot do
--layersfor analysis and one without for the final image) is not viable for performance and pipeline simplicity reasons.Desired Outcome
I’m looking for a way to:
rootfs.diff_ids(parent base image layer + squashed final image layers).Example Containerfile &&
buildah buildexamples and outcomes(buildah images)
rootfs.diff_ids(simplified)buildah build--layers-t final-image:0.1final-image:0.1- intermediate:
<none>- base:
ubi9- builder:
golang:1.21sha256:2cef...(base image layer)-
sha256:f600...(yum install)-
sha256:153a...(pip install)-
sha256:43cd...(COPY hello)diff_idsin final image – every
instruction becomes a layer
buildah build-t final-image:0.1final-image:0.1- base:
ubi9- builder:
golang:1.21sha256:2cef...(base image layer)-
sha256:92b3...(squashedfinal image layers)
retained – content from stages is lost
buildah build--squash --layers-t final-image:0.1final-image:0.1- intermediate:
<none>- base:
ubi9- builder:
golang:1.21sha256:92b3...(fullysquashed: parent base image + all final image layers)
(partial)
parent base image are squashed
❗ History is mostly lost
buildah build--new-arg(s)-t final-image:0.1final-image:0.1- intermediate:
<none>- base:
ubi9- builder:
golang:1.21sha256:2cef...(base image layer)-
sha256:92b3...(squashed final image layers)preserved history, accessible intermediate stages
Potential Proposed Solution
Based on current functionality, I believe we could introduce a new alternative flag: e.g.,
--layers-stages-onlywhich would store intermediate stages as images, but squashes all filesystem changes of the final image layer chain, effectively yielding the same layer structure as abuildah buildwithout--layers(parent base image sha + sha containing all other changes from final image manifested inrootfs.diff_ids).Of course, I don't insist on the name of the argument, this is just an example, up for discussion.
Questions
Thank you for your time and consideration. I’d appreciate any guidance or thoughts on how best to proceed.