Proxmox VE Integration
Proxmox VE Integration
Uptrakit can discover virtual machines and containers from a Proxmox VE cluster and link them to managed hosts.
See also: Plugin Configurations, Proxmox Plugin Development.
Overview
The Proxmox VE plugin connects to your Proxmox cluster's REST API to discover QEMU VMs and LXC containers. Once discovered, you can manually link them to existing Uptrakit-managed hosts to see Proxmox metadata alongside update information.
Beyond discovery and manual matching, Uptrakit can automatically create a PVE
snapshot or vzdump backup for a matched guest before applying an update
(configurable per software item/host). These protection artifacts are
labeled in the PVE UI with the software name, the version transition (e.g.
1.0.0 -> 2.0.0), and the triggering update's ID, so you can identify and
manage them at a glance. Automated rollback from these artifacts is not yet
implemented — a future capability.
Setup
Prerequisites
- A Proxmox VE cluster (version 7.x or 8.x)
- An API token with at least read access to nodes and VMs/CTs
Creating a Proxmox API Token
- In the Proxmox web UI, go to Datacenter > Permissions > API Tokens
- Click Add and create a token for an existing user
- Note the token ID and secret — you will need the full string in the format
USER@REALM!TOKENID=SECRET
Uptrakit's automatic provisioning (see Automatic Credential Provisioning below) grants only the minimum privileges each feature needs, via custom roles rather than the built-in
PVEAuditorrole. If you are creating a token manually instead of letting the SSH agent provision one, grant itSys.Audit,VM.Audit,VM.GuestAgent.Audit, andVM.GuestAgent.FileReadon/for discovery-only access -- the same privilege set as theUptrakitAuditrole Uptrakit creates automatically. Update-protection (snapshots/backups) and resource-scaling features need additional privileges; see Automatic Credential Provisioning for the full list.
Adding the Plugin Configuration
Create a plugin configuration via the API or CLI:
uptrakit plugin-configs create \
--name "My PVE Cluster" \
--plugin-type infrastructure.proxmox \
--config '{
"api_url": "https://pve.local:8006",
"api_token": "root@pam!uptrakit=your-secret-here",
"verify_tls": false
}'
Configuration Fields
| Field | Required | Default | Description |
|---|---|---|---|
api_url | Yes | — | Proxmox VE API URL (must be HTTPS) |
api_token | Yes | — | API token in USER@REALM!TOKENID=SECRET format |
verify_tls | No | true | Set to false for self-signed certificates |
node_filter | No | [] | Restrict discovery to specific node names |
Usage
All Proxmox operations are accessed through shared surfaces.
Testing the Connection
uptrakit surfaces invoke proxmox.hosts test-connection \
--params '{"plugin_config_id": "YOUR_PLUGIN_CONFIG_ID"}'
Discovering VMs and Containers
uptrakit surfaces invoke proxmox.hosts discover \
--params '{"plugin_config_id": "YOUR_PLUGIN_CONFIG_ID"}'
This queries all online nodes (or filtered nodes) and lists their QEMU VMs and LXC containers. For running QEMU VMs, it also attempts to query the guest agent for IP address information.
Listing Discovered Guests
uptrakit surfaces invoke proxmox.hosts list \
--params '{"plugin_config_id": "YOUR_PLUGIN_CONFIG_ID"}'
Matching to Uptrakit Hosts
Matching is manual — you explicitly link a discovered Proxmox guest to an Uptrakit host:
uptrakit surfaces invoke proxmox.hosts match \
--params '{"mapping_id": "MAPPING_ID", "host_id": "HOST_ID"}'
To remove a match:
uptrakit surfaces invoke proxmox.hosts unmatch \
--params '{"mapping_id": "MAPPING_ID"}'
Viewing Proxmox Info for a Host
uptrakit surfaces invoke proxmox.host-info info \
--params '{"host_id": "HOST_ID"}'
Node Filtering
To restrict discovery to specific Proxmox nodes, set the node_filter field
in your plugin configuration:
{
"api_url": "https://pve.local:8006",
"api_token": "root@pam!uptrakit=secret",
"node_filter": ["pve1", "pve3"]
}
Only nodes listed in the filter will be queried. An empty array (the default) means all online nodes are included.
Bootstrapping Guests via SSH Agent
When the SSH agent bootstraps a Proxmox VE node (via regular SSH bootstrap), it automatically detects PVE and provisions API credentials for the tenant. This enables a second bootstrap mode: bootstrapping guests (LXC containers and QEMU VMs) directly through the PVE node without needing SSH access to the guest.
Automatic Credential Provisioning
Uptrakit provisions PVE API access using a single cluster-wide user shared by every tenant, plus one privilege-separated token per tenant -- not a dedicated user per tenant:
-
One user,
uptrakit@pve, shared across every tenant on the cluster. It never gets a password -- it exists only to hold API tokens. -
One API token per tenant, id
tenant-{tenant_uuid}(full formuptrakit@pve!tenant-{tenant_uuid}), created with--privsep=1. Because the token is privilege-separated, its effective privileges are the intersection of the grants onuptrakit@pveand the grants on the token itself -- the user-level grants are the ceiling, the token-level grants are the selection within it. A tenant's token can never exceed whatuptrakit@pveitself is granted, and revoking one tenant's token never affects another tenant. -
Three custom roles, created and kept up to date automatically:
UptrakitAudit-- read-only access (Sys.Audit,VM.Audit,VM.GuestAgent.Audit,VM.GuestAgent.FileRead), granted on/.UptrakitProtection-- pre-update snapshot/backup access (VM.Snapshot,VM.Backup,Datastore.AllocateSpace), granted on/vmsand/storage.UptrakitScaling-- live resource-scaling access (VM.Audit,VM.Config.CPU,VM.Config.Memory), granted on/vms.
Each role is granted at both the user level (
uptrakit@pve) and the token level (uptrakit@pve!tenant-{tenant_uuid}) -- privilege separation requires a grant at both levels before it takes effect for the token.
Multiple tenants can share one uptrakit@pve user on the same cluster, each
with its own token; a sync only ever creates, reads, or removes its own
tenant's token, never another tenant's. See
ADR-0044
for the full rationale.
Reuse is gated by whether the agent already holds an acknowledgment for a reported plugin configuration, not by whether a token happens to exist on the cluster: if the cluster already has a token for the same tenant (from bootstrapping another node in the same cluster) but the agent has no such acknowledgment yet, the token is regenerated rather than reused.
Recovering from a deleted plugin config
That ack-gating is a permanent hazard, not just a startup edge case: deleting
a pve-* plugin configuration in the controller does not clear the
corresponding acknowledgment on the agent. The agent keeps taking the reuse
branch against a plugin configuration the controller no longer has, and never
re-reports one on its own -- re-syncing or re-bootstrapping the same host does
not recover automatically.
The reliable recovery is to delete this tenant's token on the PVE node itself, as root:
pveum user token remove 'uptrakit@pve' 'tenant-{tenant_uuid}'
This is the same command as Removing one tenant below. With the token gone, the next sync observes it missing and stops reusing the acknowledged plugin configuration id -- it takes the create branch instead, issuing a fresh token and reporting a new plugin configuration.
Removing the host from Uptrakit and re-bootstrapping it recovers only on a
standalone PVE node: the re-added host gets a fresh host ID with no
local tracking state, so the next bootstrap provisions PVE credentials and
reports a plugin configuration from a clean slate. It does not work on a
multi-node cluster -- removing a host only deletes its ssh_hosts entry,
not the underlying Proxmox host-tracking row, so that row keeps the same
node name and still matches the cluster on the next sync, feeding the same
stale plugin configuration id right back in.
Each token created or regenerated this way carries a comment visible in
pveum user token list 'uptrakit@pve': Uptrakit managed token (<instance host>, tenant <tenant uuid>) when the controller's oauth.canonical_host
setting is configured and sanitizes cleanly, or Uptrakit managed token (tenant <tenant uuid>) when it is not set (the default) or does not
sanitize. The comment is stamped only at token creation or regeneration
time -- changing oauth.canonical_host later does not update the comment
on tokens that already exist. A canonical-host change also does not reach
an agent until its next reconnect to the controller; for the embedded SSH
agent specifically, that means the next controller restart, since embedded
services receive settings once at controller boot and never reconnect. See
oauth.canonical_host
for where this setting is configured.
Privileged session requirement
Credential provisioning issues pveum commands (pveum user add, pveum user token add, and related role/ACL commands) directly against the PVE
node, with no sudo wrapping. pveum is never included in the agent's
generated sudoers file -- no plugin declares it as a required sudo command --
so provisioning only succeeds when the SSH session used for bootstrap or
sync is already a privileged (root) session. Syncing or bootstrapping as an
unprivileged user is not blocked ahead of time; the sync still runs, it just
fails once it reaches a pveum command.
When that happens, the bootstrap or sync summary reports:
PVE credential setup failed (see agent logs)
Check the agent logs for the underlying pveum error, and re-run bootstrap
or sync using a privileged (root) SSH session.
How It Works
- Bootstrap the PVE node — Use the regular SSH bootstrap to set up the PVE host. The agent detects PVE automatically and provisions (or reuses) the API user/token described above.
- Bootstrap guests — Use the "Bootstrap via Proxmox" action in the UI or
CLI. The agent SSHs to the PVE node and runs commands inside the guest via
pct exec(LXC) orqm guest exec(QEMU).
Bootstrap via Proxmox Action
Available in the SSH Hosts surface page when at least one PVE node has been bootstrapped.
| Field | Required | Default | Description |
|---|---|---|---|
| PVE Host | Yes | — | PVE node to use as gateway (select from bootstrapped nodes) |
| Guest VMID | Yes | — | VMID of the target container or VM |
| Guest Type | Yes | lxc | LXC Container or QEMU VM |
| Host Name | Yes | — | Friendly name for identification |
| Target Username | No | uptrakit | User to create/use in the guest |
| Allow All | No | false | Use NOPASSWD: ALL in sudoers |
What Happens During Guest Bootstrap
- Connects to the PVE node via SSH
- Creates the target user inside the guest
- Deploys an SSH key to the guest's
authorized_keys - Configures sudoers for the target user
- Retrieves the guest's IP address
- Verifies direct SSH connectivity to the guest
- Saves the host entry to the local database
After bootstrap, the guest is managed like any other SSH host — the agent connects directly to it for version checks and updates.
Bootstrap via Discovered Guest
The "Bootstrap via Discovered Guest" action matches guests discovered by the
Proxmox plugin to PVE hosts using the stored PVE node name and
plugin config ID. This requires that PVE hosts have been synced (via the
Sync Host action or during initial bootstrap) so the short node name (e.g.
optiplex2) is stored in the local database. Without a stored node name,
matching will fail.
If matching fails, use the Sync Host row action in the web UI (or run
uptrakit surfaces ssh-agent.hosts --target-provider-id <PROVIDER_ID> sync <host-id>)
to populate the node name, then retry.
Deprovisioning
Use these steps when you stop using Uptrakit's Proxmox VE integration entirely, or when you drop a single tenant from a shared cluster.
Removing one tenant
To remove a single tenant's access while leaving other tenants on the same cluster untouched:
pveum user token remove 'uptrakit@pve' 'tenant-{tenant_uuid}'
This is safe to run with other tenants still active on the cluster -- it
removes only that tenant's token. The shared uptrakit@pve user, its roles,
and every other tenant's token are unaffected.
Removing the last tenant
First confirm no other tenant still has a token on the shared user:
pveum user token list 'uptrakit@pve'
Only once that list is empty is it safe to remove the shared user and its custom roles:
pveum user delete 'uptrakit@pve'
pveum role delete 'UptrakitAudit'
pveum role delete 'UptrakitProtection'
pveum role delete 'UptrakitScaling'
Deleting
uptrakit@pveor any of the three roles while another tenant still has a token on that user breaks that tenant's PVE integration -- always confirm the token list is empty first.
Host-side cleanup
Removing a host from Uptrakit (uptrakit-agent-ssh host remove, or deleting
the host in the controller UI) does not remove the managed user, its SSH key,
or its sudoers drop-in from the PVE node itself -- see
SSH Agent Host Management -- Removing a host.
To clean those up on the PVE node, remove the target user's entry from
~<username>/.ssh/authorized_keys (or delete the user outright), and delete
its sudoers drop-in at /etc/sudoers.d/uptrakit-<username> (default target
username uptrakit).
Controller-side cleanup
Deleting the pve-* plugin configuration in the web UI removes only the
controller's record of it -- it does not remove anything on the PVE node.
Use the pveum commands above to remove the actual PVE-side user, token, and
roles.
Security Considerations
- The API token secret is stored encrypted at rest and masked in API responses
- HTTPS is required for the API URL — HTTP connections are rejected
- Private and loopback addresses are allowed since Proxmox is typically deployed on-premise
- TLS verification can be disabled for self-signed certificates common in PVE installations