Build reproducibility
June 3, 2026 ยท View on GitHub
Reproducing the pinned dependencies
Reproducing the pinned dependencies is supported since BuildKit v0.11.
e.g.,
buildctl build --frontend dockerfile.v0 --local dockerfile=. --local context=. --source-policy-file policy.json
An example policy.json:
{
"rules": [
{
"action": "CONVERT",
"selector": {
"identifier": "docker-image://docker.io/library/alpine:latest"
},
"updates": {
"identifier": "docker-image://docker.io/library/alpine:latest@sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454"
}
},
{
"action": "CONVERT",
"selector": {
"identifier": "https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md"
},
"updates": {
"attrs": {"http.checksum": "sha256:6e4b94fc270e708e1068be28bd3551dc6917a4fc5a61293d51bb36e6b75c4b53"}
}
}
]
}
Any source type is supported, but how to pin a source depends on the type.
SOURCE_DATE_EPOCH
SOURCE_DATE_EPOCH is the convention for pinning timestamps to a specific value.
The Dockerfile frontend supports consuming the SOURCE_DATE_EPOCH value as a special build arg, since BuildKit 0.11.
Minimal support is also available on older BuildKit when using Dockerfile 1.5 frontend.
When the caller does not pass SOURCE_DATE_EPOCH, the Dockerfile may define a
default in the global scope:
ARG SOURCE_DATE_EPOCH=1704067200
FROM alpine
The Dockerfile frontend also supports the special value SOURCE_DATE_EPOCH=context.
This resolves the main build context to a numeric Unix timestamp before the build:
- git context: commit time
- HTTP context:
Last-Modified, or the newest archive entry mtime when building from an archive withoutLast-Modified - local context: ignored, leaving
SOURCE_DATE_EPOCHunset
buildctl build --frontend dockerfile.v0 --opt build-arg:SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) ...
The buildctl CLI (>= v0.13) and Docker Buildx (>= 0.10) automatically propagate the $SOURCE_DATE_EPOCH environment value from the client host to the SOURCE_DATE_EPOCH build arg.
The build arg value is used for:
- the
createdtimestamp in the OCI Image Config - the
createdtimestamp in thehistoryobjects in the OCI Image Config - the
org.opencontainers.image.createdannotation in the OCI Image Index - the timestamp of the files exported with the
localexporter - the timestamp of the files exported with the
tarexporter
To apply the build arg value to the timestamps of the files inside the image, specify rewrite-timestamp=true as an image exporter option:
--output type=image,name=docker.io/username/image,push=true,rewrite-timestamp=true
The rewrite-timestamp option is available since BuildKit v0.13.
See v0.12 documentation for dealing with timestamps
in BuildKit v0.12 and v0.11.
See also the documentation of the Dockerfile frontend.
compatibility-version
compatibility-version pins digest-affecting image assembly behavior for the image and oci exporters.
BuildKit currently supports these values:
10for thev0.13.0andv0.14.0historical path20for thev0.15.0throughv0.31.xpath30for the current path
30
30 is the current compatibility version.
Compared to 20, 30 changes the default image exporter media type behavior:
- when
oci-mediatypesis not set, image exports default to OCI media types
20
20 represents the historical v0.15.0 through v0.31.x path.
It matches the v0.15.0 through v0.31.x output covered by the compatibility test matrix in client/compatibility_test.go.
Compared to 30, image exports that do not set oci-mediatypes keep the historical 20 media type behavior. Image exports without attestations default to Docker media types. Image exports with attestations use OCI media types because attestations require them.
10
10 represents the v0.13.0 and v0.14.0 historical path.
Compared to 20, 10 differs in two ways:
- in the git-backed copied-content layer, non-executable regular files from the
gitsource are stored as mode0666instead of0644 compression=zstdproduces different historical artifacts
The git-backed layer boundary was introduced by commit 6493fd064ceece1d29f3e61aa73a531502f4795c ("git: ensure exec option is propagated to child git clis").
The old pre-v0.15.0 zstd artifacts crossed two independent historical changes:
a2a440feb58388e6873082900a5c0b35746b18f3("vendor: update klauspost/compress to v1.17.9")6493fd064ceece1d29f3e61aa73a531502f4795c("git: ensure exec option is propagated to child git clis")
The first changed zstd-compressed layer bytes. The second changed the git-backed copied-content layer metadata described above.
Because the currently supported historical backfill for 10 only covers the git-backed artifact difference, BuildKit currently rejects compression=zstd with compatibility-version=10 instead of claiming full reproduction of the old zstd output.