VDI β desktop pools
corral-vdi is Phase 1 of a small, self-hosted Virtual Desktop
Infrastructure built entirely on Corral primitives you already have:
corral bootc (desktop Linux images), corral-windows (Windows guests),
noVNC/RDP console bridges, and Tailscale exposure. This isn't a
Citrix/Horizon/oVirt competitor β it's "good VDI for personal or
small-team use," scoped to what fits in one Go binary and a tailnet.
Design doc: RFC-0001. Tracking issue: #69.
Mental modelβ
A pool is not a new kind of object β it's just VMs with a
corral.dev/vdi-pool=<name> label. There's no pool CRD, no controller, no
reconciliation loop watching anything. corral vdi pool create clones N
VMs and labels them; pool list reads the labels back; pool delete
deletes the labeled VMs. If you're ever unsure what a command did,
kubectl get vm -n <ns> -l corral.dev/vdi-pool shows you the truth
directly β there's no other state to go stale.
Assignment works the same way: a corral.dev/vdi-assigned-to=<user> label
plus a corral.dev/vdi-claimed-at timestamp annotation on the member VM.
Nothing lives outside the Kubernetes API.
Installβ
corral plugin install vdi
Prerequisitesβ
- A golden VM β an already-built, already-working VM you want to make
copies of. Build it the normal way:
- Desktop Linux:
corral bootc create mydesktop --image ghcr.io/ublue-os/bluefin:latest - Windows:
corral windows create mydesktop --iso <url>β see the Windows plugin guide for ISO sourcing - Anything else:
corral create mydesktop --kubevirt ...
- Desktop Linux:
- KubeVirt's clone feature needs a
VolumeSnapshotClassfor persistent-disk VMs βcorral doctorflags a missing one before you find out the hard way.
Walkthroughβ
# 1. Build (or reuse) a golden VM, customize it, then stop it β clone from
# a stopped VM for a clean disk state.
corral bootc create golden-desktop --image ghcr.io/ublue-os/bluefin:latest
corral start golden-desktop
# ...install packages, configure things...
corral stop golden-desktop
# 2. Create a pool of 3 clones.
corral vdi pool create devpool --from golden-desktop --size 3
# 3. See what's in it.
corral vdi pool list
# devpool (ns/corral-vms, 3 members)
# devpool-1 free stopped
# devpool-2 free stopped
# devpool-3 free stopped
# 4. Hand one to a user β starts it if it was stopped.
corral vdi assign devpool alice
# assigned devpool-1 β alice
# connect: corral vdi connect devpool-1
# 5. Connect β prints every reachable path, pick what fits the guest.
corral vdi connect devpool-1
# 6. Release it when done. Unassign always stops the VM β pooled desktops
# don't stay running unclaimed.
corral vdi unassign devpool-1
# 7. Tear the whole pool down when you're finished with it.
corral vdi pool delete devpool
What "connect" does todayβ
corral vdi connect <member> prints instructions β it doesn't yet pick
a protocol and open a session for you automatically. One-click routing is
Phase 2 territory, and depends on in-browser RDP landing first (see
ADR-0002)
so Windows members get the same one-click experience VNC already gives
Linux members. Until then:
| Guest | How to connect |
|---|---|
| Any VM | corral web β open the VM β Console tab (noVNC in the browser) β always works |
| Linux VM with RDP configured | The VM's Summary panel shows whether port 3389 answers; connect with a native RDP client via virtctl port-forward |
| Windows VM | Same RDP path β corral-windows-created VMs expose RDP through the proxy service |
| Any VM with SSH | corral ssh <member> |
Troubleshootingβ
golden VM "X" not foundβ the--fromVM doesn't exist in the target namespace. Pass-nto match wherever it actually lives.timed out ... waiting for the clone to produce VMβ KubeVirt's clone controller didn't produce the target VM within 2 minutes. Checkkubectl get virtualmachineclone -n <ns>β a stuck clone is almost always a StorageClass/VolumeSnapshotClass issue.pool has no free membersβ every member is claimed.corral vdi unassignone, or create a bigger pool (no live resize yet).- Assigned member won't start β
assignsurfaces the underlyingvirtctl starterror directly. The assignment label is still set even if start failed;unassignto back out and retry.
Current limitations (Phase 1 β by design)β
- No self-serve "get a desktop" page β assignment is a CLI/admin action.
- No idle/logout reclaim β nothing notices "alice hasn't touched devpool-1 in 3 hours" and reclaims it automatically.
- No live pool resize β delete and recreate, or clone one more member by hand.
- No GPU-gating on pool create β if the golden VM needs a GPU, every clone needs one too; nothing stops you from over-provisioning.
- CT-backed pools aren't implemented β only VM golden sources work today.
None of this is hidden. Full phased plan: RFC-0001.