Skip to content

block65/pontoon

Repository files navigation

Pontoon

QEMU microVM orchestrator. Define VMs, networks, and layers in a pontoon.yml file, build them with Docker, and launch them with KVM. The CLI mirrors docker compose where it makes sense.

Requirements

  • Linux with KVM (/dev/kvm)
  • QEMU (qemu-system-x86_64)
  • Docker (for building VM images and the kernel)

Install with uv:

uv tool install --editable .

Quick Start

pontoon doctor            # check prerequisites
pontoon build-kernel      # build the Linux kernel (one-time, cached)
pontoon build             # build per-VM initrds from layers
sudo pontoon network up   # create host bridges and TAPs
pontoon up                # start all VMs

A runnable two-VM topology lives in examples/quickstart.

pontoon.yml

A pontoon.yml defines the topology. Pontoon searches upward from the working directory to find it, or accepts -f path/to/pontoon.yml.

version: "1"

defaults:
  kernel: ./vm/build/vmlinuz
  alpine: "3.21"

networks:
  lan:
    subnet: 10.0.0.0/24
  wan:
    subnet: 10.0.1.0/24

services:
  router:
    memory: 128m
    cpus: 2
    layers:
      - base
      - firewall
    networks:
      lan:
        ipv4_address: 10.0.0.1
      wan:
        ipv4_address: 10.0.1.1
        masquerade: true
    kernel_args:
      svc.start: firewall

  webserver:
    memory: 256m
    cpus: 1
    layers:
      - base
      - nginx
    networks:
      lan:
        ipv4_address: 10.0.0.80
        gateway: 10.0.0.1
    kernel_args:
      svc.start: nginx

Services

Each service is a QEMU microVM. Fields:

Field Description
memory RAM allocation (64m, 128m, 256m, 1g)
cpus vCPU count
layers List of layer names composing the VM rootfs
networks Network attachments with IP, gateway, masquerade
kernel_args Extra kernel command line parameters

Networks

Each network becomes a Linux bridge on the host. VMs attach via TAP interfaces. Set internal: true to prevent host-side routing.

Link networks connect exactly two VMs with a direct QEMU socket (no bridge):

networks:
  wire:
    link: true

Layers

Layers compose a VM's rootfs. Each layer is a directory with a layer.yml:

# layers/nginx/layer.yml
packages:
  - nginx
configs:
  - etc/nginx/nginx.conf
start: |
  nginx -g 'daemon off;' &
Field Description
packages Alpine packages to install
configs Files to copy from the layer directory into the image
run Commands to execute during the Docker build
start Shell script run at VM boot (as a background service)
binary Host binary to inject (path/to/bin -> /dest/in/vm)

Layers are resolved in order: local layers/ directory first, then the stdlib layers shipped with pontoon. Later layers override earlier ones for the same destination path.

Stdlib Layers

Pontoon ships these layers:

Layer Contents
base iproute2, iptables, iperf3, socat, tcpdump
ssh OpenSSH server
ssh-hardened SSH with restricted config
redis Redis server
memcached Memcached
postgres PostgreSQL
mariadb MariaDB
prometheus Prometheus monitoring
samba Samba file server
squid Tinyproxy HTTP proxy
vsftpd FTP server
dante SOCKS proxy

CLI Reference

Commands follow docker compose conventions where applicable.

Lifecycle

Command Description
pontoon up Start all VMs. Detects and recreates VMs with changed initrds.
pontoon down Stop all VMs and clean up state.
pontoon restart [vm ...] Restart all or specific VMs.
pontoon stop [vm ...] Stop VMs without removing state.
pontoon start [vm ...] Start previously stopped VMs.
pontoon kill [-s SIGNAL] [vm ...] Send a signal to VMs (default: SIGKILL).

Build

Command Description
pontoon build [vm] Build per-VM initrds from layer definitions.
pontoon build-kernel [--no-cache] Build the Linux kernel. Cached after first build.

Inspection

Command Description
pontoon ps Show VM status (running, zombie, stopped).
pontoon top [vm ...] Show processes running inside VMs.
pontoon logs [vm] [--lines N] Stream VM console logs. All VMs if none specified.
pontoon check [vm ...] Test serial connectivity to VMs.
pontoon config Dump parsed configuration as JSON.
pontoon doctor Check prerequisites, build state, and memory sizing.
pontoon version Print pontoon version.

Interaction

Command Description
pontoon exec VM COMMAND [-u USER] [--timeout N] Run a command inside a VM via serial.
pontoon console VM Interactive serial console (Ctrl-] to exit).
pontoon watch Open a tmux dashboard with all VM consoles.

Network

Command Description
sudo pontoon network up Create host bridges and TAPs.
sudo pontoon network down Remove host bridges and TAPs.
pontoon network tcpdump NETWORK [FILTER...] Capture traffic on a bridge.

MCP Server

pontoon mcp

Starts a Model Context Protocol server on stdio. This exposes VM control tools to AI agents (Claude Code, etc.) over JSON-RPC 2.0.

MCP tools:

Tool Description
helo Server health check. Returns version and VM count.
vm_helo Test connectivity to a specific VM.
vm_exec Run a command inside a VM.
vm_exec_bg Run a background command. Checks binary exists first.
vm_tail Read a background command's log file.
vm_cp Copy a host file into a VM via 9p share.
vm_bulk_exec Run a command on multiple VMs in parallel.
vm_ps List processes inside a VM.
vm_pkill Signal processes by pattern inside a VM.
vm_logs Read a VM's host-side console log.
vm_console_stream Read raw serial output for N seconds.
vm_restart Restart a single VM.
network_tcpdump Capture traffic on a host bridge.
topology_get Return the full parsed topology as JSON.
range_up / range_down / range_status Lifecycle control.

All VM-targeting tools accept an IP address or hostname.

MCP Access Control

MCP access is configured under the mcp key in pontoon.yml:

defaults:
  mcp:
    hide_tools:             # remove from MCP tool listing
      - topology_get
      - range_status
    allow_tools:            # per-VM tool allowlist (default: unrestricted)
      - vm_exec
      - vm_exec_bg
      - vm_tail
    allow_binaries:         # per-VM command allowlist for vm_exec
      - myapp
    deny_root: true         # block user=root on vm_exec

services:
  admin:
    mcp:
      allow_tools: ["*"]       # override: full access
      allow_binaries: ["*"]
      deny_root: false
Setting Scope Semantics
hide_tools global Tools removed from the MCP listing entirely
allow_tools per-VM + global default Which MCP tools work on this VM. ["*"] = unrestricted, [] = none.
allow_binaries per-VM + global default Which commands vm_exec and vm_exec_bg allow. Matches the binary name (path-stripped).
deny_root per-VM + global default Block user: root on exec tools.

Per-VM settings override the global default. Absent settings inherit from defaults.mcp.

Architecture

Each VM runs as a QEMU microvm process with KVM acceleration. No BIOS, no PCI, no ACPI. Boot time is under 1 second for most VMs.

The rootfs is packed as a gzip-compressed cpio archive (initramfs). The kernel decompresses it into tmpfs at boot. An init.sh script (provided per-range) runs as PID 1, configures networking from kernel command line parameters, and starts services.

Communication with VMs uses two serial channels:

  • ttyS0 (console): interactive shell, log capture
  • hvc0 (MCP): dedicated channel for programmatic command execution

The MCP channel maintains persistent socket connections to avoid shell respawn overhead between commands.

Memory Sizing

pontoon doctor estimates boot memory requirements per VM:

doctor: memory
  [ok] router: 128m ram, 6.4m initrd → ~43m peak, 85m free
  [FAIL] agent: 64m ram, 9.5m initrd → ~53m peak, 11m free
         → increase memory to at least 69m

The peak includes the kernel (~22 MB), the compressed initrd in memory during decompression, and the decompressed rootfs in tmpfs. VMs need at least 15 MB of headroom above this peak to boot and run services.

Docker Compose Comparison

Docker Compose Pontoon Notes
docker compose up pontoon up Auto-recreates VMs with changed initrds
docker compose down pontoon down
docker compose restart pontoon restart All or specific VMs
docker compose stop pontoon stop
docker compose start pontoon start
docker compose kill pontoon kill
docker compose ps pontoon ps Includes zombie detection
docker compose top pontoon top
docker compose logs pontoon logs
docker compose exec pontoon exec Via serial, not docker exec
docker compose build pontoon build Builds initrds, not images
docker compose config pontoon config
docker compose version pontoon version
pontoon doctor Prerequisite and memory checks
pontoon check Serial connectivity test
pontoon console Interactive serial console
pontoon watch tmux dashboard
pontoon mcp MCP server for AI agents
pontoon network up/down Host bridge management

License

MIT

About

QEMU microVM orchestrator with composable layers and MCP server

Topics

Resources

License

Stars

Watchers

Forks

Contributors