Documentation · Alpha

Pertisk VMs Docs

QEMU/KVM control plane with HTTP API, CLI, redesigned web UI, cloud templates, live metrics, and Terraform export.

Overview

Pertisk VMs is a Rust virtualization control plane. Operators use the HTTP API, CLI (pertisk), or web UI — not SSH — for day-to-day VM work.

Linux defaults to QEMU + OVMF (UEFI guests, serial + VNC). macOS stays on the mock driver (no /dev/kvm). Optional Cloud Hypervisor for kernel/initramfs guests.

Pertisk KOS registers this hypervisor as provider kind = "pertisk-vms" and creates cluster VMs over REST /v1 with no SSH. See KOS — Pertisk VMs notes.

Piece What you get
Daemon pertiskd — inventory, VMM lifecycle, cluster, HA, HTTPS UI/API
CLI pertisk — login, cluster, vm, iso, storage
TUI pertisk-tui — serial/SSH console: LAN IP, admin password, guest power
UI React app at :7480 (HTTP) / :7443 (HTTPS)
Node image Debian Trixie + pertiskd (install-node.sh / flashable raw)

Default bootstrap user is admin. Password is PERTISK_ADMIN_PASSWORD if set, otherwise admin.

Live demo: vms.tools.pertisk.com

Screenshots

Recapture (Playwright). Metrics pages wait 5s so charts have samples:

KOS_USER=admin KOS_PASS=admin npm run capture:vms
# TLS lab: VMS_URL=https://10.1.1.144:7443

Files: public/images/projects/vms/. Then npm run optimize:images.

Sign-in and datacenter

Pertisk VMs sign-in
Login
Datacenter overview with live CPU memory disk and network charts
Overview — live metrics
Cluster members and join
Cluster
Terraform export: provider setup, template clone, generated HCL
Terraform
Create guest wizard
Create guest

Templates

Import a cloud image once, then clone guests with unique cloud-init identity. Templates cannot be started. Options on a stopped guest can Convert to template.

Cloud templates catalog with Clone and Import image
Templates
Clone template wizard with cloud-init guest step
Clone template

Guest

Running guest AlmaLinux-10-1 (id 101) on node intel-285h, with live guest charts.

Guest summary with live CPU memory disk and network charts
Guest summary — live metrics
Graphics console Display tab on a running AlmaLinux guest
Guest console — Display (VNC)
Guest hardware disks, CD-ROM, and NICs
Guest hardware
Guest HA and start-at-boot options
Guest options

Node

In-place hypervisor updates (apt), repositories, and a root Shell on this node. Guests stay on disk.

Node summary with live host metrics
Node summary — live metrics
Node updates Refresh and Upgrade
Node updates
Node apt repositories
Repositories
Web UI root shell on the hypervisor node
Node shell (web UI)

HDMI / serial on the node itself: root MOTD (LAN, UI URL, install hints) and pertisk-tui for guest power without SSH.

pertisk-vm host console MOTD with LAN IP and root shell
Host console — MOTD / root shell
pertisk-tui guest list with CPU memory disk and power keys
Host console — pertisk-tui

Storage, network, and access

Volumes, ISOs, and cloud-init
Storage
NAT and bridged networks
Networks
Task activity log
Tasks
Users and roles
Users

Admin UI

HashRouter app (/#/…). Chrome: resource tree, search, Quorate, Create guest, Join cluster, theme, user menu / Sign out. Roles: viewer (read), operator (mutate guests), admin (users).

Page Path Shows / actions
Login /#/login Username, password, Stay signed in, Sign in
Overview /#/dc/summary Guests live, nodes, quorum, volumes, live charts
Storage /#/dc/storage Volumes (replica/RBD), import ISO, cloud-init ISO
Templates /#/dc/templates Cloud images; import, clone, destroy
Networks /#/dc/networks NAT or bridged (vmbr0 / br0)
Cluster /#/dc/cluster Members, join cluster, leave
Terraform /#/dc/terraform Provider HCL, template clone example, inventory snapshot
Tasks /#/dc/tasks Activity log
Users /#/dc/users Create user, role
Node /#/node/:id/summary Host summary + live metrics
Node guests /#/node/:id/guests Guests on that node
Node updates /#/node/:id/updates apt refresh / dist-upgrade (hypervisor only)
Repositories /#/node/:id/repositories apt sources
Node shell /#/node/:id/shell Root terminal on this hypervisor
Guest /#/vm/:id/summary Spec, power, live metrics
Console /#/vm/:id/console Serial websocket or VNC Display (QEMU graphics)
Hardware /#/vm/:id/hardware CPU / memory / disks / NICs / CD-ROM
Options /#/vm/:id/options HA, autostart, convert to template

Create guest wizard: Guest → Disk → Media → Network → Review. HA defaults on; QEMU can enable graphics (VGA/VNC).

Clone wizard (from a template): Guest → Network → Access → Options. Injects a cloud-init seed; templates cannot be started.

Quick start

git clone https://github.com/pertisktech/pertisk-vms.git
cd pertisk-vms
cargo test --workspace
PERTISK_ADMIN_PASSWORD=admin cargo run -p pertisk-daemon
cargo run -p pertisk-cli -- login -u admin -p admin
cargo run -p pertisk-cli -- cluster status

Web UI: http://127.0.0.1:7480/
OpenAPI: http://127.0.0.1:7480/v1/openapi.json

Vite UI hot reload: cd web/ui && npm run dev (proxies /v1 to the daemon). Rebuild into the daemon with npm run build in web/ui.

Cluster and HA

Writes need majority quorum. A node that loses quorum fences itself (stops local VMs) so the majority can HA-restart them. pertisk vm migrate <id> moves a guest (mock starts on the destination before tearing down the source).

PERTISK_HOME=/tmp/p1 cargo run -p pertisk-daemon -- --listen 127.0.0.1:7481 --node-name n1 --driver mock
PERTISK_HOME=/tmp/p2 cargo run -p pertisk-daemon -- --listen 127.0.0.1:7482 --node-name n2 --driver mock --join http://127.0.0.1:7481
PERTISK_HOME=/tmp/p3 cargo run -p pertisk-daemon -- --listen 127.0.0.1:7483 --node-name n3 --driver mock --join http://127.0.0.1:7482

Join from a running node: pertisk --url http://127.0.0.1:7482 cluster join --peer http://127.0.0.1:7481 -u admin -p admin.

Topic Detail
Quorum Majority: online * 2 > total (3 nodes can lose 1)
Fence Losing quorum stops local VMs
HA restart Quorate members reschedule HA guests
Heartbeat Default 1000 ms; offline after 5000 ms

Storage and network

Setting Default Notes
storage.backend replica Sparse files on N cluster nodes (replica_count = 2)
rbd optional Ceph RBD when rbd is on PATH
HA / migrate Prefer a node that already holds a replica
Runtime writes owner node Pushed to replicas on stop and before migrate
Linux bridges apply_host_links = true Creates bridge/TAP with ip
qcow2 Needs qemu-img

Linux guests

KVM kernel guest:

PERTISK_ADMIN_PASSWORD=admin ./scripts/linux-guest.sh

ISO guest (firmware + Cloud Hypervisor or QEMU):

./scripts/linux-host.sh
PERTISK_ADMIN_PASSWORD=admin ./scripts/linux-iso-guest.sh
pertisk iso import /path/to/alpine-virt.iso
pertisk vm create --name alpine --cpus 4 --memory 4096 --iso alpine-virt.iso --disk-size 32G --start
pertisk vm console <id> --attach

Guest power: POST /v1/vms/{id}/start|stop|shutdown|restart. Stop force-kills; shutdown ACPI (up to 120s); restart hard-reset (QEMU) or stop+start (Cloud Hypervisor).

Serial console websocket: /v1/vms/{id}/console/ws?token=…. Graphics: /v1/vms/{id}/graphics/ws (noVNC).

Cloud-init ISO: Storage → Import ISO → Cloud-init, or pertisk iso cloud-init --name web-1 --hostname web-1 --user ubuntu --password ubuntu.

Cloud templates

Import a cloud disk once, then clone guests with unique cloud-init identity. UI: Datacenter → Templates → Import image, then Clone. Or convert a stopped guest from Options → Convert to template. Templates cannot be started.

pertisk template import ubuntu-24.04-server-cloudimg-amd64.img --name ubuntu-24.04 --cpus 1 --memory 1024
pertisk vm clone 100 --name web-1 --cloud-init --user ubuntu --password ubuntu --ssh-key "$(cat ~/.ssh/id_ed25519.pub)" --start

Terraform

Datacenter → Terraform exports the live inventory as HCL: provider setup (pertisktech/pertisk-vms), a template-then-clone example, and a snapshot of networks, volumes, and guests. Copy or download main.tf. Import existing objects with terraform import pertisk_vms_vm.<name> <id> (and the matching template / network / volume types).

Build the provider from terraform-provider-pertisk-vms/ (go build) and use dev_overrides in ~/.terraformrc. Resources: pertisk_vms_vm, pertisk_vms_template, pertisk_vms_network, pertisk_vms_volume.

Live metrics

Datacenter overview, node summary, and guest summary stream CPU, memory, disk, and network over the events websocket (/v1/metrics, /v1/metrics/node, /v1/vms/{id}/metrics). Charts keep about 60 samples.

Node install

On an existing Linux KVM box:

sudo ./scripts/install-node.sh

Installs pertiskd as systemd (0.0.0.0:7480, home /var/lib/pertisk). Admin password: /etc/pertisk/admin.

Flashable raw image (mkosi on a Linux build host):

./scripts/build-iso.sh
sudo ./scripts/flash.sh --image out/pertisk-node.raw --disk /dev/sdX --yes

Boot USB in UEFI, then pertisk-install --disk /dev/nvme0n1 --yes. Optional cluster join: PERTISK_JOIN=http://<peer>:7480 in /etc/pertisk/join before first boot.

In-place updates (do not reflash): Node → Updates → Refresh, then Upgrade (apt-get dist-upgrade on the hypervisor; guests stay in /var/lib/pertisk). Repositories is the apt sources list. CLI: pertisk updates list|refresh|upgrade and pertisk repo list.

Host console: HDMI/serial drops you in a root MOTD (LAN, UI URL, pertisk-install). Run pertisk-tui for guest power without SSH.

Host shell: Node → Shell is a root terminal in the web UI on that hypervisor.

KOS provider

Register in KOS mgmt or Terraform:

resource "pertisk_provider" "vms" {
  name         = "lab-vms"
  kind         = "pertisk-vms"
  url          = "https://10.1.1.80:7443"
  token_id     = "admin"
  token_secret = var.pertisk_vms_password
  node         = "n1"
  storage      = "replica"
  bridge       = "vmbr0"
  insecure     = true
}
KOS field pertisk-vms
URL https://host:7443 or http://host:7480
Username / password admin / /etc/pertisk/admin
Node cluster member name
Storage replica (or rbd)
Network vmbr0

Aliases: pertisk-vm, pertiskvms, vmspertisk-vms. Lab: ./scripts/pertisk-vms-lab-up.sh in the KOS repo.

Config

Home directory: ~/.pertisk (PERTISK_HOME). Example: config.example.toml.

Key Notes
daemon.listen Default 127.0.0.1:7480
daemon.tls_listen e.g. 0.0.0.0:7443 (self-signed on first start)
vmm.driver mock | qemu | cloud-hypervisor
storage.backend replica | rbd
cluster.heartbeat_ms Default 1000