Kvmcli v1.0.0-alpha
1 Introduction
And here it is: kvmcli v1.0.0-alpha
The goal of this release was not to add more features, but to fix architectural problems that appeared as soon as I started managing more than a few VMs.
Earlier versions worked, but they were basically just wrappers around the virsh command line (with Docker-Compose-inspired configuration files), which made further development very limited.
2 Why switching to Go
go-libvirt is a Go client for the libvirt API, which allows direct, structured interaction with libvirt and provides better control over resource lifecycles and error handling.
Project link: https://github.com/digitalocean/go-libvirt
To be honest, the first reason was that I wanted to learn Go.
At the same time, I knew that Go is fast and has a good library for libvirt (go-libvirt), which made the decision easier ¯\_(ツ)_/¯
This project was also ambitious. I wanted to create something that can handle:
- Virtual networks
- Virtual machines
- Snapshots
- Clusters (groups of VMs with logic and metadata)
- Stores
- …etc
Using the libvirt library for Go instead of writing just a wrapper gave me the freedom to achieve what I really wanted.
Today, I can say that this project is ready to be tagged as v1.0.0-alpha. There are still issues that will come later as patches, but the foundation is now solid.
What go-libvirt provided:
- Direct API calls
- Better error handling
- Less implicit behavior
- Clearer lifecycle management
3 YAML Configuration: Why It Didn’t Scale
When I first switched to Go, I kept using YAML.
The idea was to use Kubernetes-like manifests, because the model is familiar and works well for simple objects.
Example of a config in YAML:
apiVersion: kvmcli/v1
kind: VirtualMachine
metadata:
name: admin
namespace: k8s
store: homelab-store
labels:
role: dns
environment: homelab
spec:
image: rocky-9.5
cpu: 1
memory: 2048
disk:
size: 20G
network:
name: homelab
mac: "02:A3:10:00:00:02"
autostart: true
This worked fine for a few VMs.
But once I started defining:
- clusters
- multiple roles
- shared networks and stores
- repeated specs with small variations
the YAML files became:
- Very long
- Hard to read (indentation hell)
- Hard to refactor and maintain
- Easy to break
YAML was good for static data, but bad for composition.
4 Switching to HCL
The main configuration change in v1.0.0 is the move from YAML to HCL.
HCL allows:
- Direct references between resources
- Clearer structure
- Less duplication
- Better readability as configs grow
Example of a config in HCL:
vm "admin" {
image = "rocky-10.1"
namespace = "k8s"
cpu = 1
memory = 2048
disk = "20G"
store = data.store.homelab
network = network.kubernetes
ip = "10.10.10.10"
labels = {
environment = "homelab"
role = "dns"
}
}
For me, this made a big difference. The configuration stays readable even when the number of VMs increases.
5 SQLite Backend
kvmcli now uses SQLite to store information about resources.
This includes:
- Networks
- VMs
- Stores
- and the relationships between them
SQLite was chosen because:
- no external service is required
- it is reliable
- it fits local infrastructure tooling very well
6 Logging Improvements
Logging was improved to make debugging easier. Go really pushed me to be more strict and intentional about logging 😄
7 What’s Next
Planned work after v1.0.0:
- Provisionners support (ansible and shell)
- Cluster-level logic
- Variables (similar to Terraform and Packer, very handy)
- Snapshots (and restore)