Image Configuration Reference

Complete reference for properties.yml configuration and image settings

Complete reference for configuring container images via properties.yml.

properties.yml Overview

Each image can have custom settings via a properties.yml file. This reference documents all available configuration options.

Minimal example:

---
rpm_packages:
  all:
    - nginx
main_package: nginx
tags:
  - value: latest

Complete structure:

---
# Distro configuration (see Image Variants section)
# distros: [hummingbird]  # Override default distros (use when package unavailable in some distros)

# Image variants (see Image Variants section)
# Use additional_variants to extend defaults, or variants to override completely
additional_variants: [fpm, fpm-builder]  # Adds to default variants
# variants: [default, builder]  # Alternative: completely replaces defaults

# Container runtime configuration (see Container Configuration section)
user: default  # Optional: 'default' for default_user (65532), or literal user ID/name

# Package management (see Package Management section)
rpm_packages:
  build-deps: [...]
  all:
    - package-name                   # All architectures
    - name: arch-specific-package    # Arch-specific (see Package Management)
      arches:
        only: x86_64
  <distro>: [...]
  <variant>: [...]
additional_repos: [...]
allow_fedora_repos: true  # Optional: allow Fedora repos for hummingbird images (temporary workaround)

# Build configuration (see Build Configuration section)
hermetic: false
build_from_source: true
prefetch_gomod_path: xcaddy-upstream

# Versioning (see Versioning section)
main_package: package-name
# version_package:            # Optional: override package for version lookup per variant/distro
#   fips: package-fips-name   # Example: variant-specific package name

# Testing configuration (see Testing Configuration section)
reverse_dependency_tests: false  # Optional: disable for images like curl

# Release configuration (see Release Configuration section)
repository: image-name           # Required: Quay.io repository name
# <reasoning for stream choice>
stream: "latest"                 # Required: version series identity for the image catalog
application_category: "Storage"  # Required: Pyxis application category
# supported-by: community        # Optional: community-supported image (default: Red Hat supported)

# Image metadata (see Image Metadata section)
summary: "Short one-liner description"   # Required when adding labels
description: >-                          # Required when adding labels
  Short paragraph describing the image.
  1-2 sentences about key features.
url: "https://upstream-project.org"      # Required when adding labels

tags:
  - value: latest
  - value: '{{ package_major_version("package-name") }}'
    label: io.hummingbird-project.major-version

Image Metadata

Images can declare metadata fields for container image labels:

summary

  • Type: String
  • Required: When adding image labels (currently caddy only, expanding to all)
  • Length: ~40-80 characters (one-liner)

Short phrase describing what the image is. Used in table/list views and catalog cards.

Do not start with the image name – the name is always visible from context (UI headings, name label, inspect output).

Example: "Web server with automatic HTTPS"

description

  • Type: String (use >- YAML scalar for multi-line readability)
  • Required: When adding image labels
  • Length: ~100-250 characters (1-2 sentences)

Short paragraph for card views, podman inspect, and Kubernetes UIs (io.k8s.description). Describes what the software is and its key distinguishing features.

Use >- (folded scalar) in YAML to keep the value readable across multiple lines – it folds to a single-line string. Do not use | (literal block) as embedded newlines break label syntax.

Avoid embedded double quotes and backslashes – values are emitted as-is in Containerfile LABEL instructions with no escaping.

The same description value is used when generating the image README: the readme_description() macro in README.md.j2 templates renders it as the introductory paragraph. Do not duplicate this text in the template; use {{ readme_description() }}.

Example:

description: >-
  Extensible server platform with automatic HTTPS by default.
  Provides HTTP/3, reverse proxying, load balancing, and static
  file serving with minimal configuration.

url

  • Type: String (URL)
  • Required: When adding image labels

URL for the upstream project homepage. Used in org.opencontainers.image.url and the Conforma url label.

Example: "https://caddyserver.com"

Image Variants

Images can have multiple variants (e.g., default and builder):

  • default: Minimal runtime environment
  • builder: Includes development tools and build dependencies
  • custom: Define custom variants in properties.yml

Each variant gets its own:

  • Containerfile in images/<name>/<variant>/Containerfile
  • RPM lockfiles in images/<name>/<variant>/rpms
  • Build pipeline and release tags (non-default variants get -<variant> suffix)

variants

  • Type: Array of strings
  • Default: [default, builder]
  • Description: List of image variants to generate. Completely overrides the default variants. Each variant gets its own Containerfile, lockfiles, build pipeline, and release tags. Non-default variants get a -<variant> suffix in their image tags.
  • Example: [default] (CI images that only need the default variant)
  • Note: Prefer additional_variants when extending defaults; use variants only when you need to exclude default variants.

additional_variants

  • Type: Array of strings or objects
  • Default: None
  • Description: Additional variants to add to the base variants. The base is variants if specified, otherwise the default variants. Use this when you want to extend without repeating.

Simple format (string array):

additional_variants: [fpm, fpm-builder]  # Adds FPM variants to base variants for all distros

Object format with distro restrictions:

Each variant can be an object with name and optional distros fields to restrict which distros the variant is built for:

additional_variants:
  - name: fips
    distros: [hummingbird]  # Only build fips variant for hummingbird distro
  - name: fpm               # Simple string still works in array
  - name: fpm-builder
    distros: [hummingbird, rawhide]  # Build for specific distros

The distros field supports glob patterns:

additional_variants:
  - name: fips
    distros: ["*bird"]      # Matches hummingbird
  - name: special
    distros: ["raw*"]       # Matches rawhide

When to use distro restrictions:

Use distro restrictions when a variant requires packages or features only available in specific distros. For example, FIPS variants may only be available for Hummingbird where FIPS-validated packages are present

variant_descriptions

  • Type: Object (string-to-string mapping)
  • Required: When image has description and defines additional_variants with non-default base specializations
  • Description: Maps base variant names to human-readable descriptions. These descriptions are emitted as the io.hummingbird-project.variant.description label on each container image. The global description for default is defined in images/variables.yml; image-specific entries add descriptions for bases like fpm, runtime, or openssl. Modifier display (builder, fips) is handled by catalog consumers using the boolean variant labels, not by these descriptions.

Writing guidelines:

  • Describe the variant holistically — what the image variant is, not just what packages it adds on top of default. Someone reading the description with no surrounding context (e.g., via podman inspect) should understand the variant’s identity.
  • Use a noun phrase (2-6 words), not a full sentence.
  • Do not start with the image name (the image context is already provided by the image name label).
  • Avoid the word “runtime” in the default description (the OpenJDK runtime variant is a separate base, and OpenJDK’s default includes the full JDK).
  • Avoid expanding abbreviations redundantly (e.g., “JRE runtime” is redundant because JRE already means “Java Runtime Environment”).
  • Prefer functional descriptions (what capability the variant provides) over repeating the variant name as a product name.

Example (PHP image with FPM base):

variant_descriptions:
  fpm: "PHP FastCGI process manager"

Example (OpenJDK image with runtime base):

variant_descriptions:
  runtime: "Headless Java runtime"

distros

  • Type: Array of strings
  • Default: [rawhide, hummingbird] (from images/variables.yml)
  • Description: List of distros to build for this image. Overrides the global default_distros. Use this to disable a distro for images where a required package is not available in that distro.
  • Example: [hummingbird] disables Rawhide builds for this image
  • Note: Each enabled distro creates a full set of variants, so the final combinations are distros × variants.

Container Configuration

user

  • Type: String, integer, or object (per-variant dict)
  • Required: No (defaults to default)
  • Description: Specifies which user the container runs as. The USER directive is rendered via the {{ set_user() }} macro at the end of Containerfile templates. The special (and default) value default maps to the default_user variable (UID 65532). For user: default, the {{ final_stage() }} macro sets HOME=/tmp and WORKDIR /tmp early in the image to ensure unprivileged users have a writable default environment. Images can override WORKDIR later in their Containerfile.j2. For builder variants with user: default, it additionally sets ENV CONTAINER_DEFAULT_USER=65532.

Same user for all variants:

user: default
user: root  # Root user
user: postgres  # Literal username

Per-variant configuration:

Use when different variants need different users:

user:
  default: root
  builder: root
  fpm: default
  fpm-builder: default

Package Management

Packages are defined in properties.yml under rpm_packages:

rpm_packages:
  build-deps:             # Build-time dependencies (installed in builder layer, not in final
                          # image). Included in lockfiles for hermetic builds.
    - golang              # Example: Go compiler for building from source
    - rpm-build           # Example: Tools for building RPMs during image construction

  all:                    # Included in all variants (Containerfiles and lockfiles)
    - coreutils-single
    - your-main-package
    - name: grub2-efi-x64           # Arch-specific: only on x86_64
      arches:
        only: x86_64
    - name: grub2-efi-aa64          # Arch-specific: only on aarch64
      arches:
        only: aarch64

  hummingbird:            # Included in all Hummingbird variants (distro-level key)
    - versioned-package-1.2

  rawhide:                # Included in all Rawhide variants (distro-level key)
    - unversioned-package

  builder:                # Included only in "builder" variant
    - compiler-packages
    - debug-tools

  custom-variant:         # Included only in "custom-variant" variant
    - variant-specific-packages

Package entry format

Each entry in an rpm_packages list can be either a string (installed on all architectures) or an object with architecture constraints:

String entry (all architectures):

- coreutils-single

Object entry (arch-specific):

- name: grub2-efi-x64
  arches:
    only: x86_64            # Install only on x86_64
- name: some-package
  arches:
    not: aarch64             # Install on all architectures except aarch64

The arches.only and arches.not fields accept either a single string or a list of strings. Supported architectures: aarch64, x86_64.

How arch-specific packages work:

  1. Lockfiles: Arch-specific entries are passed to rpm-lockfile-prototype using its native per-arch package format. The lockfile resolver only includes matching packages for each architecture.
  2. Containerfiles: Arch-specific packages are installed via a case statement on TARGETARCH, so the correct packages are installed during multi-arch builds.

rpm_packages.build-deps

  • Type: Array of strings or arch-specific objects
  • Description: Build-time dependencies needed during image construction. Automatically installed in the builder layer (not in the final image) and included in lockfiles for hermetic builds. Use for compilers, build tools, and other packages needed only during the build process.

rpm_packages.all

  • Type: Array of strings or arch-specific objects
  • Description: Packages included in all variants, in both Containerfiles and lockfiles

rpm_packages.<distro>

  • Type: Array of strings or arch-specific objects
  • Description: Packages included in all variants for the specified distro. Useful when different distros ship different package names (e.g. versioned vs unversioned) and the packages should apply to every variant within that distro.

rpm_packages.<variant>

  • Type: Array of strings or arch-specific objects
  • Description: Packages included only in the specified variant, regardless of distro

additional_repos

  • Type: Array of strings (repository filenames)
  • Default: None
  • Description: Additional yum repository files from yum-repos/ to include in the RPM lockfile generation. These repos are added to the variant-specific repos (see [Global Variables Reference
  • Example: [konflux-ci-rpm-lockfile-prototype-main-fedora-rawhide.repo]
  • Usage: Only needed for packages not available in standard Fedora repos or variant-specific repos

allow_fedora_repos

  • Type: Boolean
  • Default: false
  • Description: Allow Fedora repositories during lockfile generation for Hummingbird production images. By default, production Hummingbird images exclude Fedora repos to ensure packages come only from the Hummingbird repository. Set to true to temporarily allow Fedora repos when required packages are not yet available in the Hummingbird repository.
  • Example: true
  • Usage: Typically used temporarily for packages not yet built for Hummingbird (e.g., .NET runtime on x86_64, OpenJDK packages). Should be removed once packages are available in the Hummingbird repository.
  • Note: CI images (in ci/images/) always have access to Fedora repos regardless of this setting, as they require fedora-gpg-keys for signature validation.

Hermetic Builds

For hermetic builds (enabled by default), all packages from:

  • rpm_packages.build-deps - included in lockfiles and Containerfiles (builder layer only)
  • rpm_packages.all - included in lockfiles and Containerfiles (final image)
  • rpm_packages.<distro> - included in lockfiles and Containerfiles for all variants of a distro
  • rpm_packages.<variant> - included in lockfiles and Containerfiles for matching variants
  • default_rpm_packages.builder - automatically added for builder variants (see Global Variables Reference - default_rpm_packages)

are added to rpms.lock.yaml and prefetched using cachi2 for offline builds.

Build-time vs Runtime Packages:

  • rpm_packages.build-deps: Installed in the builder layer only (not in final image)
  • rpm_packages.all: Installed in ${NEWROOT} (the final image)
  • rpm_packages.<distro>: Installed in ${NEWROOT} for all variants of the specified distro
  • rpm_packages.<variant>: Installed in ${NEWROOT} for the specified variant

Build Configuration

hermetic

  • Type: Boolean
  • Default: true
  • Description: Controls whether the Konflux build pipeline passes HERMETIC=true to the buildah task, enabling hermetic (offline, network-isolated) builds with prefetched dependencies.
  • Important: Images with hermetic: false are blocked from release by the Conforma hermetic_task policy. Only use hermetic: false for non-production images in ci/images/.

build_from_source

  • Type: Boolean
  • Default: false
  • Description: Controls version extraction in the package_version() macro. When true, extracts version from .gitmodules for images built from git submodules. When false, extracts version from RPM package metadata. Does not control whether source code is compiled in the Containerfile.

prefetch_gomod_path

  • Type: String (path relative to image directory)
  • Default: None
  • Description: Path to a Golang project directory (typically a git submodule) for prefetching Go modules via cachi2. Enables hermetic builds for Go projects without a vendor tree. The path is relative to the image directory (e.g., xcaddy-upstream for images/xcaddy/xcaddy-upstream).
  • Example: xcaddy-upstream
  • Usage: Only needed for images that build Go projects from source

Versioning

main_package

  • Type: String
  • Description: Specifies the main package for version labeling

For RPM-based images:

Set to the package name. Version is extracted from RPM package metadata.

main_package: nginx

For source-built images:

Set to the submodule path. Version is extracted from the branch field in .gitmodules.

main_package: images/xcaddy/xcaddy-upstream
build_from_source: true

Example .gitmodules entry:

[submodule "images/xcaddy/xcaddy-upstream"]
    path = images/xcaddy/xcaddy-upstream
    url = https://github.com/caddyserver/xcaddy
    branch = v0.4.5

version_constraints

  • Type: String (glob pattern)
  • Default: None (no constraints)
  • Description: Defines a version constraint pattern to prevent unwanted major or minor version updates. The constraint applies to all distros for this image. Constraints are validated during make check, and builds fail if VERSION files violate the constraint. Primarily used for multi-version images (e.g., python-3-11, nodejs-20) to ensure they stay on their intended version family.

Constraint patterns:

Patterns use glob-style matching:

  • 3.11.* - Match any 3.11.X version (recommended)
  • 20.* - Match any 20.X version
  • 8.* - Match any 8.X version
  • 1.25.* - Match any 1.25.X version (for 3-part versioning like Go)

The pattern matches against the content of VERSION files generated during the build process.

Example:

main_package: python3.11
version_constraints: '3.11.*'  # Ensure python-3-11 stays on 3.11.X across all distros
main_package: dotnet-sdk-8.0
version_constraints: '8.*'  # Applies to rawhide, hummingbird, and any other distros

When to use:

  • Multi-version images where each image targets a specific version family
  • Preventing automatic major/minor version bumps during Renovate updates
  • Ensuring version tags remain accurate (e.g., python:3.11 doesn’t become python:3.12)

Violation handling:

When a constraint is violated, make check fails with a detailed error message showing:

  • The VERSION file path
  • The actual version found
  • The constraint that was violated
  • The properties.yml file where the constraint is defined

Options for resolution:

  1. Create a new image for the new version (recommended for major/minor bumps)
  2. Exclude the violating version via yum-repos/*.repo excludepkgs
  3. Update the constraint to allow the new version

See Version Constraints for detailed documentation.

version_package

  • Type: Object (string-to-string mapping)
  • Default: None
  • Description: Override the package name used for version extraction on a per-distro, per-variant, or per-distro/variant basis. When a variant or distro installs a different package that provides the same software (e.g., a FIPS-enabled build), this field tells the version/tag macros which package name to look up in the lockfile. The lookup order is: distro/variant, then variant, then distro, then main_package.

Per-variant override:

Use when a variant installs a differently-named package (e.g., FIPS variant):

main_package: golang1.25
version_package:
  fips: golang-fips1.25  # fips variant resolves version from golang-fips1.25

Per-distro override:

Use when different distros ship the same software under different package names:

main_package: ruby
version_package:
  hummingbird: ruby4.0  # hummingbird ships ruby4.0, rawhide ships ruby

Per-distro/variant override:

Use when both distro and variant affect the package name:

main_package: golang1.25
version_package:
  hummingbird/fips: golang-fips1.25

Usage in templates:

The resolved package name is available as package_name_for_version in Jinja2 templates:

tags:
  - value: '{{ package_version(package_name_for_version) }}'
    label: org.opencontainers.image.version
ENV GOLANG_VERSION={{ package_version(package_name_for_version) }}

Testing Configuration

reverse_dependency_tests

  • Type: Boolean
  • Default: true
  • Description: Controls whether this image participates in reverse dependency workflows (both building and testing). When true, changes to this image trigger rebuilds and tests of all images that depend on it. Dependencies are detected by searching for TEST_IMAGES[...] references in test scripts. Set to false for images that should not trigger reverse dependency workflows when changed.
  • Example: Set to false for curl to avoid triggering unnecessary reverse dependency tests

How it works in CI:

When an image is tested, the CI pipeline performs two phases:

  1. Build Phase: Builds all dependencies needed for testing

    • Uses ci/build_images.sh --build-deps to build:
      • Forward dependencies (images the main image’s tests depend on)
      • Reverse dependencies (images that depend on the main image, filtered by reverse_dependency_tests: true)
      • Forward dependencies of those reverse dependencies
    • Automatically deduplicates to ensure each image is built exactly once
  2. Test Phase: Runs tests on the main image and its reverse dependencies

    • Uses ci/run_tests_container.sh and ci/run_tests_k8s.sh with --include-reverse-deps to test:
      • The main image
      • All reverse dependencies (filtered by reverse_dependency_tests: true)

Why set to false?

Set reverse_dependency_tests: false for images like curl that are:

  • Used pervasively across many images (too many reverse dependencies)
  • Have a small, stable API that can be fully tested in their own tests
  • Would cause excessive CI load if every dependent image were tested on each change

Release Configuration

The repository and stream fields together define the release identity for an image. Both are required and validated during CI. The repository specifies the Quay.io repository name, and the stream specifies the version series within that repository.

repository

  • Type: String
  • Required: Yes
  • Description: The Quay.io repository name for this image. For images where the directory name matches the desired repository name (e.g., images/nginx/repository: nginx), this is redundant but still required for explicitness. For versioned image directories, this groups multiple versions under one repository (e.g., nodejs-20 and nodejs-24 both use repository: nodejs).
  • Example: nodejs (used by nodejs-20 and nodejs-24 directories)

stream

  • Type: String
  • Required: Yes
  • Description: The version series identity for this image within its repository. Emitted as the io.hummingbird-project.stream label on the container image, enabling the image catalog to provide consistent structured data about multi-stream repositories.

The stream value must reflect upstream’s actual branch structure at the granularity where parallel security-maintained branches exist. Use endoflife.date to determine the correct granularity.

Key rules:

  • The stream is never derived from the image directory name. The directory name is not evidence for stream granularity (e.g., a directory named foo/ for an upstream with branches 2.8, 3.0, 3.2 still requires a major.minor stream like "3.0").
  • The purpose of upstream branches is irrelevant to granularity. Whether upstream calls them “LTS”, “stable”, “mainline”, or “STS” does not matter. If they are parallel branches receiving security updates, the stream granularity must match.
  • Match the upstream project’s version naming convention. If the project identifies branches as 8.0, 9.0 (not 8, 9), use that format even if endoflife.date abbreviates it.
  • Sequential minor versions are not parallel branches. If endoflife.date lists minor versions that each immediately supersede the previous with no support overlap, the minor is a release counter. Use the major version as the stream.
  • Use "latest" only for images where no meaningful version series identity exists: rolling-release tools with date-based versioning (e.g., minio) or base images that track the distro rather than independent software (e.g., core-runtime).
  • For unversioned images (no version in directory name, no version constraints), the stream value reflects the version currently shipped and must be updated when the upstream version changes.

See the stream assignment guidelines for the full decision process and rationale.

Document the reasoning for the stream choice as a YAML comment above the stream field. This ensures future maintainers understand why a particular granularity was chosen.

Examples:

# endoflife.date: parallel at major (Docker Hub + Debian + Wolfi)
stream: "24"
# endoflife.date: parallel at major.minor (Docker Hub + Debian + Wolfi)
stream: "3.11"
# endoflife.date: branches are 10.1, 11.0, not 10, 11
stream: "10.1"
# rolling release with date-based versions, no version branches
stream: "latest"

application_category

  • Type: String
  • Required: Yes
  • Description: The Pyxis application category for the image’s delivery repository. Emitted as the io.hummingbird-project.application-category label on the container image and used in the generated Pyxis repo config. All images sharing the same repository must use the same value. Validated during make check against the list in ci/application-categories.txt.

Common values for Hummingbird images:

  • "Programming Languages & Runtimes" – language runtimes and SDKs (Go, Python, Node.js, .NET, Ruby, etc.)
  • "Web Services" – web servers, reverse proxies, and load balancers (Nginx, httpd, Caddy, HAProxy, Tomcat)
  • "Database & Data Management" – databases and caches (PostgreSQL, MariaDB, Valkey, Memcached)
  • "Developer Tools" – CLI utilities and build tools (git, curl, jq)
  • "Storage" – object storage (MinIO, MinIO Client)
  • "Operating System" – base runtime images (core-runtime)

Example:

application_category: "Web Services"

tags

  • Type: Array of objects
  • Description: Define image tags for Quay.io releases. Each variant gets these tags, with non-default variants receiving a -<variant> suffix.

Tag Object Fields:

  • value (required): The tag value. Can be a literal string (e.g., latest) or a Jinja2 template using helper functions.
  • label (optional): If specified, the tag value is written as a LABEL in the Containerfile, and the actual rendered value from the label is used as the image tag. This ensures version tags match the actual package versions in the image.

Tag Helper Functions:

  • package_version("<package-name>") - Full version (e.g., 1.2.3-4.fc42)
  • package_major_version("<package-name>") - Major version only (e.g., 1)
  • package_major_minor_version("<package-name>") - Major.minor version (e.g., 1.2)

Example:

tags:
  - value: latest                                        # Literal tag, no label
  - value: '{{ package_major_version("nginx") }}'        # Template tag with label
    label: io.hummingbird-project.major-version
  - value: '{{ package_major_minor_version("nginx") }}'  # Tag becomes actual version from label
    label: io.hummingbird-project.major-minor-version
  - value: '{{ package_version("nginx") }}'
    label: org.opencontainers.image.version

Note: For images with multiple versions, only set latest on the latest version.

supported-by

  • Type: String
  • Default: Omitted (image is Red Hat supported)
  • Description: Controls the support level and Konflux application assignment for hummingbird-distro images. When set to community, the image is assigned to the containers-ci-hummingbird Konflux application, excluded from the Red Hat release pipeline, and labeled with io.hummingbird-project.supported-by: community. When omitted, the image is Red Hat supported and assigned to containers-hummingbird. Rawhide images are implicitly community-only and always assigned to containers-rawhide regardless of this property.
  • Example: supported-by: community
  • Usage: Use for images that are not Red Hat supported, such as internal CI tooling or community-maintained images. These images are built and tested normally but published only to the quay.io/hummingbird-ci registry, not to the official Red Hat registry.

How Labels Work:

  1. Template is rendered and written to Containerfile: LABEL org.opencontainers.image.version=1.27.3-1.fc42
  2. Build system extracts the actual value from the image label
  3. Image is tagged with the extracted value: quay.io/hummingbird/nginx:1.27.3-1.fc42

This ensures tags always match the actual package versions built into the image.

Next Steps