Skip to content

Building FROM a base image with layers but no history silently drops the child's history #7037

Description

@chkpnt

Issue Description

Building an image FROM a base image that has layers but an empty/absent history (e.g. produced with buildah build --omit-history or buildah commit --squash --omit-history) currently produces inconsistent and always suboptimal results, depending on the tool and version:

  • Buildah 1.42.x: the build succeeds, but the child image silently ends up with no history at all (forceOmitHistory).
  • Podman 4.9.5: the build aborts with an internal consistency error as soon as the child records a history entry of its own.
  • Docker 24.0 accepts the base and appends the child's history entries without synthesizing records for the inherited layers, so layer-aware tools such as dive associate the child's history with the wrong filesystem layers.

I know that the history is optional according to the OCI image spec. But omitting a history when --omit-history is not passed is unexpected.

Steps to reproduce the issue

  1. Create a base image that has a layer but no history:

    $ echo hello > test.txt
    $ printf 'FROM scratch\nCOPY test.txt /test.txt\n' > Containerfile.base
    $ buildah build --omit-history -t localhost/base:nohistory -f Containerfile.base .
    $ skopeo inspect --config containers-storage:localhost/base:nohistory | jq '{layers: (.rootfs.diff_ids|length), history: .history}'
    # 1 layer, empty/absent history
  2. Build a child that records a metadata step before adding a layer:

    $ printf 'FROM localhost/base:nohistory\nARG SOURCE_DATE_EPOCH=315532800\nCOPY test.txt /test2.txt\n' > Containerfile.child
    $ buildah build -t localhost/child -f Containerfile.child .
    $ # resp. podman and docker build

Describe the results you received

Podman 4.9.5: hard failure:

STEP 2/3: ARG SOURCE_DATE_EPOCH=315532800
--> c577ce811744
STEP 3/3: COPY test.txt /test2.txt
Error: committing container for step {... Command:copy Args:[test.txt /test2.txt] ...}:
copying layers and metadata for container "...": initializing source
containers-storage:c577ce811744-working-container: internal error:
history lists 1 non-empty layers, but we have 2 layers on disk

Buildah 1.42.x: the build succeeds, but the child has no history:

$ skopeo inspect --config containers-storage:localhost/child | jq '.history'
null

Docker 24.0: the build succeeds, but the child's history entries are appended without placeholder records for the inherited base layer(s), so dive (and similar layer-aware tools) attribute the child's history to the wrong layers.

Describe the results you expected

A consistent, documented, and structurally correct behavior, ideally:

  • The build succeeds and synthesizes blank placeholder history entries for the base image's filesystem layers, so the child's history stays consistent and effectively "starts with its own layers" (preferred), or failing that,
  • A clear, actionable error at the FROM step stating that the base image has layers but no history, so the user explicitly needs to add --omit-history.

Not silent history loss (Buildah), an internal error (Podman 4.9.5), or silently misaligned history (Docker).

Root cause (from image.go)

makeContainerImageRef sets forceOmitHistory = true when the parent's history is completely empty (len(b.OCIv1.History) == 0 && len(b.OCIv1.RootFS.DiffIDs) != 0). The code comment says this avoids "an image with multiple layers, only some of which have history entries."

Possible solutions

Ranked:

  1. Synthesize blank placeholder history entries for the base image's filesystem layers (empty_layer absent/false, blank fields), so the child's history stays consistent and consumers can append correctly. Buildah already appends structured entries via preEmptyLayers/postEmptyLayers. This avoids both the Docker problem (misaligned history) and the Buildah problem (dropped history).
  2. Fail fast at FROM with a clear, actionable error, so the user explicitly needs to add --omit-history, if Buildah deliberately refuses to repair third-party images.
  3. Force-omit history (current Buildah behavior): last resort, ideally explicit/opt-in, since it silently discards history for all descendants.
  4. Append without padding (Docker's behavior): avoids the failed build but demonstrably misleads layer-aware tools; avoid.

buildah version output

Version:         1.43.1
Go Version:      go1.26.1-X:nodwarf5
Image Spec:      1.1.1
Runtime Spec:    1.2.1
CNI Spec:        1.1.0
libcni Version:  
image Version:   5.39.2
Git Commit:      
Built:           Wed Apr  8 19:37:51 2026
OS/Arch:         linux/arm64
BuildPlatform:   linux/arm64/v8

buildah info output

{
    "host": {
        "CgroupVersion": "v2",
        "Distribution": {
            "distribution": "fedora",
            "version": "44"
        },
        "MemFree": 181133312,
        "MemTotal": 2036867072,
        "OCIRuntime": "crun",
        "SwapFree": 0,
        "SwapTotal": 0,
        "arch": "arm64",
        "cpus": 6,
        "hostname": "d1b12df9c461",
        "kernel": "6.17.7-300.fc43.aarch64",
        "os": "linux",
        "rootless": true,
        "uptime": "10h 18m 57.17s (Approximately 0.42 days)",
        "variant": ""
    },
    "store": {
        "ContainerStore": {
            "number": 0
        },
        "GraphDriverName": "overlay",
        "GraphImageStore": "",
        "GraphOptions": [
            "overlay.imagestore=/var/lib/shared",
            "overlay.imagestore=/usr/lib/containers/storage",
            "overlay.mount_program=/usr/bin/fuse-overlayfs",
            "overlay.mountopt=nodev,fsync=0"
        ],
        "GraphRoot": "/var/lib/containers/storage",
        "GraphStatus": {
            "Backing Filesystem": "xfs",
            "Native Overlay Diff": "false",
            "Supports d_type": "true",
            "Supports shifting": "true",
            "Supports volatile": "true",
            "Using metacopy": "false"
        },
        "GraphTransientStore": false,
        "ImageStore": {
            "number": 0
        },
        "RunRoot": "/run/containers/storage"
    }
}

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

No

Additional environment details

Tested in a GitLab Pipeline with Docker Runners on Linux and using podman on macOS (via podman machine).

Additional information

  • Arose in a reproducible-builds pipeline (--source-date-epoch + --rewrite-timestamp + caching), where --omit-history is currently the only way to avoid non-static history content.

Metadata

Metadata

Assignees

No one assigned

    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