Rocky Linux 9 Image Builder: Blueprint‑Driven VM & Cloud Image Creation Made Simple
Discover how Rocky Linux 9’s osbuild‑composer turns image creation into a declarative, repeatable process. Build a qcow2 image from a TOML blueprint, compare to manual kickstart, and see the trade‑offs.
05 Nov 2025, 01:44 UTC

Problem & Thesis
In a world where servers are spun up in minutes, the image that boots them must be consistent, auditable, and easy to update. Traditional kickstart or cloud‑init scripts are quick to write but hard to version‑control and often miss subtle dependencies. Rocky Linux 9 ships an osbuild‑composer service that lets you describe an image in a single TOML file—called a blueprint—and let the system build it in a reproducible way. This blog shows how to use that feature, why it matters, and what you should watch out for.
Blueprints: Declarative Image Definition
A blueprint is a plain‑text TOML document that lists everything the image should contain: packages, enabled services, users, kernel options, and output formats. Because it lives in source control, you get versioning, code review, and a single source of truth. Here’s a minimal example that installs httpd and enables the service:
[image]
name = "rockylinux-9-httpd"
description = "Base image with Apache"
[packages]
# Packages are added from BaseOS and AppStream
packages = ["httpd", "httpd-tools"]
[services]
enabled = ["httpd"]
[users]
# Optional: create a non‑root user
users = [{name = "deploy", uid = 1001, groups = ["wheel"]}]
[output]
# Build one or more formats
format = ["qcow2", "vmdk"]
Notice the image section only names the build; all other sections are optional but keep the file readable. The composer backend pulls the same BaseOS/AppStream repositories that the host uses, ensuring binary compatibility and security updates flow through the same channel.
Building & Managing Images with composer‑cli
First, make sure the service is installed and running:
# Install the service from AppStream
sudo dnf install -y osbuild-composer
# Verify the CLI and service are available
composer-cli blueprints list
After confirming, push the blueprint to the backend and start a build. Replace my-blueprint.toml with the path to your file:
# Push the blueprint
composer-cli blueprints push my-blueprint.toml
# Start the build
composer-cli build start my-blueprint
The build status can be polled:
composer-cli build status
When finished, download the artifacts:
composer-cli build download --dest-dir ./artifacts
To inspect the resulting qcow2 image, you can use guestfish or virt-customize:
sudo guestfish -a artifacts/rockylinux-9-httpd.qcow2 -i \
> print /etc/os-release
That command mounts the image and prints the OS release file, confirming the image was built from Rocky Linux 9.
Trade‑offs & Limitations
- Storage Burden: The composer service writes intermediate layers under
/var/lib/osbuild-composer. Large builds can consume dozens of gigabytes, so plan disk headroom accordingly. - Blueprint Version Drift: The TOML schema and supported output formats can change between minor releases. Always validate your blueprint against the installed composer version with
composer-cli validate(if available). - Cloud‑Specific Tweaks: Images produced by osbuild may still need cloud‑init or subscription manager configuration to work in AWS, Azure, or VMware. The blueprint can embed
cloud-initseeds, but that adds complexity. - Learning Curve: While declarative, blueprints require understanding of TOML syntax and the available keys. A small learning curve compared to ad‑hoc kickstart scripts.
Practical Example: Building a Production‑Ready Web Server Image
Below is a step‑by‑step walkthrough that builds a qcow2 image ready for deployment in a KVM environment. The blueprint includes Apache, a custom user, and a firewall rule.
- Create the blueprint (
webserver.toml).
[image]
name = "rockylinux-9-webserver"
description = "Base image with Apache, firewall, and deploy user"
[packages]
packages = ["httpd", "firewalld"]
[services]
enabled = ["httpd", "firewalld"]
[users]
users = [{name = "deploy", uid = 1001, groups = ["wheel"]}]
[firewall]
# Allow HTTP
allow = [{service = "http"}]
[output]
format = ["qcow2"]
- Push and build.
composer-cli blueprints push webserver.toml composer-cli build start webserver - Wait for completion and download.
# Replace <build-id> with the actual ID composer-cli build download <build-id> --dest-dir ./artifacts - Validate the image.
sudo guestfish -a artifacts/rockylinux-9-webserver.qcow2 -i \ > print /etc/os-release > print /etc/systemd/system/multi-user.target.wants/httpd.service > print /etc/systemd/system/multi-user.target.wants/firewalld.service - Deploy to KVM.
virt-install \ --name webserver \ --ram 2048 \ --vcpus 2 \ --disk path=artifacts/rockylinux-9-webserver.qcow2,size=20 \ --import \ --network network=default \ --boot uefi
Once the VM boots, systemctl status httpd should show the service active, and the firewall should allow HTTP traffic. You now have a repeatable, versioned image that can be rebuilt whenever you need to patch or add software.
Conclusion
Rocky Linux 9’s osbuild‑composer turns image creation from an ad‑hoc task into a declarative, reproducible workflow. By writing a single blueprint you can control packages, services, users, and output formats, and the resulting image inherits the same security update stream as your host. The main trade‑offs are storage demands and the need to keep the blueprint schema in sync with the composer version. For teams that value consistency and auditability, the Image Builder offers a robust alternative to manual kickstart or cloud‑init scripts.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.