Achieving a 0-CVE OS for VMs: The End of Traditional Patching
Posted on January 21, 2026 • 5 minutes • 1058 words
Note: When I talk about 0-CVE, I mean zero known CVEs. It’s impossible to have true 0-CVE as new vulnerabilities are discovered daily, but I can aim for zero known vulnerabilities in my deployed images.
This post dives into the details of my previous talk, “From Container to Bare Metal: Redefining OS Build with bootc,” presented at Chainguard’s “In Containers We Trust” event.
Note: Clicking the video constitutes your consent to view it via YouTube (including cookies). To view it on the YouTube site instead, please use this link.
From Containers to VMs
I started in 2022 with Chainguard’s Wolfi OS to make 0-CVE container images a reality. While that solved our container security story, it left a glaring gap: our VM workloads. I spent a lot of time looking for a way to bring that same security posture and developer experience to the virtual machine world.
Back in 2024, when I first learned about RHEL image mode (also known as RHEL bootable container) at Red Hat Summit, I knew this was the missing piece.
Building Confidence in Updates
My goals were straightforward, but the ultimate objective was to build user confidence. Without the confidence that an update can be safely applied and easily reverted, system owners simply won’t update.
Achieving this with traditional tools was surprisingly hard:
- 0 or near-zero CVEs: I needed a way to keep VMs as secure as our containers.
- Immutability: No more “snowflake” servers that drift over time.
- Easy Updates and Rollbacks: This is the mechanical key to building that user confidence. If you can’t rollback reliably, adoption will always fail.
If you work in a corporate environment, you know the fear that system owners often have: “If I run dnf update, will the application still work? And if it doesn’t, how do I go back?”
I didn’t want to keep using Ansible and Packer to spin up VMs, harden them, and snapshot them. I didn’t want to use Ansible to SSH in and run updates because manual rollbacks are a nightmare. I wanted to deliver the update as a single, immutable unit—just like a container image.
RHEL image mode checked all my boxes
RHEL image mode checked all my boxes. It allows you to build a bootable disk image using the same Dockerfile syntax I use for containers.
Here is a simplified example of what a bootable container Dockerfile might look like:
FROM registry.redhat.io/rhel10/rhel-bootc@sha256:cdc39ac8e531ce2b826e467a75d81e9f0fd7a30326c8c8d02fc5ea8bb224dc6e
# Basic hardening and package management
RUN ...
# Add your application or configuration
COPY my-app.service /etc/systemd/system/
RUN systemctl enable my-app.service
Once built, I can push this update to a standard container registry. From there, bootc-image-builder can take that same image and output it as an AWS AMI, a VMware VMDK, or even a raw disk image.
Why this works:
- Familiar Tooling: While it uses
bootc-image-builderunder the hood, the syntax is the same Dockerfile format I already know and use. - Immutable by Design: The OS is treated as a read-only image, with only a few specific paths like
/etcand/varremaining writable for configuration and persistent state. - Reliable Rollbacks: If an update fails, you just boot back into the previous version.
- It’s still RHEL: This was critical. I didn’t have to “sell” a new OS to management or retrain the ops team. More importantly, the security team didn’t need to vet a new operating system, and I could reuse all my existing RHEL hardening rules and compliance policies.
I built my setup around a simple model
I built my setup around a simple but powerful model. I have a repository dedicated to building and hardening a base image, stripping out unnecessary packages to minimize the attack surface.
The repository structure looks something like this:
.
├── rhel9/
│ ├── Dockerfile
│ └── baselayout/
├── rhel10/
│ ├── Dockerfile
│ └── baselayout/
├── python-3.14/
│ ├── Dockerfile
│ └── baselayout/
├── jre/
│ ├── Dockerfile
│ └── baselayout/
└── README.md
Each folder represents a specific bootable image. The baselayout directory contains configuration files, systemd units, and scripts that are copied into the image during the build.
To keep things fresh, I use a tool I call digestbot. Just like Renovatebot or Dependabot, it watches for updates to the official Red Hat base images. When a new version is released, it automatically:
- Triggers a rebuild of my hardened base image.
- Triggers rebuilds of all application images that extend from it.
- Runs automated tests against the new images: compliance checks, vulnerability scan, …
Every day at 9 AM, like clockwork, the scan is triggered and the entire process repeats.
The usage workflow is simple
For the end-user (the application owner), the workflow is incredibly simple. I provide a script that checks for available updates and, if a new version is found, prints the exact bootc update command they need to run. When they are ready to deploy, they use two main commands:
# Pull the latest image and prepare the update
bootc update <image>
# Switch to the new version (may requires a reboot)
bootc switch <image>
If something goes wrong after the reboot, rolling back is just as easy. This confidence is what finally broke the adoption barrier in our organization.
I plan to go a step further for teams that are fully confident in the process by introducing an automated update mechanism. System owners can opt into this simply by tagging their EC2 instances. For example, by setting tags like example.com/prefer-maintence-window and example.com/soft-apply: yes, the update is applied automatically during their specified window. This takes the “human” out of the loop for standard security patches while allowing them to remain in control via simple AWS tags.
But before I can do that, I need to find a way to standardize health checks for applications. Without a reliable way to verify that an application is healthy after an update, automated rollouts carry too much risk.
RHEL image mode is definitely worth exploring
If you’re wrestling with VM patching and security, RHEL image mode is definitely worth a look. It bridges the gap between the flexibility of VMs and the security of modern container workflows. It might not be the right fit for every use case, but for workloads that can be containerized or run in an immutable format, it solves a lot of problems I’ve been wrestling with for years.