stacklok / stacklok/go-microvm

Add Hyper-V backend for Windows support

Open
#2 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Go
Stars
25
Forks
5
PR merge metrics
No merged PRs in 30d

Description

Motivation

propolis currently only runs on Linux (KVM) and macOS (Hypervisor.framework) via libkrun. There is no Windows support. This issue tracks adding a Hyper-V backend to enable running OCI container images as microVMs on Windows.

Depends on: #1 (hypervisor abstraction layer)

Background

Why Hyper-V?
  • Native Windows hypervisor — ships with Windows 10/11 Pro and Server editions
  • HCS (Host Compute Service) — well-maintained Go bindings via hcsshim, used by Docker Desktop and containerd
  • LCOW support — HCS natively supports Linux Containers on Windows (running Linux guests on Hyper-V)
  • No CGO required — hcsshim is pure Go, unlike the libkrun backend
Alternatives considered
Alternative Why not
Hyperlight No OS in guest, no filesystem, no networking, no OCI support, no Go SDK. Fundamentally different execution model (function sandbox, not container VM).
QEMU + WHPX Too heavyweight. Full device emulation stack adds significant complexity and binary size.
WSL2 + KVM KVM support in WSL2 is unreliable — works in some WSL versions, regressed in others. Not shippable.
Firecracker Linux/KVM only, no Windows support.

Proposed Implementation

Backend: hypervisor/hyperv/

Implements the hypervisor.Backend interface from #1:

//go:build windows

type Backend struct {
    // KernelPath is the path to the Linux kernel to boot.
    KernelPath string

    // InitRDPath is the optional path to an initrd image.
    InitRDPath string
}

func (b *Backend) Name() string { return "hyperv" }

func (b *Backend) PrepareRootFS(ctx context.Context, rootfsPath string, initCfg InitConfig) (string, error) {
    // 1. Write /.propolis_init.json to rootfs (generic init config)
    // 2. Convert flat rootfs directory to VHDX disk image
    // 3. Return path to VHDX
}

func (b *Backend) Start(ctx context.Context, cfg VMConfig) (VMHandle, error) {
    // 1. Create LCOW utility VM via hcsshim
    // 2. Attach root VHDX
    // 3. Configure networking (hvsock transport)
    // 4. Add filesystem mounts (Plan 9 shares)
    // 5. Start VM
    // 6. Return hcsHandle wrapping uvm.UtilityVM
}
Key components
Rootfs → VHDX conversion

The OCI image extraction pipeline produces a flat directory. Hyper-V needs a VHDX disk image. Options to investigate:

  • virtdisk.dll Windows API (CreateVirtualDisk, AttachVirtualDisk)
  • hcsshim's internal LCOW layer utilities
  • diskpart / Convert-VHD PowerShell (fragile, last resort)
Networking: gvisor-tap-vsock over hvsock

gvisor-tap-vsock already supports Hyper-V socket transport (AF_HYPERV). The same virtual network stack used by libkrun's in-process networking can be reused with a different transport:

  • libkrun: AcceptQemu() over Unix socketpair
  • Hyper-V: AcceptHyperV() over hvsock

The NetEndpoint type from #1 carries the transport info:

NetEndpoint{Type: NetEndpointHVSocket, Path: serviceGUID}
Guest init

libkrun reads /.krun_config.json via its built-in init. Hyper-V needs a different mechanism:

  • Write /.propolis_init.json with the same schema ({cmd, env, working_dir})
  • Extend the guest/boot package to read this config and exec the workload
  • Or use a minimal custom init binary in the initrd
Linux kernel

Unlike libkrun (which bundles a kernel in libkrunfw), Hyper-V requires a separate kernel image. Options:

  • Bundle a kernel build (like Docker Desktop uses LinuxKit)
  • Build a custom minimal kernel with virtio + 9p + ext4
  • Let users provide their own via Backend.KernelPath
Platform-specific files needed
File Purpose
hypervisor/hyperv/backend.go Backend implementation via hcsshim
hypervisor/hyperv/handle.go VMHandle wrapping uvm.UtilityVM
hypervisor/hyperv/vhd.go Rootfs dir → VHDX conversion
hypervisor/default_windows.go DefaultBackendName = "hyperv"
preflight/hyperv_windows.go Check Hyper-V is enabled
preflight/resources_windows.go Disk/CPU/RAM checks
internal/procutil/process_windows.go Process identification via QueryFullProcessImageName
extract/libname_windows.go .dll naming convention
VM lifecycle

Unlike libkrun (where krun_start_enter() takes over the process and never returns), Hyper-V manages VM lifecycle via API calls:

type hcsHandle struct {
    vm *uvm.UtilityVM
}

func (h *hcsHandle) Stop(ctx context.Context) error {
    // Graceful: vm.Shutdown()
    // Force:    vm.Terminate()
    // Cleanup:  vm.Close()
}

func (h *hcsHandle) IsAlive() bool { return h.vm.State() == StateRunning }
func (h *hcsHandle) ID() string    { return h.vm.ID() }

No two-process model needed. No signals. No Setsid. Pure API-driven lifecycle.

Open Questions

  • Kernel bundling: LinuxKit kernel? Custom minimal build? User-provided? What's the right default for a library?
  • VHD creation: Which Windows API is most reliable for creating ext4-formatted VHDX from a directory tree?
  • Guest init: Extend guest/boot package, or build a separate init binary for Hyper-V guests?
  • CI/CD: How to test? Windows runners with nested virtualization? Mock HCS interfaces? Integration test matrix?
  • hvsock vs named pipe: Which transport for gvisor-tap-vsock? hvsock is more natural for Hyper-V, named pipes are simpler.
  • Hyper-V editions: Require Pro/Enterprise (full Hyper-V) or also support Home (limited WHP)?
  • Filesystem mounts: Plan 9 shares via hcsshim, or alternative mechanism?

Implementation phases

  1. #1 must land first — the abstraction layer
  2. Windows platform files — preflight, procutil, extract, default backend (can be done in parallel with 3)
  3. Hyper-V backend — hcsshim integration, VHD creation, hvsock networking
  4. Guest init — extend guest/boot or build custom init for non-libkrun backends
  5. Kernel packaging — decide on and implement kernel bundling strategy
  6. CI — Windows test infrastructure

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the hypervisor.Backend interface from #1 and the existing libkrun backend, then review the proposed files under hypervisor/hyperv/, preflight/, internal/procutil/, and extract/. Resolve the open questions around kernel packaging, VHDX creation, guest init, networking, and Windows CI before implementing. Done means Windows can prepare a rootfs, start and stop a Hyper-V VM, and pass the platform checks and tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, infrastructure, operating-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.