Proxmox ops
Safe Proxmox VE administration: VM and LXC lifecycle with snapshot-before-write discipline, vzdump backup and migration workflows, storage troubleshooting, task (UPID) polling, qm/pct/pvesm command guidance, API-first with documented SSH fallback. Use for any question about proxmox, pve, vm, lxc, container, vzdump, backup, snapshot, qemu, cluster, storage pool, passthrough, or homelab virtualization.From its SKILL.md
npx -y skills add Enzojol/claude-homelab-skills --skill proxmox-opsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 21 days oldThe repository was created 21 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.9 KB, ~2.4k tokens by cl100k_base, as published. Nobody here has run it
proxmox-ops
Judgment layer for Proxmox VE. The API gives you hands; this skill is the part that checks twice. Read operations are free. Write operations follow the golden rule. No exceptions.
Credentials
All scripts source ~/.config/claude-homelab/credentials.env if it exists,
falling back to environment variables. Expected:
PROXMOX_HOST=192.168.1.10:8006 # host[:port]
PROXMOX_TOKEN_ID=claude@pve!claude # user@realm!tokenname
PROXMOX_TOKEN_SECRET=uuid-secret
PROXMOX_VERIFY_TLS=false # self-signed default; true if proper certs
API auth header: Authorization: PVEAPIToken=${PROXMOX_TOKEN_ID}=${PROXMOX_TOKEN_SECRET}.
Base URL: https://${PROXMOX_HOST}/api2/json.
Never use root@pam. Always a dedicated token with privilege
separation: user claude@pve, role PVEAuditor on / for read-only,
elevate later with PVEVMAdmin on a restricted path (e.g. /vms/<test-id>)
for the write phase. If the configured token turns out to be root@pam,
warn and recommend recreating it before doing anything else.
Missing credentials or 401: don't improvise — show the state, point to
/homelab, stop. Token pasted in chat: warn (it's in history now),
tell the user to delete the token in Datacenter → Permissions → API Tokens
and re-run bash ~/.claude/skills/proxmox-ops/scripts/setup.sh in their
own terminal.
Golden rule — write operations
Applies to: start/stop/shutdown/reset/delete of VM or CT, config changes, snapshot delete, storage changes, migration, restore. Before ANY of these:
- Safety net when relevant: destructive ops (delete, restore, config
change, snapshot delete) require a fresh snapshot or a verified recent
backup (
GET /nodes/{node}/storage/{storage}/contentfiltered onvzdump, checkctime). Start/stop don't need one, but check for locks first. - Show the exact operation: the full API call (method + path + body) or the exact shell command, before running it.
- Explicit user confirmation on that exact operation. A yes to
"restart the VM?" is not a yes to
qm stop— show what will actually run.
Read operations (status, list, config read, task log read) are always free.
Async tasks (UPID) — poll before claiming success
Almost every Proxmox write returns immediately with a UPID string
(UPID:node:...), NOT a result. The operation may still fail afterward.
Never announce success from the UPID alone. Always poll:
GET /nodes/{node}/tasks/{upid}/status → repeat while "status": "running"
Terminal state: status: "stopped". Then check exitstatus: "OK" =
success; anything else = failure — fetch the log:
GET /nodes/{node}/tasks/{upid}/log
and show the failing lines. Poll every 2s, timeout ~5 min for normal ops (longer for backup/restore/migration — tell the user it's long-running and keep polling).
Runbook: VM won't start
In order:
- Task log first: the failed start created a task —
GET /nodes/{node}/tasks?vmid={vmid}&limit=5, read the error of the most recentqmstart. The error usually names the culprit directly. - Config:
GET /nodes/{node}/qemu/{vmid}/config— look for missing ISO in cdrom drive, hostpci device that disappeared, bridge that doesn't exist (vmbrtypo), memory > host capacity. - Storage: is the disk's storage active?
GET /nodes/{node}/storage→active: 1? Full? Volume actually present (pvesm list <storage> --vmid <vmid>)? - Locks:
lock:field in config (backup, snapshot, migrate). A stale lock after an interrupted backup is classic. Clearing it (qm unlock <vmid>) is a WRITE op → golden rule: confirm first, and only after verifying no backup/migration is genuinely running (GET /nodes/{node}/tasks?running=1).
Runbook: storage full
Identify the consumers, biggest first:
- Overview:
GET /nodes/{node}/storage— which store, how full. - Orphan disks:
pvesm list <storage>and comparevmidcolumn against existing guests — volumes of deleted guests, andunused0/unused1entries in guest configs. - Backup accumulation: vzdump files beyond retention —
GET /nodes/{node}/storage/{storage}/content?content=backup, sort byctime, compare against retention settings (prune-backups). - Old templates/ISOs:
content=iso,vztmpl. - Snapshot sprawl: guests with many/old snapshots
(
GET .../qemu/{vmid}/snapshot); qcow2/ZFS snapshots grow with delta. - Deleting any of it = write op → golden rule. Propose a deletion list with sizes and let the user pick.
Runbook: backup & migration (vzdump)
- Backup one guest:
vzdump <vmid> --storage <store> --mode snapshot --compress zstd. Modesnapshot= minimal downtime (needs snapshot-capable storage);suspend= pause;stop= clean shutdown, most consistent. - Restore:
qmrestore <archive> <newvmid>/pct restore— restoring OVER an existing vmid destroys it → golden rule, backup check, confirm. - Migration: online needs shared storage or
--with-local-disks(block-copies, slow). Check target node capacity first (GET /nodes/{target}/status). It's a task: poll the UPID. - Failed backups: task log; classic causes = storage full, guest lock,
qemu-guest-agent absent while
--mode snapshotwith fsfreeze expected.
Runbook: LXC unprivileged vs privileged
Default and recommendation: unprivileged (unprivileged: 1). Root in
container = uid 100000 on host.
uid/gid mapping traps:
- Bind mounts: host files owned by uid 1000 appear as
nobodyin the container. Fix: chown host-side to 101000 (100000 + container uid), or custom idmap in/etc/pve/lxc/<id>.conf(lxc.idmapentries + matching/etc/subuid,/etc/subgidon the host). - NFS/CIFS mounts inside the CT: blocked in unprivileged. Mount on the
host, bind-mount into the CT (
mp0: /host/path,mp=/ct/path). - Docker inside LXC: needs
features: nesting=1(+keyctl=1); works unprivileged on recent PVE, but a VM is the more robust choice. - Privileged only when a device/kernel feature genuinely requires it — say why when recommending it.
Pattern: create LXC from template
- Template present?
pvesm list <store> --content vztmpl; elsepveam update && pveam available --section systemthenpveam download <store> <template>. - Recommended create (adjust sizes to need):
pct create <vmid> <store>:vztmpl/<template> \
--hostname <name> --unprivileged 1 \
--cores 2 --memory 2048 --swap 512 \
--rootfs <store>:8 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp,firewall=1 \
--features nesting=1 \
--start 0
- It's a write op → golden rule (show command, confirm). Then start,
poll the UPID, and verify with
pct exec <vmid> -- ip a(SSH fallback).
Pattern: cluster config changes
Before ANY modification under /etc/pve (storage.cfg, corosync, etc.):
tar czf /root/pve-backup-$(date +%F-%H%M).tar.gz /etc/pve (via SSH —
/etc/pve is a fuse mount of pmxcfs, snapshots don't cover it).
Single-node "cluster": no quorum issues; in a real cluster, never edit
corosync.conf without quorum understanding — see references.
GPU/device passthrough (basic)
Precondition: IOMMU on (dmesg | grep -i iommu), device in its own IOMMU
group. VM must be machine: q35; add hostpci0: <bus:dev.fn>,pcie=1.
Host must not claim the device (blacklist driver or vfio-pci early bind).
Config change = write op → golden rule + snapshot/backup first.
API vs SSH fallback
Prefer API always — auditable, token-scoped, no shell risk. SSH needed for:
pct exec/ commands inside a CT (no API endpoint)/etc/pvebackup (fuse mount)qm unlockwhen API refuses due to the lock itselfpveamtemplate downloads (API partial)- Reading host files (sysctl, /etc/network/interfaces)
When falling back to SSH, show the exact command and say why the API can't do it. SSH commands respect the golden rule too.
References & scripts
references/cli-cheatsheet.md— qm/pct/pvesm/vzdump flags that matter, known traps (locks, single-node quorum, storage content types).scripts/health-check.sh— nodes, storage, guests, recent failed tasks via API. Read-only, safe anytime.scripts/setup.sh— credential onboarding; run by the USER in their terminal, never by Claude.
Test rules (when operating on a real cluster)
- Phase 1: READ ONLY (status, list, health-check).
- Phase 2 (only after the user's explicit GO): writes ONLY on the designated test VM/CT — never anything else.
What ships with it: 3 files
8.2 KB alongside SKILL.md, 2 of them executable
references/
- cli-cheatsheet.md4.5 KB
scripts/
- health-check.shruns3.3 KB
- setup.shruns474 B