# Reproducible Builds in Yocto - Build Inputs Control

Table of Contents

After establishing a controlled and reproducible build environment, the next step is to ensure that all inputs to the build process are deterministic. In Yocto, this means explicitly controlling what is being built, where it comes from, and how it is interpreted by the build system.

A reproducible build cannot rely on floating revisions, implicit defaults, or external resources that may change over time. Every source, layer, and tool involved in the build must be fixed, traceable, and version-controlled.

This article focuses on the mechanisms provided by Yocto to lock down build inputs, laying the groundwork for verifiable and repeatable builds.

Block with SRCREV

In Yocto, many recipes fetch their sources directly from version control systems such as Git. When a recipe uses a floating reference (for example a branch name like main or master), the exact source code used for the build may change over time, even if the recipe itself has not been modified.

This is fundamentally incompatible with reproducible builds.

To avoid this, all Git-based recipes must use a fixed commit hash via the SRCREV variable.

Terminal window
SRC_URI = "git://example.com/project.git;branch=main"
SRCREV = "3f2a9c1e6c4b8d0a9e7b1f4c2a6d8e9f12345678"

By pinning SRCREV to a specific commit:

  • the exact source content is unambiguously defined,
  • historical builds can be reproduced at any time,
  • changes in upstream repositories cannot silently affect the build.

For project-specific recipes, SRCREV should always be defined explicitly. For third-party layers, it is important to verify that floating revisions are not used implicitly or overridden elsewhere in the build configuration.

A good practice is to periodically review the build metadata and ensure that no recipe relies on AUTOREV or branch-based revisions, as these break reproducibility by design.

Avoid using AUTOREV

Yocto provides the AUTOREV keyword as a convenience mechanism to always fetch the latest available revision from a source control repository. While this can be useful during early development or experimentation, it is fundamentally incompatible with reproducible builds.

When AUTOREV is used, the exact source revision is resolved at build time, meaning that:

  • the same recipe may fetch different source code at different times,
  • builds performed days or weeks apart can produce different binaries,
  • historical builds cannot be reliably reproduced.

From a reproducibility standpoint, AUTOREV removes determinism from the build process and should therefore be considered unsafe.

For production builds, releases, and CI pipelines, AUTOREV must always be replaced with an explicit and immutable SRCREV value pointing to a specific commit hash. This guarantees that the source code used for the build is fully defined and traceable.

A useful rule of thumb is:

If a build must be reproducible, AUTOREV must not appear anywhere in the metadata.

Enforcing this rule early in the project helps prevent subtle and hard-to-debug issues later in the development lifecycle.

Block layers and build instruments

A Yocto build is defined not only by individual recipes, but by the exact set of layers included and their respective revisions. Even if all recipes use fixed SRCREV values, changes in a layer itself can alter build behavior in subtle and sometimes unexpected ways.

For this reason, layer versions must be treated as first-class build inputs.

Each layer used in the project should be:

  • checked out at a specific Git commit or tag,
  • tracked explicitly in version control,
  • updated only through a controlled and reviewable process.

Whether layers are managed using repo, kas, or custom scripts, the principle remains the same: layer revisions must never float.

In addition to layers, the build depends on various instruments such as:

  • native build tools,
  • cross-compilers,
  • helper scripts provided by layers or by the Yocto build system.

These instruments are indirectly defined by the selected Yocto release and the layer revisions. By fixing both, the toolchain and auxiliary tools become deterministic, removing another major source of variability.

Example: locking layer revisions with repo

When managing multiple Yocto layers, the repo tool provides a simple and explicit way to lock each layer to a specific Git revision using a manifest file.

A minimal repo manifest might look like the following:

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote name="poky"
fetch="https://git.yoctoproject.org"/>
<remote name="vendor"
fetch="https://github.com/vendor"/>
<remote name="project"
fetch="https://github.com/mycompany"/>
<project name="poky"
path="sources/poky"
remote="poky"
revision="scarthgap"/>
<project name="meta-vendor-bsp"
path="sources/meta-vendor-bsp"
remote="vendor"
revision="9a3f6d8c2e4b1a7f5c9e0d123456789abcdef01"/>
<project name="meta-myproject"
path="sources/meta-myproject"
remote="project"
revision="4c2e8b1d9f6a7c5e3a0b123456789abcdef01234"/>
</manifest>

In this example:

  • the Yocto core layer (poky) is fixed to a specific release branch,
  • the vendor BSP layer is pinned to an immutable commit hash,
  • the project-specific layer is also locked to a known revision.

Because each project entry specifies an explicit revision, the exact state of all layers is fully defined. Re-running repo sync at any point in the future will produce the same layer set, provided the repositories remain accessible.

From a reproducibility perspective, this approach offers several advantages:

  • layer updates are explicit and reviewable,
  • the toolchain and build instruments provided by the layers are indirectly locked,
  • the manifest itself becomes a reproducible artifact that can be version-controlled and tagged alongside releases.

By treating the repo manifest as part of the source code, the set of layers and instruments involved in the build becomes deterministic and auditable.

Set source date epoch

Timestamps are one of the most common sources of non-determinism in build systems. Without explicit control, binaries may embed the current build time, resulting in different outputs even when the source code is identical.

Yocto addresses this issue through the SOURCE_DATE_EPOCH variable, which defines a fixed reference timestamp used during the build process.

Example:

Terminal window
SOURCE_DATE_EPOCH = "1700000000"

When correctly set, this variable ensures that:

  • generated files use consistent timestamps,
  • build artifacts do not depend on the wall-clock time,
  • repeated builds produce bit-for-bit identical outputs.

In practice, SOURCE_DATE_EPOCH is often derived from a meaningful and documented reference, such as:

  • the timestamp of a specific Git commit,
  • the official release date of the project,
  • a fixed epoch shared across the organization.

The exact value is less important than the fact that it is explicit, stable, and reproducible.

Where to set SOURCE_DATE_EPOCH

In Yocto, SOURCE_DATE_EPOCH can be defined at different levels of the build configuration, depending on the scope you want to apply and how the project is structured.

The most common and recommended approach is to define it globally, so that it applies consistently to all recipes and build artifacts.

A global definition can be placed in the project configuration file, typically conf/local.conf or a dedicated project configuration include:

Terminal window
SOURCE_DATE_EPOCH = "1700000000"

Defining it at this level ensures that all recipes inherit the same reference timestamp, eliminating variability across the entire build.

In larger projects, it is often preferable to place this setting in a project-specific configuration file (for example conf/distro/mydistro.conf or a custom .inc file) rather than in local.conf, which is usually considered developer-specific.

Distribution-level definition

For builds that target multiple machines or environments, SOURCE_DATE_EPOCH can also be set at the distribution level:

conf/distro/mydistro.conf
SOURCE_DATE_EPOCH = "1700000000"

This approach makes the reference timestamp part of the distribution definition itself, ensuring consistency across all builds that use the same distro configuration.

Lock external dependencies

Many Yocto recipes fetch source code from external locations such as HTTP servers, mirrors, or upstream release archives. Over time, these resources may change, disappear, or be replaced, directly impacting reproducibility.

To mitigate this risk, external dependencies must be locked and verified.

Best practices include:

  • defining checksums (SRC_URI[sha256sum]) for all downloaded sources,
  • relying on trusted and controlled mirrors whenever possible,
  • archiving critical source tarballs internally.

By validating checksums, Yocto ensures that the downloaded content matches the expected source exactly. If the upstream content changes, the build will fail rather than silently producing different binaries.

For long-lived products or regulated environments, maintaining an internal source mirror is often a key requirement to guarantee long-term availability and immutability of all build inputs.


By controlling source revisions, layer versions, timestamps, and external dependencies, the build inputs become fully deterministic. At this stage, all the prerequisites for reproducible builds are in place.

In the next article, we will focus on verifying and validating reproducibility, showing how to prove that two independent builds actually produce identical outputs and how to investigate differences when they occur.

Next: Reproducible Builds in Yocto - Verification and Validation
deidlab logo

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


Yocto Reproducibility Series

Comments