Home RSS Feed My CV

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)

Creative Commons License

Copyright © 2025 Zakaria Kebairia
Content licensed under CC-BY-SA 4.0 unless otherwise noted.