Lesson
Reproducible Releases and Project Maintenance
How to turn a working Yocto build into a release that can be rebuilt, patched, and maintained later
A development build proves that the system can be built today.
A release proves something more useful: that you know exactly what you built, can explain where it came from, and can rebuild or patch it later when somebody finds a bug in the field. Those are different standards.
This is where Yocto stops being just a build system and becomes part of the product maintenance process. That does not mean drowning the project in paperwork. It means keeping enough information that an engineer can recreate the release without becoming an archaeologist.
A Successful Build Is Not a Release
It is very easy to get a clean image from a developer machine and treat it as a release candidate. The image boots. The application runs. Everyone is relieved. This is exactly the moment when you need to be slightly suspicious.
A development build may have:
- layers checked out on branch heads
- recipes using moving source revisions
- local changes that were never committed
- downloads that only exist on one developer machine
- build configuration hidden in
local.conf - patches sitting in a vendor layer
- artifacts with no clear version or source record
None of that means the build is bad. It means the build is not yet controlled enough to be a product release.
Pin the Layer Revisions
Your build depends on the metadata in every enabled layer. That includes OpenEmbedded-Core, BSP layers, vendor layers, community layers, and your own project layers.
For a release, record the exact commit used for each layer.
Branch names are useful for development:
meta-openembedded: wrynose
meta-vendor-bsp: wrynose
meta-my-distro: main
But branch names move. A branch tells you which stream you followed, not the exact metadata you released.
For release work, you want commit pinning:
meta-openembedded: 4c1a...f92
meta-vendor-bsp: 91ab...772
meta-my-distro: 6f0d...e14
If you use kas, repo manifests, submodules, or another project setup tool, the
same rule applies: the release should point at exact revisions, not just branch
names. Branch names are for humans. Commits are for rebuilding the thing.
Pin Recipe Source Revisions
Recipes that fetch from Git should normally use SRCREV.
For example:
SRC_URI = "git://github.com/example/hello-product.git;branch=main;protocol=https"
SRCREV = "0123456789abcdef0123456789abcdef01234567"
S = "${UNPACKDIR}/git"
The branch still matters because it tells the fetcher where to look, but the
commit is what pins the source. That means a later rebuild fetches the same
source revision even if main has moved.
This was introduced in Creating Yocto Recipes.
For production maintenance, the important point is not just that SRCREV exists.
The important point is that SRCREV is part of the release definition.
Be Careful with AUTOREV
AUTOREV tells BitBake to fetch the latest revision from the selected branch.
You may see something like:
SRCREV = "${AUTOREV}"
This can be useful during active development when you deliberately want every build to pick up the latest application code. It is generally a poor production release choice because the same recipe metadata can fetch different source code tomorrow.
If you used AUTOREV while iterating, replace it before release and record the
commit you actually tested.
Record the Exact Build Configuration
The source and layer revisions are only part of the story.
You also need to know which configuration was used:
MACHINEDISTRO- image recipe
- enabled layers
- important distro and image features
- package format and image output types
- any release-specific configuration
- build host or container details
Do not hide product decisions in a developer’s local.conf. Earlier in the
course, we separated BSP, distro, image, and application metadata. That
separation pays off during release work because the project configuration is in
version control rather than in someone’s build directory.
For local experiments, local.conf is fine. For release policy, it is not where
the truth should live.
Use Release Tags
Tag your project layers when you release.
For example:
git tag product-2.3.0
git push origin product-2.3.0
The exact naming scheme is a project decision. What matters is that the release tag maps to the project metadata used to build the shipped image.
If you have several internal layers, tag them consistently or use a manifest that records their exact commits. A release should not depend on memory or a message in a chat channel. Those have a shorter half-life than most of us would like.
Keep Project Layers Separate
The project structure from the setup lesson was not just tidiness:
layers/
third-party/
meta-openembedded-core
meta-vendor
project/
meta-my-bsp
meta-my-distro
meta-my-software
Keep that boundary in production projects.
Third-party and vendor layers should be treated as inputs. Your project layers carry your product decisions, local patches, image recipes, distribution policy, and application recipes.
The reason is simple: you will update vendor and upstream layers eventually. If your changes are mixed directly into those layers, every update becomes a manual reconstruction exercise.
Avoid Direct Modifications to Vendor Layers
If you need to change a vendor recipe, use a .bbappend from your own layer.
If you need to carry a patch, keep the patch in your layer and add it through
SRC_URI.
If you need to replace a vendor decision, do it deliberately from your own BSP, distro, or application layer.
This is not because vendor layers are sacred. It is because your future upgrade work needs a clean separation between:
- what came from the vendor or community
- what your product changed
- why the change exists
The append workflow is covered in Changing Yocto Recipes. In production maintenance, that workflow becomes one of the main tools for keeping local customisation visible.
Maintain Local Patches
Patches are not a dumping ground for mysterious fixes.
For every local patch you carry, try to know:
- what problem it fixes
- which upstream version it was written against
- whether it has been sent upstream
- whether it should be dropped after an update
- who reviewed it
Patch drift is one of the normal costs of maintaining an embedded Linux product. The cost is much lower when patches are small, clearly named, and stored in the layer that owns the reason for the change.
For example:
meta-my-bsp/
recipes-kernel/
linux-vendor/
linux-vendor_%.bbappend
linux-vendor/
0001-myboard-fix-reset-gpio-polarity.patch
That tells the next engineer that the patch is board-related, not general distro policy or application code.
Product Versioning and Build Metadata
A released image should identify itself.
At minimum, decide how the product records:
- product version
- build or release identifier
- distro version
- image name
- machine name
- source or manifest revision
Some projects write this into /etc/os-release, a product-specific file under
/etc, or an application-visible version endpoint. The exact mechanism depends
on the product, but the principle is the same: when a device comes back from the
field, you should be able to identify what it is running.
Yocto gives you variables such as DISTRO_VERSION, image names, and build
metadata hooks, but the product still needs a policy for how those values are
set and reviewed.
What to Retain for an Old Release
If you want to reproduce an old release, keep enough to rebuild and enough to explain the release.
That normally includes:
- the final image artifacts
- kernel, bootloader, device tree, and firmware artifacts
- generated SDKs, if released to application teams or customers
- build logs
- license manifests and SBOM output
- the exact layer commits or manifest
- source archives or a reliable source mirror
- local patches
- release notes
- known vulnerability decisions
- build host or container definition
You do not necessarily need to keep every temporary build directory forever. You do need to keep enough inputs and evidence that the release is not just a file that happened to exist on a CI server one Friday afternoon.
Yocto Release Cadence and LTS Releases
The Yocto Project has regular releases, and some releases are Long Term Support releases. Wrynose, the Yocto Project 6.0 release series, is an LTS release.
For products, LTS releases are often the sensible base because they give you a longer maintenance window. That does not mean you can ignore updates. It means you have a more stable branch for security fixes and product maintenance.
When planning a product, decide:
- which Yocto release series you are targeting
- how long the product must be maintained
- when you will take updates within that release series
- when you will migrate to a newer series
Migration is real engineering work. Budget for it.
Migration Guides and LAYERSERIES_COMPAT
When moving between Yocto release series, read the migration guides. Do not just change branch names and hope. Hope is not a migration strategy, even if it has excellent adoption across the industry.
Layer compatibility is declared in layer.conf:
LAYERSERIES_COMPAT_meta-my-distro = "wrynose"
This tells BitBake which Yocto release series the layer is expected to work with. When you migrate, update this only after the layer has actually been tested and fixed for the new release.
Do not treat LAYERSERIES_COMPAT as a magic compatibility switch. It is a
statement of support, not a spell.
Summary
A production Yocto release is a controlled build, not just a successful one.
The main ideas from this lesson are:
- pin layer revisions for releases
- pin recipe sources with
SRCREV - avoid
AUTOREVin production releases - record the exact build configuration
- tag or manifest project layer revisions
- keep project changes out of vendor layers
- maintain local patches deliberately
- version the product and record build metadata
- retain enough inputs and artifacts to reproduce old releases
- use Yocto release cadence, LTS releases, migration guides, and
LAYERSERIES_COMPATas part of the maintenance plan
Check your understanding
Quick quiz: reproducible releases
Check the difference between a development build and a maintainable release.