base-images/docs/todo/base-image-version-locking.md
Colin Walters 25747256ea BIPVL: New design doc
Signed-off-by: Colin Walters <walters@verbum.org>
2025-11-14 16:55:35 -05:00

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.

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_VERSION to its own version scheme
  • Set BASE_IMAGE_VERSION to 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:

  1. Check for imageversion in the following priority order:
    • /etc/dnf/vars/imageversion (highest priority, allows derived images to override)
    • BASE_IMAGE_VERSION from /usr/lib/os-release
    • IMAGE_VERSION from /usr/lib/os-release
  2. Substitute $imageversion in metalink URLs when a version is found
  3. Provide clear messaging about which version is being used

Override Mechanism

There are several ways to override or disable version locking:

  1. One-time override: Use dnf --setvar imageversion=latest to bypass version locking for a single command
  2. Persistent override: Write a different version to /etc/dnf/vars/imageversion
  3. Complete removal: Remove $imageversion from 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 $imageversion in their metalink/baseurl use the locked snapshot
  • Repositories without $imageversion use 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:42 with IMAGE_VERSION=20251015.0 is published
  • The corresponding snapshot fedora-42-20251015.0 must 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.