Build and Push a Configurable Image
A configurable image is a bootable container image with template files that are rendered per-host at deploy time. This lets you build one image and configure it differently for each machine.
Create the image
We'll build a simple Caddy web server image as an example. Create a directory with these files:
Containerfile
FROM quay.io/fedora/fedora-bootc:44
RUN dnf -y install caddy && dnf clean all
RUN systemctl enable caddy
# Caddy stores ACME certs under its data dir, but the caddy RPM ships no
# tmpfiles.d rule for it. On a bootc host /var is reconstructed from
# tmpfiles.d on each boot, so without this rule /var/lib/caddy never exists
# and Caddy fails TLS with "mkdir /var/lib/caddy: permission denied".
RUN printf 'd /var/lib/caddy 0750 caddy caddy - -\n' > /usr/lib/tmpfiles.d/caddy.conf
COPY Caddyfile.khtmpl /etc/caddy/Caddyfile.khtmpl
COPY schema.json /etc/kheeper/schema.json
RUN bootc container lint
Caddyfile.khtmpl — a Go template that will be rendered to /etc/caddy/Caddyfile when this image is configured.
{{ .domain }} {
respond "Hello, {{ .name }}"
}
schema.json (Optional) — validates configuration values for this image.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"domain": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": ["domain", "name"],
"additionalProperties": false
}
Build
First set the ORG var to your user organization.
ORG=$(kheeper orgs list | grep 'user-[0-9a-f]' | awk '{ print $1 }')
The kheeper.configurable=1 annotation tells the registry to extract the templates and schema when the image is pushed.
podman build --annotation kheeper.configurable=1 --arch amd64 -t us.kheeper.com/${ORG}/getting-started:v1 -f Containerfile .
Push
kheeper push us.kheeper.com/${ORG}/getting-started:v1
Verify
kheeper images get ${ORG}/getting-started:v1
The output will include a ConfigImage field, confirming the registry recognized the image as configurable.