Adding a new container image
This guide covers adding a new service/cron-job image to an existing repo. The main flow uses Python + RPM in cki-tools as the example; adjust for RPM-only builder images as noted.
Quick-reference checklist
[ ] builds/<name>/Containerfile.in
[ ] builds/<name>/rpms/rpms.in.yaml + rpms.lock.yaml
[ ] builds/<name>/requirements.lock (if Python deps)
[ ] .gitlab-ci.yml matrix entry (IMAGE_NAME, CHANGES, SMOKE_TEST_COMMANDS)
Detailed steps
1. Create Containerfile.in
Create builds/<name>/Containerfile.in with a digest-pinned base image. Fetch
the current digest:
skopeo inspect --no-tags docker://quay.io/fedora/fedora:44 | jq -r .Digest
Start the file with:
/* renovate: datasource=docker depName=quay.io/fedora/fedora */
#define _BASE_IMAGE_TAG 44@sha256:<digest>
#include "setup"
The #define MUST come before the #include. Include the digest from the start
for reproducible builds – do not rely on “Renovate adds it on first run.”
2. Create rpms.in.yaml
Create builds/<name>/rpms/rpms.in.yaml. Copy from an existing image (e.g.,
builds/pipeline-herder/rpms/rpms.in.yaml in cki-tools) as a template.
Required top-level keys:
installWeakDeps: false
zchunk: false
Required repos: fedora + updates with dependency-archive baseurls. For
images targeting s390x/ppc64le, also add fedora-secondary and
updates-secondary repos with skip_if_unavailable: true.
The context.image: must match the _BASE_IMAGE_TAG value (same version +
digest) with its own # renovate: comment.
3. Generate RPM lockfile
make builds/<name>/rpms/rpms.lock.yaml
Or use podman directly with the dependency-tools image if no Makefile target
exists yet.
4. Create Python lockfile (if needed)
Generate the per-image lockfile constrained by the root:
make builds/<name>/requirements.lock
The header command will look like:
# uv pip compile --constraint=requirements.lock --exclude-newer=P7D --python-version=3.13 ...
Use --no-emit-package=<python-name> for any packages already provided by the
RPM lockfile (e.g., --no-emit-package=pyyaml if python3-pyyaml is in
rpms.in.yaml).
5. Tag build-only packages
If packages are only needed at build time (e.g., gcc, python3-devel,
python3-pip, git-core), tag them with # builddep in rpms.in.yaml:
packages:
- gcc # builddep
- python3-devel # builddep
- python3-pyyaml
6. Add includes to Containerfile.in
#include "rpm-lockfile"
#define _PYTHON_REQUIREMENTS_LOCKFILE
#include "python-requirements"
/* Add #include "verify-python-lockfiles" here if using --no-emit-package */
#include "cleanup"
The #define _PYTHON_REQUIREMENTS_LOCKFILE MUST come before
#include "python-requirements". If the image uses --no-emit-package= to
exclude RPM-provided packages, also add #include "verify-python-lockfiles"
before #include "cleanup".
Alternatively, for more control over the install sequence, use inline pip
commands instead of the python-requirements include (as done in datawarehouse
and kernel-qe-tools – see
Container images for
both patterns).
7. Register in CI build matrix
Add to .gitlab-ci.yml under the parallel matrix:
- IMAGE_NAME: <name>
CHANGES: builds/<name>
SMOKE_TEST_COMMANDS: "python -c 'import my_package'"
8. Verify locally
In the containers repo: cpp -E -traditional -undef -Iincludes builds/<name>/Containerfile.in
for quick preprocessing checks, or cki_build_image.sh for a full build.
In service repos (cki-tools, datawarehouse, etc.): Use cki_build_image.sh
or podman build with the buildah image. Raw cpp won’t work because includes
are baked into the buildah image, not in the local working tree:
podman run \
--rm \
-e IMAGE_NAME=<name> \
--privileged \
-w /code \
-v .:/code \
-v ~/.local/share/containers:/var/lib/containers \
quay.io/cki/buildah:production \
cki_build_image.sh
Lockfile header format
The uv pip compile command in the lockfile header must follow CI validation
rules (see Lockfiles). Use
long-form options with = separators, include --exclude-newer=P7D, include
--no-strip-extras on the root lockfile. CI rejects MRs with non-conforming
headers.
Variant: RPM-only builder images
Builder images in the containers repo are simpler: RPM lockfile only, no Python
lockfile. May need the rpm-lockfile-repos include for RHEL builders to set up
correct repository configuration.