Skip to content

Inconsistency in the handling of duplicate stage names #6731

@chmeliik

Description

@chmeliik

Issue Description

When a Containerfile contains duplicate stage names (FROM ... AS <name>):

  • --target <name> matches the first one
  • Without --target, if the final stage depends on the duplicate stages, buildah builds all of them and uses the last one
    • (same for --target <different stage> if this stage depends on the duplicate stages)

Comparing to docker, buildah should probably match the last one in both cases (and skip building the earlier ones unless --skip-unused-stages=false).

Context: in Konflux, we do builds with no network access, which requires pre-pulling the images for FROM, COPY --from= etc. We had bugs in the pre-pull logic, so we're correcting it to match buildah behavior, and found this inconsistency.

Steps to reproduce the issue

Steps to reproduce the issue

Containerfile:

FROM scratch AS stage0

LABEL x="really stage 0"


FROM scratch AS stage0

LABEL x="actually stage 1"


FROM stage0

Run:

$ buildah build -t test --target stage0 .              
STEP 1/2: FROM scratch AS stage0
STEP 2/2: LABEL x="really stage 0"
COMMIT test

$ buildah inspect test:latest | jq .OCIv1.config.Labels
{
  "io.buildah.version": "1.42.2",
  "x": "really stage 0"
}

# without --target
$ buildah build -t test . 
[1/3] STEP 1/2: FROM scratch AS stage0
[1/3] STEP 2/2: LABEL x="really stage 0"
Getting image source signatures
Copying blob 5f70bf18a086 skipped: already exists  
Copying config 4723b20146 done   | 
Writing manifest to image destination
--> 4723b2014644
[2/3] STEP 1/2: FROM scratch AS stage0
[2/3] STEP 2/2: LABEL x="actually stage 1"
Getting image source signatures
Copying blob 5f70bf18a086 skipped: already exists  
Copying config c6c98af0b5 done   | 
Writing manifest to image destination
--> c6c98af0b568
[3/3] STEP 1/1: FROM c6c98af0b5683156d753a2a41e315c13fb0c808e9eec87a165b111932acd2079
[3/3] COMMIT test

$ buildah inspect test:latest | jq .OCIv1.config.Labels
{
  "io.buildah.version": "1.42.2",
  "x": "actually stage 1"
}

Repeat experiment with docker:

$ docker build -t test -f Containerfile --target stage0 .
$ docker inspect test:latest | jq '.[].Config.Labels'    
{
  "x": "actually stage 1"
}

$ docker build -t test -f Containerfile .
$ docker inspect test:latest | jq '.[].Config.Labels'
{
  "x": "actually stage 1"
}

Note that docker logs for this Containerfile don't actually show which stages are being built, so to prove that it doesn't build the first stage0 at all, replace the first line in the Containerfile with: FROM image-does-not-exist:foo AS stage0 (which would fail if docker tried to build it)

Describe the results you received

  • --target <name> matches the first one
  • Without --target, if the final stage depends on the duplicate stages, buildah builds all of them and uses the last one

Describe the results you expected

  • --target <name> matches the last one, same as docker
  • Without --target, if the final stage depends on the duplicate stages, buildah builds and uses the last one, same as docker

buildah version output

Version:         1.42.2
Go Version:      go1.24.10
Image Spec:      1.1.1
Runtime Spec:    1.2.1
CNI Spec:        1.1.0
libcni Version:  
image Version:   5.38.0
Git Commit:      
Built:           Wed Dec  3 15:03:30 2025
OS/Arch:         linux/amd64
BuildPlatform:   linux/amd64

buildah info output

{
    "host": {
        "CgroupVersion": "v2",
        "Distribution": {
            "distribution": "fedora",
            "version": "42"
        },
        "MemFree": 14483775488,
        "MemTotal": 33056059392,
        "OCIRuntime": "crun",
        "SwapFree": 8589930496,
        "SwapTotal": 8589930496,
        "arch": "amd64",
        "cpus": 14,
        "hostname": "acmiel-thinkpadx1carbongen12.tpbc.csb",
        "kernel": "6.18.13-100.fc42.x86_64",
        "os": "linux",
        "rootless": true,
        "uptime": "1h 20m 26.46s (Approximately 0.04 days)",
        "variant": ""
    },
    "store": {
        "ContainerStore": {
            "number": 5
        },
        "GraphDriverName": "overlay",
        "GraphImageStore": "",
        "GraphOptions": null,
        "GraphRoot": "/home/acmiel/.local/share/containers/storage",
        "GraphStatus": {
            "Backing Filesystem": "btrfs",
            "Native Overlay Diff": "true",
            "Supports d_type": "true",
            "Supports shifting": "false",
            "Supports volatile": "true",
            "Using metacopy": "false"
        },
        "GraphTransientStore": false,
        "ImageStore": {
            "number": 122
        },
        "RunRoot": "/run/user/105500/containers"
    }
}

Provide your storage.conf

[storage]
driver = "overlay"
runroot = "/run/containers/storage"
graphroot = "/var/lib/containers/storage"

[storage.options]
additionalimagestores = [
"/usr/lib/containers/storage",
]
pull_options = {enable_partial_images = "true", use_hard_links = "false", ostree_repos=""}

[storage.options.overlay]
mountopt = "nodev,metacopy=on"

Upstream Latest Release

Yes

Additional environment details

No response

Additional information

No response

Metadata

Metadata

Assignees

Labels

kind/bugCategorizes issue or PR as related to a bug.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions