Skip to content

Getting Started

VM Registry is a container-like management system for virtual machines. It brings the familiar Docker workflow — build, push, pull, and run — to full virtual machines backed by libvirt.

Prerequisites

Before setting up VM Registry, ensure you have the following installed on your system:

  • libvirt with QEMU/KVM support
  • Go 1.24+ (for server-side components)
  • Rust toolchain (for CLI and LSP)
  • PostgreSQL 17+ (for the auth server)
  • Protobuf compiler (protoc) for gRPC code generation

TIP

All components include a flake.nix for reproducible development environments. If you use Nix, you can enter each project's devshell with nix develop.

Architecture Overview

VM Registry consists of six main components:

ComponentLanguageRole
vm-registry-cliRustUser-facing command-line interface
vm-registry-daemonGoLocal daemon managing VMs via libvirt and gRPC
vm-registry-serverGoOCI-compatible image registry with S3/filesystem storage
vm-registry-authGoJWT authentication server with PostgreSQL-backed ACLs
vm-registry-lspRustLanguage server for VMFile and VMCompose formats
vm-registry-protoProtobufShared gRPC service and message definitions

The CLI communicates with the daemon over a Unix socket using gRPC. The daemon manages local VM images and libvirt domains, and talks to the remote registry server over HTTP. The auth server issues JWT tokens that the registry server validates.

text
┌─────────────┐    gRPC/Unix Socket    ┌──────────────────┐
│   CLI (Rust) │ ◄────────────────────► │  Daemon (Go)     │
└─────────────┘                        │  ├─ libvirt       │
                                       │  ├─ image storage │
                                       │  └─ gRPC server   │
                                       └────────┬─────────┘
                                                │ HTTP
                                       ┌────────▼─────────┐
                                       │  Registry Server  │
                                       │  (Go, OCI API)    │
                                       │  ├─ Filesystem    │
                                       │  └─ S3 backend    │
                                       └────────┬─────────┘
                                                │ JWT verify
                                       ┌────────▼─────────┐
                                       │  Auth Server (Go) │
                                       │  └─ PostgreSQL    │
                                       └──────────────────┘

Setting Up the Auth Server

The auth server requires PostgreSQL and an RSA key pair for JWT signing.

1. Generate RSA keys

bash
mkdir -p keys
openssl genrsa -out keys/auth.key 4096
openssl rsa -in keys/auth.key -pubout -out keys/auth.pub

2. Start PostgreSQL

Use the provided docker-compose.yml in vm-registry-auth:

bash
cd vm-registry-auth
docker compose up -d postgres

This starts PostgreSQL on 127.0.0.1:5432 and runs the init.sql schema automatically.

3. Start the auth server

bash
DATABASE_URL="postgresql://admin:admin@localhost:5432/registry_auth_db" go run server/main.go

The auth server listens on port 4078 and exposes:

  • POST /register — Register a new user
  • POST /login — Obtain access and refresh tokens
  • GET /token — Token endpoint for registry authentication
  • POST /logout — Revoke refresh tokens
  • GET /health — Health check

Setting Up the Registry Server

The registry server stores VM images and serves them over an OCI-compatible HTTP API.

Filesystem backend (default)

bash
cd vm-registry-server
go run server/main.go -storage filesystem -fs-path ./storage

S3-compatible backend

An S3-compatible object store such as MinIO or RustFS can be used:

bash
cd vm-registry-server
docker compose up -d rustfs

go run server/main.go \
  -storage s3 \
  -s3-bucket vm-images \
  -s3-endpoint http://127.0.0.1:9000 \
  -s3-access-key rustfsadmin \
  -s3-secret-key rustfsadmin

The registry server listens on port 8080 by default (configurable with -port).

To enable authentication, copy the auth server's public key:

bash
cp ../vm-registry-auth/keys/auth.pub ./auth.pub

Setting Up the Daemon

The daemon manages local VM images and runs VMs through libvirt.

bash
cd vm-registry-daemon
go run server/main.go \
  -socket /tmp/vm-registry-daemon.sock \
  -registry-url http://127.0.0.1:8080 \
  -auth-url http://127.0.0.1:4078

Configuration flags

FlagEnv VariableDefaultDescription
-socketVM_REGISTRY_SOCKET_PATH/var/run/vm-registry.sockUnix socket path
-registry-urlVM_REGISTRY_SERVER_URLRegistry server URL
-auth-urlVM_AUTH_SERVER_URLAuth server URL
-storagefilesystemStorage backend (filesystem or s3)
-fs-pathVM_REGISTRY_STORAGE_PATH/var/lib/vm-registryLocal image storage path
-log-levelVM_REGISTRY_LOG_LEVELinfoLog level

Building the CLI

bash
cd vm-registry-cli
cargo build --release

The binary is produced at target/release/vm-registry-cli.

Environment variables

VariableDefaultDescription
VM_AUTH_SERVER_URLhttp://127.0.0.1:4078Authentication server URL
VM_REGISTRY_SERVER_URLhttp://127.0.0.1:8080Registry server URL
VM_REGISTRY_SOCKET_PATH/tmp/vm-registry-daemon.sockDaemon socket path

Quick Start Workflow

1. Register and log in

bash
vmr register -u myuser -p mypassword
vmr login -u myuser -p mypassword

2. Import a VM image

Create a VMFile describing your image:

yaml
apiVersion: vmregistry.io/v1alpha1

metadata:
  name: "myuser/my-vm"
  description: "My first VM image"
  author: "myuser"

spec:
  diskImage: "my-disk.qcow2"
  diskFormat: "qcow2"
  resources:
    cpu: 2
    memory: "4GiB"
    bootloader:
      type: "BIOS"

Then import it:

bash
vmr import ./VMFile -t latest

3. Run a VM

bash
vmr run myuser/my-vm:latest --vm-name test-vm --cpus 2 --memory-mb 4096

4. Check running VMs

bash
vmr ps

5. Push to the registry

bash
vmr push myuser/my-vm:latest

6. Pull from the registry

bash
vmr pull myuser/my-vm:latest

Next Steps

Built with Go and Rust