Skip to content

Daemon

Go

The VM Registry Daemon (vm-registry-daemon) is the central orchestrator running on each host machine. It exposes a gRPC service over a Unix domain socket and manages all local VM operations — image storage, VM lifecycle through libvirt, virtual networking, VMCompose orchestration, and communication with the remote registry and auth servers.

Overview

The daemon is the bridge between the user-facing CLI and the underlying infrastructure. It receives gRPC requests from the CLI, translates them into libvirt API calls, filesystem operations, and HTTP requests to remote servers, then returns structured responses.

text
CLI ──gRPC/Unix Socket──► Daemon ──libvirt API──► QEMU/KVM

                              ├──HTTP──► Registry Server
                              ├──HTTP──► Auth Server
                              └──Filesystem/S3──► Image Store

Running the Daemon

For production installation, see the Installation page. The commands below are for development:

bash
cd vm-registry-daemon
go run ./cmd/vm-registry-daemon [FLAGS]

The daemon uses cobra for CLI argument parsing. All flags use double dashes (e.g., --socket).

Configuration Flags

FlagEnv VariableDefaultDescription
--socket <PATH>VM_REGISTRY_SOCKET_PATH/var/run/vm-registry.sockUnix socket path for gRPC (mutually exclusive w/ --port)
--port <PORT>VM_REGISTRY_SOCKET_PORTTCP port to listen on at 127.0.0.1 (mutually exclusive w/ --socket)
--registry-url <URL>VM_REGISTRY_SERVER_URLRemote registry server URL
--auth-url <URL>VM_AUTH_SERVER_URLRemote auth server URL
--storage <BACKEND>filesystemStorage backend: filesystem or s3
--fs-path <PATH>VM_REGISTRY_STORAGE_PATH/var/lib/vm-registryLocal filesystem storage path
--log-level <LEVEL>VM_REGISTRY_LOG_LEVELinfoLog level: debug, info, warn, or error
--s3-bucket <BUCKET>S3 bucket name
--s3-region <REGION>us-east-1S3 region
--s3-endpoint <URL>S3-compatible endpoint URL
--s3-access-key <KEY>AWS_ACCESS_KEY_IDS3 access key
--s3-secret-key <SECRET>AWS_SECRET_ACCESS_KEYS3 secret key
--s3-skip-bucket-creationfalseSkip automatic bucket creation

TIP

--socket and --port are mutually exclusive. Use --socket for Unix domain sockets (default) or --port for TCP listening. When neither is specified, the daemon checks the VM_REGISTRY_SOCKET_PORT env var for TCP, then VM_REGISTRY_SOCKET_PATH for Unix, falling back to /var/run/vm-registry.sock.

systemd Socket Activation

The daemon supports systemd socket activation via go-systemd. When launched by systemd with a passed file descriptor, the daemon uses the inherited listener instead of creating its own. This is the recommended approach for production deployments — the NixOS module configures this automatically.

The listener resolution order is:

  1. systemd-passed listener (if present)
  2. --socket or --port flag
  3. VM_REGISTRY_SOCKET_PORT env var (TCP)
  4. VM_REGISTRY_SOCKET_PATH env var (Unix)
  5. Default: /var/run/vm-registry.sock

Examples

bash
# Unix socket (default)
go run ./cmd/vm-registry-daemon \
  --socket /tmp/vm-registry-daemon.sock \
  --registry-url http://127.0.0.1:8080 \
  --auth-url http://127.0.0.1:4078

# TCP listening
go run ./cmd/vm-registry-daemon \
  --port 50051 \
  --registry-url http://127.0.0.1:8080 \
  --auth-url http://127.0.0.1:4078

# S3 storage backend
go run ./cmd/vm-registry-daemon \
  --storage s3 \
  --s3-bucket vm-images \
  --s3-endpoint http://127.0.0.1:9000 \
  --s3-access-key admin \
  --s3-secret-key admin

# Debug logging
go run ./cmd/vm-registry-daemon --log-level debug

Internal Structure

The daemon is organized into the following internal packages:

PackageResponsibility
cmd/vm-registry-daemonCLI entry point — cobra command definition, flag parsing, listener setup, and gRPC server initialization
internal/servicegRPC service implementations — one file per domain (auth, compose, context, images, vm, network, gc, console, etc.)
internal/libvirtLibvirt wrapper — domain management, network management, bridge configuration, disk handling, cloud-init ISO generation, and helper utilities
internal/vmfileVMFile parser and validator — YAML parsing, path resolution, schema validation, and image config generation
internal/vmcomposeVMCompose parser and validator — parses multi-VM compose files, validates structure and dependency graphs
internal/storageStorage interface definition with filesystem and s3 sub-packages for pluggable backends
internal/configConfiguration struct and validation — loads from flags and environment variables
internal/clientHTTP client for communicating with the registry and auth servers (push, pull, auth, context)
internal/protoGenerated gRPC/protobuf Go code from vm-registry-proto
internal/logsStructured logging with gRPC interceptors for request/response tracing
internal/utilsShared utility functions

gRPC Service

The daemon implements the VMService gRPC service defined in vm-registry-proto. The service is registered on a Unix domain socket with reflection enabled for debugging with tools like grpcurl.

The gRPC server is configured with:

  • 4 GiB max message size for both send and receive — necessary for streaming large disk images during import
  • Unary interceptor for request/response logging
  • Stream interceptor for streaming call logging

Libvirt Integration

The daemon uses the libvirt.org/go/libvirt and libvirt.org/go/libvirtxml Go bindings for direct interaction with the libvirt hypervisor API. Key capabilities include:

Domain Management

  • Creating libvirt domains from image specs (CPU, memory, bootloader, disk)
  • Starting, stopping (graceful ACPI shutdown or forced destroy), restarting, and undefining domains
  • Listing running and defined domains with resource and status information

Network Management

  • Creating virtual networks in all supported modes: NAT, isolated, routed, bridged, open, and macvtap
  • Activating, deactivating, and deleting networks
  • Bridge interface configuration for bridged networking
  • DHCP range configuration for NAT and isolated networks

Cloud-Init

  • Generating cloud-init ISO images from user-data, meta-data, and network-config sources
  • Attaching cloud-init ISOs to VM domains for guest provisioning at first boot

Disk Management

  • Creating overlay disks (copy-on-write) for ephemeral VM storage
  • Resizing disk images when diskSize overrides are specified in VMCompose services

Image Storage

The daemon stores images locally in a content-addressable layout:

text
/var/lib/vm-registry/
├── manifests/
│   └── <repository>/
│       └── <tag>/
│           └── manifest.json
├── blobs/
│   └── sha256/
│       └── <digest>
└── runtime/
    └── <vm-name>/
  • manifests/ — Image manifests indexed by repository and tag, containing references to content-addressed blobs
  • blobs/ — Content-addressed storage for disk images and configuration blobs, keyed by SHA-256 digest
  • runtime/ — Ephemeral state for running VMs (cloud-init ISOs, overlay disks)

VMFile Processing

When a vmr import command is received, the daemon:

  1. Receives the VMFile metadata and disk image chunks over a gRPC stream
  2. Parses and validates the VMFile YAML structure
  3. Computes the SHA-256 digest of the disk image
  4. Stores the disk image blob in content-addressable storage
  5. Creates an image configuration blob from the VMFile spec
  6. Generates a manifest referencing both blobs
  7. Stores the manifest under the specified repository and tags

VMCompose Orchestration

When a vmr compose up command is received, the daemon:

  1. Parses and validates the VMCompose YAML
  2. Creates any networks defined in the networks section that don't already exist
  3. Resolves the service dependency graph using topological sorting
  4. For each service in dependency order:
    • Resolves the image reference to a local manifest
    • Applies resource overrides (CPU, memory, disk size)
    • Loads cloud-init configuration (inline or from files)
    • Creates and starts a libvirt domain with the appropriate network attachments
  5. Returns the status of all services

Dependencies

DependencyPurpose
github.com/spf13/cobraCLI framework for flag parsing and command structure
github.com/coreos/go-systemd/v22systemd socket activation support
google.golang.org/grpcgRPC server framework
google.golang.org/protobufProtocol Buffer runtime
gopkg.in/yaml.v3YAML parsing for VMFile and VMCompose
libvirt.org/go/libvirtLibvirt C API bindings
libvirt.org/go/libvirtxmlLibvirt XML domain/network generation
github.com/aws/aws-sdk-go-v2S3 storage backend

NixOS Module

The daemon's flake.nix exports a NixOS module for declarative system configuration. It manages the vm-registry user, group, systemd socket activation, and service dependencies.

Options

OptionTypeDefaultDescription
services.vm-registry-daemon.enablebooleanfalseEnable the VM Registry daemon service
services.vm-registry-daemon.packagepackageself.packages.<system>.defaultThe daemon package to use
services.vm-registry-daemon.dataDirstring/var/lib/vm-registryData directory for the daemon
services.vm-registry-daemon.socketPathstring/run/vm-registry-daemon.sockUnix socket path for the daemon

Usage

nix
{
  inputs.vm-registry.url = "github:VM-Registry/vm-registry";

  outputs = { vm-registry, nixpkgs, ... }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      modules = [
        vm-registry.nixosModules.default
        {
          services.vm-registry-daemon = {
            enable = true;
            dataDir = "/var/lib/vm-registry";
            socketPath = "/run/vm-registry-daemon.sock";
          };
        }
      ];
    };
  };
}

The module automatically:

  • Creates a vm-registry system user and group
  • Adds the user to libvirtd and kvm groups
  • Configures systemd socket activation with 0660 root:vm-registry permissions
  • Sets up the service to require libvirtd.service and the socket
  • Applies tmpfiles rules for the data directory

Docker Image

The flake builds a Docker image at ghcr.io/vm-registry/daemon:latest containing the daemon binary, QEMU/KVM, and cdrkit (for ISO generation). Build it with:

bash
nix build .#docker-image --impure

Built with Go and Rust