Container base images
When migrate --format container emits a Containerfile, the FROM line is not a guess. It is
the provenance'd base image the Translation Index records for the target release (FR-22 C2), read
from the container_base_image table (sql/43, extended for openSUSE Leap by sql/214) and
returned to the engine with the translation. The same value is written into the Containerfile
and recorded in the signed attestation, so a build and its attestation can never name different
bases.
Two rules make the FROM deterministic and auditable:
- Family and tag only, never a digest. A pinned
@sha256:…digest is a session pin; the tag is the stable reference a registry resolves and verifies at pull time. The table'sCHECKconstraint structurally refuses a digest. - Absence is a refusal, not a placeholder. A target with no measured row makes
migrate --format containerrefuse atFROMrather than write a placeholder. Red Hat Enterprise Linux is unseeded on purpose — UBI is not RHEL, and a RHEL image lives behind a subscription registry this Index has not fetched — and SUSE Linux Enterprise is unseeded until its BCI catalog is measured.
The targets below are the ones carrying a measured base image today. The table is generated by
scripts/check-container-base-images-docs.py and bound by CI: change a row in sql/43,
sql/214 or sql/218, or the engine's list of left-out packages, and regenerate, or the
pipeline fails.
The third column lists the packages the engine leaves out of the dnf install line for that
image. Some base images ship a reduced variant of a package (curl-minimal, coreutils-single)
that provides the full package's name and conflicts with it, so asking dnf for the full package
aborts the build. The engine leaves those names out and says so in a comment in the
Containerfile. verify then reads the built image's own rpm database and counts a promised
name as present when an installed package provides it, reporting name (provided by variant).
The variant measured for each image is the one in parentheses. The column is read from the table
the engine itself uses
(BASE_IMAGE_PROVIDED in engine/src/render.rs), which was measured against each dnf base
image, not derived from the release number. On an apt or zypper base image the engine leaves
nothing out.
The fourth column lists the other names the measurement found on that image: names the engine
does not leave out, where asking the image's package manager for the name does not do what was
asked. Each entry is the name, and in parentheses the installed package it meets. Unless the
entry says otherwise, asking for the name aborts the install. Oracle Linux 8 and 9 ship the
full curl, libcurl and coreutils, so there it is the reduced names that abort. Ubuntu
26.04 ships coreutils-from-uutils, and apt there refuses the other coreutils-from-*
packages. On Debian 12 apt does something else, and the entry says so: it removes the
installed package, installs the name and reports success, so the build goes green with a
package missing that the image shipped. The first install line in the Containerfile does not
stop apt from doing that. The translations in sql/ send none of the names in this column
to the target they are listed for.
In both columns nothing means the image was measured and no such name was found, and
not measured means the measurement was not run on that image. The two openSUSE Leap images
are not measured: no probe here handles zypper. What was measured is recorded per image in
container_base_probe and container_base_variant (sql/218), and an image with no record
there fails the CI check, so it cannot be printed as nothing. The measurement asks about
names that conflict with an installed package directly. A name whose dependencies conflict
with the image is not covered, on any image.
| Target | Base image (FROM) | Left out of the install line (provided by) | Not left out, and must not be asked for (the image ships) |
|---|---|---|---|
| AlmaLinux 8 | almalinux:8 | coreutils (coreutils-single), libcurl (libcurl-minimal) | nothing |
| AlmaLinux 9 | almalinux:9 | coreutils (coreutils-single), curl (curl-minimal), libcurl (libcurl-minimal) | nothing |
| AlmaLinux 10 | almalinux:10 | coreutils (coreutils-single), libcurl (libcurl-minimal) | nothing |
| Amazon Linux 2 | amazonlinux:2 | nothing | libcrypt-nss (libcrypt) |
| Amazon Linux 2023 | amazonlinux:2023 | coreutils (coreutils-single), curl (curl-minimal), gnupg2 (gnupg2-minimal), libcurl (libcurl-minimal) | amazon-linux-repo-s3 (amazon-linux-repo-cdn) |
| Debian 12 | debian:12 | nothing | libelogind0 (libsystemd0): apt removes the package the image ships and reports success |
| Debian 13 | debian:13 | nothing | nothing |
| Oracle Linux 8 | oraclelinux:8 | nothing | coreutils-single (coreutils), libcurl-minimal (libcurl) |
| Oracle Linux 9 | oraclelinux:9 | nothing | coreutils-single (coreutils), curl-minimal (curl), fips-provider-next (openssl-fips-provider-so), libcurl-minimal (libcurl) |
| Rocky Linux 8 | rockylinux/rockylinux:8 | coreutils (coreutils-single), libcurl (libcurl-minimal) | nothing |
| Rocky Linux 9 | rockylinux/rockylinux:9 | coreutils (coreutils-single), curl (curl-minimal), libcurl (libcurl-minimal) | nothing |
| Rocky Linux 10 | rockylinux/rockylinux:10 | coreutils (coreutils-single), libcurl (libcurl-minimal) | nothing |
| Ubuntu 18.04 | ubuntu:18.04 | nothing | nothing |
| Ubuntu 20.04 | ubuntu:20.04 | nothing | nothing |
| Ubuntu 22.04 | ubuntu:22.04 | nothing | nothing |
| Ubuntu 24.04 | ubuntu:24.04 | nothing | nothing |
| Ubuntu 26.04 | ubuntu:26.04 | nothing | coreutils-from-busybox (coreutils-from-uutils), coreutils-from-gnu (coreutils-from-uutils), coreutils-from-toybox (coreutils-from-uutils) |
| openSUSE Leap 15 | opensuse/leap:15 | not measured | not measured |
| openSUSE Leap 16 | opensuse/leap:16 | not measured | not measured |
Minimal base images
The Index also records, per target, the distribution's minimal image, the package manager that
image ships, and the package names that must stay out of an install line on it. translate --json shows them for the target as container_minimal_base. migrate --format container
writes the image above and does not start from them. They are here for thin images, which
start from the minimal image and install only the packages one workload needs, with the
image's own package manager. Thin images are not released: verify does not grade one yet.
A target is in this table only if its catalog publishes a minimal image. Ubuntu, Amazon Linux and openSUSE Leap publish none under the image name used above, and have no row. Red Hat Enterprise Linux has none for the same reason it has no row above.
The last column is measured in each image with its own microdnf and does not follow from
the table above. A minimal image is not always the smaller one: Rocky Linux 9's full image
ships curl-minimal and libcurl-minimal, and its minimal image ships the full curl and
libcurl, so on the minimal image it is the -minimal names that cannot be installed. Each
entry is the name, and in parentheses the package the image ships in its place. Asking for
the name aborts the install, with one exception that is marked: on the three EL8 images
microdnf does not abort, it installs the i686 build of the package beside the x86_64 one
and reports success.
nothing and not measured mean what they mean in the table above. The two Debian images
were measured with apt, and their entry is marked the same way: apt does not abort, it
removes the package the image ships.
The table is generated from sql/217 and sql/218 by the same script and bound by the same
CI check.
| Target | Minimal base image | Package manager | Kept out of the install line (the image ships) |
|---|---|---|---|
| AlmaLinux 8 | almalinux:8-minimal | microdnf | coreutils (coreutils-single), libcurl-minimal (libcurl): the i686 build is installed beside it |
| AlmaLinux 9 | almalinux:9-minimal | microdnf | coreutils (coreutils-single), curl (curl-minimal), libcurl (libcurl-minimal) |
| AlmaLinux 10 | almalinux:10-minimal | microdnf | coreutils (coreutils-single), libcurl (libcurl-minimal) |
| Debian 12 | debian:12-slim | apt | libelogind0 (libsystemd0): apt removes the package the image ships and reports success |
| Debian 13 | debian:13-slim | apt | nothing |
| Oracle Linux 8 | oraclelinux:8-slim | microdnf | coreutils (coreutils-single), libcurl-minimal (libcurl): the i686 build is installed beside it |
| Oracle Linux 9 | oraclelinux:9-slim | microdnf | coreutils (coreutils-single), curl-minimal (curl), fips-provider-next (openssl-fips-provider-so), libcurl-minimal (libcurl) |
| Rocky Linux 8 | rockylinux/rockylinux:8-minimal | microdnf | coreutils (coreutils-single), libcurl (libcurl-minimal): the i686 build is installed beside it |
| Rocky Linux 9 | rockylinux/rockylinux:9-minimal | microdnf | coreutils (coreutils-single), curl-minimal (curl), libcurl-minimal (libcurl) |
| Rocky Linux 10 | rockylinux/rockylinux:10-minimal | microdnf | coreutils (coreutils-single), libcurl-minimal (libcurl) |
For how a target spec like rocky9 expands to one of these releases, see
Supported targets. For where the FROM is recorded in the attestation, see
Parity Attestation.
Operator overrides and digest pinning
The Index names each image by tag. migrate pins it by digest when it writes the
Containerfile: it asks the image's registry what the tag names at that moment and writes
FROM with that digest. See
Base image per target for what is asked, what is
recorded and what happens when the registry does not answer. The tables above are the tags;
no digest is stored in the Index.
Whether an operator can override the base image is still deferred to an owner decision. The
trade-offs are written up in docs/container-base-image-override-options.md.