Skip to main content

A friendly, AI-friendly CLI to explore and manage a Proxmox VE cluster.

Project description

pmox

A friendly, AI-friendly command-line tool to explore and manage a Proxmox VE cluster.

pmox wraps the Proxmox API with clean commands, pretty tables for humans, and a --json mode for machines. It is read-only by default so you (or an AI) can explore safely, with two layers of protection before anything can change.

pmox health
pmox vm list
pmox vm describe 100

# Spin up a cloud-init VM in one command:
pmox --dangerous vm new web --image ubuntu-24.04 --size small --disk 50 \
     --ssh-key ~/.ssh/id_ed25519.pub --ip dhcp --wait

# Edit a running VM:
pmox --dangerous vm set 100 -o cores=4 -o memory=4096
pmox --dangerous vm resize 100 --disk scsi0 --size +10G
pmox --dangerous vm rename 100 web01
pmox --dangerous vm tag 100 --add prod,k3s

pmox --dangerous vm start 100
pmox --dangerous vm delete 100 --yes

Safety model

Two independent gates protect your cluster:

Gate Flag Applies to Default
Dangerous mode --dangerous (or PMOX_DANGEROUS=1) any state change (power, create, delete, clone, migrate, snapshot) off — read-only
Confirmation --yes destructive ops: delete, stop, reset, migrate, rollback, snapshot delete, and set with a delete= key required when non-interactive

So:

  • Explore with no flags — nothing can be modified.
  • Change something benign (e.g. start) — add --dangerous.
  • Destroy something (e.g. delete) — add --dangerous and --yes. Note: --dangerous is global, but --yes is a per-subcommand flag, so it goes after the subcommand — pmox --dangerous vm delete 100 --yes, not pmox --dangerous --yes vm delete 100.

When running non-interactively (e.g. an AI calling the CLI), a destructive op without --yes is refused rather than silently prompted. Exit codes: 0 ok · 1 error · 2 config · 3 confirmation required · 4 read-only.

Under --json, errors are a JSON envelope {"ok": false, "error": "...", "need": [...], "message": "..."}.

Install

From PyPI:

pip install pmox

Or install from source (editable), e.g. for development:

macOS / Linux

git clone https://github.com/lukebward/pmox.git
cd pmox
python3 -m venv .venv
source .venv/bin/activate
pip install -e .

Windows (PowerShell)

git clone https://github.com/lukebward/pmox.git
cd pmox
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -e .

This puts a pmox command on your PATH. You can also run it without installing via python -m pmox.

Configure

Create an API token in Proxmox: Datacenter → Permissions → API Tokens. For full management, give the token the privileges it needs (or, for a homelab, uncheck "Privilege Separation" so it inherits the user's permissions).

Provide configuration via environment variables, a .env file, a TOML config file, or CLI flags (highest priority last):

.env (copy from .env.example):

PROXMOX_HOST=192.168.1.10
PROXMOX_TOKEN_ID=root@pam!pmox
PROXMOX_TOKEN_SECRET=00000000-0000-0000-0000-000000000000
PROXMOX_VERIFY_SSL=false

TOML at ~/.config/pmox/config.toml (or point --config / PMOX_CONFIG at one):

host = "192.168.1.10"
token_id = "root@pam!pmox"
token_secret = "..."
verify_ssl = false

TLS verification defaults to off because homelab Proxmox uses self-signed certificates. Set PROXMOX_VERIFY_SSL=true (or --verify-ssl) if your node has a CA-signed cert.

Commands

pmox version                         Proxmox version of the connected node
pmox health                          One-shot cluster health triage (read-only)
pmox nodes list                      nodes + CPU/mem/uptime
pmox nodes status <node>             detailed node status
pmox cluster status                  cluster membership/quorum
pmox cluster resources [--type]      everything the cluster sees (vm|node|storage|...)

pmox vm list [--node N]              QEMU VMs (cluster-wide)
pmox vm status <vmid>                live status (node auto-resolved)
pmox vm config <vmid>                raw configuration
pmox vm describe <vmid>              consolidated view: status + config + snapshots + tasks
pmox vm ip <vmid> [--all]            live IP(s) from the guest agent (--all: loopback/link-local/MAC)

pmox vm set <vmid> -o key=val        update config (needs --dangerous; delete=key needs --yes)
pmox vm resize <vmid> --disk D --size [+]G    grow a disk (needs --dangerous)
pmox vm rename <vmid> <newname>      rename (needs --dangerous)
pmox vm tag <vmid> --add t1,t2       add tags; --remove / --set also available (needs --dangerous)

pmox vm start|shutdown|reboot|suspend|resume <vmid>      (needs --dangerous)
pmox vm stop|reset <vmid>                                (needs --dangerous --yes)
pmox vm new [name] [--image img | --from-template id]    create VM (needs --dangerous; see Provisioning)
pmox vm up <name> --image <name>                         ready-to-SSH VM (DHCP; --ip or [network] pool for a static IP)
pmox vm create <vmid> --node N [-o key=val ...]          low-level create
pmox vm clone <vmid> --newid <id> [--name X] [--full] [--target N]
pmox vm migrate <vmid> --target N [--online]             (needs --dangerous --yes)
pmox vm delete <vmid> [--purge]                          (needs --dangerous --yes)
pmox vm snapshot list|create|delete|rollback <vmid> ...

pmox ct ...                          same as `vm`, for LXC containers
pmox ct describe <ctid>              consolidated view of a container
pmox ct new [name] --template <t>    create container from template (needs --dangerous; see Provisioning)

pmox storage list [--node N]         storage usage
pmox storage content <id> --node N
pmox task list --node N              recent tasks
pmox task status|log <upid> --node N

pmox image list                      list VM cloud-image catalog
pmox image list --ct --node N        list LXC container templates available on a node
pmox image pull <name|url> --storage S --node N [--as-template]   download image (needs --dangerous)

--node is optional for guest commands — pmox finds which node a VMID lives on via the cluster resources endpoint.

Global flags are position-independent — they work before or after the subcommand: --json/--no-json, --dangerous, --wait/--no-wait, --timeout <s> (default 600), --dry-run, --host, --port, --token-id, --token-secret, --verify-ssl/--no-verify-ssl, --config.

Provisioning

pmox can build cloud-init VMs, containers, and golden templates in a single command.

VM from a cloud image (vm new --image)

# One-call cloud-init VM — downloads the image, creates the VM, injects SSH key + network:
pmox --dangerous vm new web \
    --image ubuntu-24.04 \
    --size small \
    --disk 50 \
    --ssh-key ~/.ssh/id_ed25519.pub \
    --ip dhcp \
    --wait

# Sizing profiles: small = 1 core / 1 GiB  ·  medium = 2 / 4 GiB  ·  large = 4 / 8 GiB

--image accepts a catalog name (e.g. ubuntu-24.04), an https:// URL, or a Proxmox volume ID. Cloud-init options: --ssh-key (repeatable), --ip dhcp|<cidr>,gw=<ip>, --ciuser, --cipassword, --nameserver.

Requirements: PVE 8.2+ (8.4+ recommended). The target storage must have the import content type enabled. pmox uses an API token, so it imports by volume ID (absolute paths would need root@pam).

No-checksum caveat: catalog images are downloaded over HTTPS without checksum verification (no warning in v1). For integrity, supply --image <url> from a trusted source or a pre-verified image.

Seamless one-shot VM (vm up)

Zero config — the VM gets its address via DHCP:

pmox --dangerous vm up web --image ubuntu-24.04 --wait

pmox routes the image import to a file-based storage, ensures an SSH key (generating ~/.ssh/id_ed25519.pub if absent), and creates the VM. With DHCP the address isn't known on return (find it in your router's leases).

For a known static IP, either pass --ip 192.168.0.50/24,gw=192.168.0.1, or configure a pool once so pmox auto-allocates the lowest free address (scanning existing static ipconfigN across the cluster — no guest agent required):

[network]
cidr = "192.168.0.0/24"
gateway = "192.168.0.1"
pool = "192.168.0.200-192.168.0.250"   # MUST be outside your DHCP scope

Want a known IP on a DHCP VM? Clone a template that already runs qemu-guest-agent — the agent then reports the address, so no static IP or pool is needed:

pmox --dangerous vm up web --from-template 9000 --wait
pmox vm ip <vmid>      # returns the DHCP-assigned address via the agent

Build that agent template once (pmox stays token-only, so this part is yours):

pmox --dangerous vm up base --image ubuntu-24.04 --ip 192.168.0.250/24,gw=192.168.0.1 --wait
ssh ubuntu@192.168.0.250 "sudo apt-get update && sudo apt-get install -y qemu-guest-agent && sudo systemctl enable --now qemu-guest-agent"
pmox --dangerous --yes vm stop <vmid>
#   then convert it to a template: Proxmox UI -> Convert to template, or `qm template <vmid>` on the node

Clone from a template (vm new --from-template)

pmox --dangerous vm new web --from-template 9000 --ssh-key ~/.ssh/id_ed25519.pub --ip dhcp --wait

Clones inherit the template's disk size — pass --disk or resize afterward.

Container from a template (ct new)

pmox --dangerous ct new box \
    --template ubuntu-24.04 \
    --ssh-key ~/.ssh/id_ed25519.pub \
    --ip dhcp \
    --wait

--template accepts a catalog/aplinfo name or a vztmpl volume ID. Discover available templates with pmox image list --ct --node N.

--template-storage (default local, stores the vztmpl) differs from --storage (default local-lvm, the rootfs). SSH public keys are sent raw — no manual encoding needed.

Golden template (image pull --as-template)

pmox --dangerous image pull ubuntu-24.04 --storage local --node pve1 --as-template
# then clone it:
pmox --dangerous vm new web --from-template <id> --ssh-key ~/.ssh/id_ed25519.pub --ip dhcp --wait

--as-template conversion is one-way. Same PVE 8.2+/import content-type requirements apply.

Finding a guest's IP (vm ip / ct ip)

After creating a DHCP guest, read the address it actually got:

pmox vm ip 100              # primary IP + per-interface table
pmox vm ip 100 --all        # also show loopback, IPv6 link-local, and MACs
pmox ct ip 200              # same for containers

For VMs this reads the live interfaces from the QEMU guest agent, so the guest needs qemu-guest-agent installed and running and agent: 1 set — cloud-init VMs from vm new already enable agent: 1. Containers report their interfaces directly, so ct ip needs no agent. JSON output (the default when piped) carries every interface and address; the table view hides loopback and link-local unless you pass --all.

Using with an AI (e.g. Claude)

Point the AI at the CLI and let it run commands via the shell. Because your shell captures pmox's output, it emits JSON automatically — the model gets structured output to parse with no flag, while you still see tables at your own terminal. (Force it either way with --json / --no-json, or globally with PMOX_JSON=1 / PMOX_JSON=0.)

  • Leave dangerous mode off for exploration. The AI literally cannot change anything without you adding --dangerous (and --yes for destructive ops), so accidental damage is impossible during read-only investigation.
pmox health                          # AI explores freely, read-only (JSON auto)
pmox vm describe 100
pmox image list

When you want the AI to act, tell it to include the flags explicitly:

pmox --dangerous vm start 100
pmox --dangerous vm snapshot create 100 before-upgrade

Claude Code plugin

This repo is also a Claude Code plugin (in plugin/), so Claude can drive pmox for you with the safety gates intact:

/plugin marketplace add lukebward/pmox
/plugin install pmox@pmox-marketplace

It adds a proxmox skill (auto-activates when you ask about your cluster) plus /pmox:cluster-status, /pmox:list-guests, and /pmox:run. Install the CLI first (pipx install pmox). See plugin/README.md.

Development & tests

The test-suite mocks the Proxmox API, so no live cluster is required.

pip install -e ".[dev]"
pytest

Tests cover config precedence, the safety gates, output formatting, the API client's endpoint mapping, and the CLI end-to-end with an injected fake client.

Architecture

pmox/
  config.py      Settings + precedence merge (file < env < flags)
  client.py      thin, injectable wrapper over proxmoxer (the only API surface)
  catalog.py     VM cloud-image catalog and container template discovery
  views.py       composite read queries (describe, health)
  provision.py   VM / container creation workflows (cloud-init, clones, ct new)
  output.py      Rich tables + plain JSON; byte/uptime/percent formatters
  safety.py      the two gates: require_dangerous() and confirm()
  cli.py         Typer app wiring config + client + output + safety together

Each module has one job and a clear interface, which is what makes the whole thing straightforward to test with mocks.

License

MIT © 2026 Luke Ward

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pmox-0.4.0.tar.gz (61.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pmox-0.4.0-py3-none-any.whl (38.9 kB view details)

Uploaded Python 3

File details

Details for the file pmox-0.4.0.tar.gz.

File metadata

  • Download URL: pmox-0.4.0.tar.gz
  • Upload date:
  • Size: 61.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pmox-0.4.0.tar.gz
Algorithm Hash digest
SHA256 715cdfde141864bd118d20ea40a41087442abb724c34870351f954286c5773f2
MD5 c713c880cf5a153b38b36b91d3e04baf
BLAKE2b-256 9abe9e933f3b7fcd061b61647c50806fc6cdad377c4e3f70ead9ceb534ceafff

See more details on using hashes here.

Provenance

The following attestation bundles were made for pmox-0.4.0.tar.gz:

Publisher: publish.yml on lukebward/pmox

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pmox-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: pmox-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 38.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pmox-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fa8e8a63793972a3f17aff64178c92c23e190d61ea1453d4b3a6b51509efb9d7
MD5 f1414eb2f1e467f4a72da13f44e4ef1e
BLAKE2b-256 1bac96685fabb63e73f03b7dbb2376279fb2fd36891228966e5b934b415623d5

See more details on using hashes here.

Provenance

The following attestation bundles were made for pmox-0.4.0-py3-none-any.whl:

Publisher: publish.yml on lukebward/pmox

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page