Skip to content

Safety model

Two independent gates protect the cluster. Read-only commands need neither.

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

Where the flags go

--dangerous is global and position-independent. --yes belongs to the subcommand and goes after it:

pmox --dangerous vm delete 100 --yes

PMOX_DANGEROUS=1 is honored from the real environment only

A .env file cannot enable dangerous mode. This is deliberate: dropping a .env into a working directory should never silently arm every command in it. --no-dangerous forces read-only regardless of the environment.

Non-interactive behavior

When output is captured — an agent, a CI job, a pipe — a destructive command without --yes is refused rather than left hanging at a prompt. You get exit code 3 and a structured envelope naming the flag you need. This fail-fast check triggers whenever stdin or stdout is not a TTY, or JSON mode is on — a confirmation prompt is never written into a stream nothing will answer.

Preview any change

Add --dry-run to a mutating command to print the exact API call as JSON without making it. It still performs read calls to resolve nodes and VMIDs, so cluster connectivity is required.

Exit codes

Code Meaning
0 success
1 error, network failure, auth failure, or not-found
2 config missing or CLI usage error (the envelope's error field tells them apart)
3 operation needs --yes
4 operation needs --dangerous

JSON envelopes

Under --json (or whenever output is captured), errors are a structured envelope:

{"ok": false, "error": "read_only", "need": ["--dangerous"], "message": "..."}

error is one of eight fixed codes:

Code Exit Meaning
read_only 4 needs --dangerous
confirm_required 3 needs --yes
config 2 credentials not configured / config file invalid
usage 2 bad command line (typo'd flag or subcommand)
auth 1 API token rejected (401/403) — fix credentials, don't retry
not_found 1 guest/node/storage/task lookup found nothing — re-list instead of retrying
network 1 can't reach the Proxmox API (DNS/TLS/timeout)
error 1 general error

Envelopes carry machine-actionable fields where relevant: task failures include upid and node plus a hint (resume with pmox task wait <upid>); partial provisioning failures include vmid, completed_steps, and failed_step.

Do not blindly retry a failed provision

A guest that was already created is not cleaned up. A second attempt creates a second guest under a new VMID. Follow the envelope's hintdescribe the guest, then finish manually or delete it first.

Successful mutations

Successes carry machine-readable fields. The created VMID is always a field, never just prose:

{"ok": true, "op": "qemu.up", "vmid": 112, "node": "pve1", "name": "web",
 "ip": null, "ssh": null, "hint": "..."}

With --wait, the upid is replaced by a task object holding the final task status.

Read the fields, not the message

message is prose for humans and may change between releases. vmid, node, upid, op, and error are the stable contract.