7.5 KiB
Base Image Package Version Locking
Overview
This design proposes a mechanism for base container images and disk/cloud images to version-lock all installed packages, providing reproducible and secure package management for derived images and systems.
Related Work
See dnf5#833 which discusses a complementary approach using dependency lockfiles. This proposal takes a different approach focused on image-level version pinning through repository metadata.
See also bootc-tracker#59 which tracks the overall implementation of base image version locking.
Core Design
IMAGE_VERSION in os-release
Today Fedora derivative images don't usually set the (relatively new) IMAGE_VERSION variable in os-release
in this proposal they will:
NAME="Fedora Linux"
VERSION="42"
ID=fedora
VERSION_ID=42
# Version scheme the same as a "compose" today
IMAGE_VERSION=20251015.0
...
The IMAGE_VERSION represents a specific snapshot of the base image + repository state that corresponds to the exact package versions installed in the base image, plus additional packages for all default-enabled rpm-md repositories.
BASE_IMAGE_VERSION for Derived Images
Some image-based systems (like Fedora CoreOS) may want to set their own IMAGE_VERSION while still benefiting from version locking of the base packages. For these cases, DNF will also recognize BASE_IMAGE_VERSION which takes precedence over IMAGE_VERSION for repository version locking.
This allows a derived image to:
- Set
IMAGE_VERSIONto its own version scheme - Set
BASE_IMAGE_VERSIONto inherit the base image's package snapshot - Maintain independent versioning while preserving reproducible package installations
Derived images can also override the base image version by writing to /etc/dnf/vars/imageversion, which DNF will use in preference to values from os-release.
DNF Integration
Any client can request a specific version-locked snapshot by appending $imageversion.
Current URL:
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch
URL with version locking:
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever-$imageversion&arch=$basearch
The metalink service will redirect to a pinned, immutable rpm-md repository snapshot that corresponds to the exact state of packages at the time the base image was built.
Benefits
Reproducibility
Derived images and systems will install the exact same package versions as the base image, regardless of when the build occurs. This eliminates the common problem where a cached container layer contains outdated packages.
Security and Updates
When a new base image is released with updated IMAGE_VERSION, derived images automatically rebuild and pick up all security updates in a controlled manner. This provides:
- Clear update points tied to base image releases
- Ability to validate and test base image updates before adoption
- Audit trail of which package versions are in use
Supply Chain Security
The image version provides a single identifier that pins all package versions, making it easy to:
- Track exactly which packages are in a given deployment
- Coordinate security updates across fleet of systems
- Verify package integrity against known-good snapshots
Compatibility with Existing Workflows
Unlike lockfile approaches, this design:
- Requires no changes to Containerfiles/Dockerfiles
- Works transparently with existing package management commands
- Applies automatically to all derivative images
Implementation Considerations
Repository Infrastructure
We propose that these snapshots are NOT stored in the donated "mirror network" but are managed by dedicated cloud-native storage backed by Fedora project sponsors.
In general an important thing to preserve here is that if a container image (or disk image) is published, the corresponding packages must be available as well. Hence, what will likely be the best implementation path is to publish the rpm-md snapshots before publishing images, but not mark it as the "latest".
But the system will work most reliably if the two are co-located; e.g. container images hosted in quay.io could have content served from the same infrastructure.
One possible implementation approach is to extend archive-repo-manager, which already handles repository archival and versioning. Each time repodata updates, it could also be copied to a versioned name that remains immutable. However, this is just one potential implementation path - other approaches such as storing RPMs as OCI artifacts in a registry are also viable.
DNF Changes
DNF needs to:
- Check for
imageversionin the following priority order:/etc/dnf/vars/imageversion(highest priority, allows derived images to override)BASE_IMAGE_VERSIONfrom/usr/lib/os-releaseIMAGE_VERSIONfrom/usr/lib/os-release
- Substitute
$imageversionin metalink URLs when a version is found - Provide clear messaging about which version is being used
Override Mechanism
There are several ways to override or disable version locking:
- One-time override: Use
dnf --setvar imageversion=latestto bypass version locking for a single command - Persistent override: Write a different version to
/etc/dnf/vars/imageversion - Complete removal: Remove
$imageversionfrom the metalink URL in repository configuration files
The metalink service may also support the alias latest as a value for $imageversion, allowing users to override the variable without editing URLs.
Mixed Repository Sources
Third-party and local repositories will not include $imageversion in their URLs by default, as they are not part of the versioned snapshot infrastructure. This is expected behavior - only official Fedora repositories that participate in the snapshot system will use $imageversion.
When DNF processes repository configurations:
- Repositories with
$imageversionin their metalink/baseurl use the locked snapshot - Repositories without
$imageversionuse their default behavior (typically latest packages)
This allows mixing locked base packages with latest versions from third-party sources.
Error Handling
If a repository snapshot becomes unavailable (404 or similar error), DNF will fail hard rather than falling back to the latest packages. This is intentional - silently falling back would break the reproducibility guarantee and could introduce unexpected package versions.
Users must explicitly modify repository configurations to disable version locking if a snapshot is unavailable.
Snapshot Lifecycle
Package snapshots are lifecycle-bound to the base images that reference them. A snapshot must be retained at least as long as any base image using that IMAGE_VERSION remains supported or available.
For example:
- Base image
fedora-bootc:42withIMAGE_VERSION=20251015.0is published - The corresponding snapshot
fedora-42-20251015.0must remain available - When the base image reaches end-of-life or is removed from registries, the snapshot may be retired
- In practice, snapshots should be retained longer than images to support derived images and cached layers
This binding ensures that any published image can always install its matching package versions, maintaining the reproducibility guarantee throughout the image's lifecycle.