Skip to documentation

Manage runners

Proxmox runners

Connect a Proxmox VE cluster and run Linux jobs in disposable QEMU virtual machines.

The Proxmox connector lets Mirage run Linux jobs in disposable QEMU virtual machines on your Proxmox VE cluster. It clones a prepared template for each accepted job and destroys the clone after completion or cancellation. Proxmox capacity appears under Mirage’s Local infrastructure provider; workflows and fallback do not need a Proxmox-specific runner label.

When to use Proxmox

Choose Proxmox when you operate a Proxmox VE cluster and want a QEMU VM boundary for each Linux job. A small Linux connector appliance talks to Mirage and the Proxmox HTTPS API. It does not have to run on a Proxmox node and does not need Docker.

Use one connector for each template architecture and resource shape. All of its slots use the same source template, target node, CPU count, memory, and disk contract. The initial connector supports one template and one target node; it does not provide HA placement, multi-node capacity discovery, automatic template creation, or a warm VM pool.

Deploy the connector

Create a dedicated Linux x64 or arm64 appliance with systemd and HTTPS access to both mirageci.com and your Proxmox API. From a Mirage source checkout, build and install the agent:

Connector appliance · build and install
cargo build --locked --release --package mirage-linux-agent
sudo bash scripts/install-linux-agent.sh

The installer creates the mirage service account and installs the systemd unit without starting it. In Machines, create a pairing code. Pair the appliance as the service account:

Connector appliance · pair with Mirage
read -rsp "Pairing code: " mirage_pairing_code
printf '%s\n' "$mirage_pairing_code" | sudo -u mirage env \
  MIRAGE_STATE_DIR=/var/lib/mirage mirage-linux-agent pair
unset mirage_pairing_code

Prepare a VM template

Create a powered-off QEMU template for the Linux architecture you want to advertise. The template must have:

  • the same runner version and software payload as the digest-qualified runner image configured on the connector;
  • Docker Engine and /usr/local/bin/mirage-entrypoint, with the guest’s normal docker.service and docker.socket disabled;
  • qemu-guest-agent installed, enabled in the guest, and enabled in Proxmox VM options;
  • Proxmox Start at boot and Protection disabled;
  • outbound HTTPS and GitHub Actions connectivity;
  • no persistent job workspace, repository credential, or copied production SSH host key;
  • a root disk at least as large as the connector’s runner disk setting.

Install the one-time configuration handoff before converting the VM to a template:

Template guest · install the Mirage handoff
sudo install -m 0755 apps/linux/proxmox-guest/mirage-proxmox-guest \
  /usr/local/bin/mirage-proxmox-guest
sudo install -m 0644 apps/linux/proxmox-guest/mirage-proxmox-guest.service \
  /etc/systemd/system/mirage-proxmox-guest.service
sudo install -m 0644 apps/linux/proxmox-guest/mirage-proxmox.conf \
  /etc/tmpfiles.d/mirage-proxmox.conf
sudo systemctl disable docker.service docker.socket
sudo systemctl enable qemu-guest-agent.service mirage-proxmox-guest.service

Shut down the VM, add the exact Proxmox tag mirage-template, and convert it to a template. Record the template’s node and VMID. The guest service reads a one-time GitHub runner configuration from tmpfs, deletes it before starting the runner, and reports only readiness and the final exit code to the connector.

Grant Proxmox access

Create a dedicated Proxmox API token and, preferably, a dedicated mirage pool. Scope the token to audit the source template and VM inventory; clone and configure VMs in the target pool and storage; use the selected network; start, stop, and delete those VMs; and call the QEMU Guest Agent. Exact ACL paths depend on your pool, storage, and SDN layout. Do not use an administrator token.

Store only the token secret—not the user@realm!token= prefix—in a root-created file that the service account can read:

Connector appliance · create the token-secret file
sudo install -o mirage -g mirage -m 0600 /dev/null /etc/mirage/proxmox-token
sudoedit /etc/mirage/proxmox-token

The connector refuses a token-secret file that is accessible to group or other users. If Proxmox uses a private certificate authority, save its PEM certificate on the appliance and set MIRAGE_PROXMOX_CA_FILE. TLS certificate verification cannot be disabled.

Configure the connector

Install the supplied environment template, then edit it for your cluster:

Connector appliance · install the Proxmox configuration
sudo install -o root -g mirage -m 0640 \
  apps/linux/packaging/proxmox.env.example /etc/mirage/agent.env
sudoedit /etc/mirage/agent.env

Set the Proxmox API URL, token ID and secret-file path, template node and VMID, target node, and an exclusive VMID range beginning at MIRAGE_PROXMOX_VMID_BASE. The range must contain at least MIRAGE_MAX_SLOTS IDs. Keep the template outside that range. A dedicated MIRAGE_PROXMOX_POOL is strongly recommended.

The configured runner image digest, runner version, architecture, CPU, memory, and disk must match the template. Linked clones are the default. Set MIRAGE_PROXMOX_FULL_CLONE=true only when your storage or node topology requires it and measured clone-to-ready time still fits Mirage’s 60-second startup window.

Validate and operate

Run the connector’s checks before enabling the service:

Connector appliance · validate and start
sudo -u mirage bash -c \
  'set -a; source /etc/mirage/agent.env; mirage-linux-agent doctor'
sudo systemctl enable --now mirage-linux-agent
sudo journalctl -u mirage-linux-agent -f

doctor verifies API access, the template flag and tag, QEMU Guest Agent configuration, and the reserved VMID range. The connector cleans up interrupted Mirage clones before it advertises capacity. It refuses to delete a VM unless the VMID is in its bounded range and the VM also has Mirage’s exact name and tag ownership markers.

If an unmanaged VM occupies a reserved VMID, or a disposable VM cannot be safely removed, the connector fails closed and advertises no capacity. Resolve the conflict or cleanup failure, then restart it. Use the machine page to confirm the Linux profile reports Ready before routing jobs to it.