AnsibleXLII · Ansible Beyond Linux ServersBeyond Linux servers
Ansible and Proxmox VE: drawing the line
What you'll learn
- Place the boundary between hypervisor platform management and guest configuration
- Authenticate to the Proxmox API with a scoped token rather than a root password
- Build inventory from the cluster and understand what that makes volatile
- Classify guest lifecycle operations by their real severity, including disk removal
- Explain why a guest that depends on its hypervisor is a defect
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
Proxmox VE is a full platform. It has a cluster, a storage model, a scheduler, a backup system, a permission system with tokens and roles, an HA manager and a REST API that exposes all of it. It is not a thing Ansible manages; it is a peer system Ansible talks to.
Which makes the boundary question sharp and answerable:
Proxmox owns the virtualisation platform. Ansible owns what runs inside the guests.
The platform decides which node a VM lives on, which storage backs its disk, when it is backed up, and whether HA restarts it elsewhere. Ansible decides that the guest has nginx, the right users, the right certificates and the right firewall rules — and does so identically whether that guest is on Proxmox, on a cloud provider, or on hardware.
That last clause is the load-bearing one, and this lesson keeps coming back to it.
The collection moved, and the module names are not what you would guess
community.proxmox is its own collection. Content that people remember
living under community.general now has its own namespace, and a play
copied from an old blog post will fail to resolve.
The two lifecycle modules are named asymmetrically and this catches everybody:
| Guest type | Module |
|---|---|
| QEMU/KVM virtual machines | community.proxmox.proxmox_kvm |
| LXC containers | community.proxmox.proxmox |
There is no proxmox_lxc. The container module is simply
community.proxmox.proxmox, which reads like a collection-level catch-all
and is not. Verified against the collection index: the LXC module page is
proxmox_module.html and there is no proxmox_lxc_module.html.
Around them sit the operations you would expect —
community.proxmox.proxmox_snap for snapshots,
community.proxmox.proxmox_backup for backup jobs,
community.proxmox.proxmox_vm_info for read-only queries — plus an
inventory plugin and two connection plugins,
community.proxmox.proxmox_pct_remote and
community.proxmox.proxmox_qemu_api.
ansible-galaxy collection list community.proxmox
ansible-doc -t inventory community.proxmox.proxmoxAuthenticate with a token, and scope it
Every module in the collection takes the same authentication parameters, and the choice you make here is the single most consequential security decision in a Proxmox play:
- name: Query the cluster, changing nothing
hosts: localhost
connection: local
gather_facts: false
vars:
proxmox_auth:
api_host: pve-01.example.com
api_user: automation@pve
api_token_id: ansible-readonly
api_token_secret: "{{ vault_proxmox_token_secret }}"
validate_certs: true
tasks:
- name: List virtual machines
community.proxmox.proxmox_vm_info:
<<: "{{ proxmox_auth }}"
register: vms
no_log: true
api_password also exists, and using it means putting a real login
credential — usually one that a human also uses — into automation. A token
is better in four specific ways, all of which matter during an incident:
- It can be scoped by role and path, so an inventory token can be permitted to read guest configuration and nothing else.
- It can be revoked independently of the account, without locking a person out.
- It appears distinctly in the cluster’s task log, so “who created this VM” has an answer that is not “the shared automation account”.
- It does not expire when a human changes their password, which is how automation breaks at the worst possible moment.
The Proxmox course covers token creation and rotation properly in REST API automation and API security and rotation, and the Ansible-facing view in Ansible on Proxmox. This lesson does not reteach them.
Inventory from the cluster
The inventory plugin turns the cluster into a host list, which is the single most valuable integration in the collection because it removes the hand-maintained file that inventory as the blast radius map identifies as the thing everyone forgets to update.
# inventory/proxmox.yml
plugin: community.proxmox.proxmox
url: https://pve-01.example.com:8006
user: automation@pve
token_id: ansible-inventory
token_secret: "{{ lookup('ansible.builtin.env', 'PROXMOX_TOKEN_SECRET') }}"
validate_certs: true
want_facts: true
group_prefix: proxmox_
keyed_groups:
- key: proxmox_tags_parsed
prefix: tag
Then inspect it before you ever run a play against it:
ansible-inventory -i inventory/proxmox.yml --graph
ansible-inventory -i inventory/proxmox.yml --list | head -50Three properties of this inventory deserve conscious handling, and Part XXIX treats each generally:
It is volatile. A VM created five minutes ago is in your host list now.
A play with hosts: all and no --limit reaches machines that did not exist
when the change was reviewed. The
unexpected hosts
lesson is the one to reread before running anything broad against a cluster
inventory.
It has a dependency. If the API is unreachable, the inventory is empty, and an empty inventory is not an error by default — it is a run that targets nothing and reports success. The empty target versus error distinction is the guard.
want_facts: true costs API calls. It fetches configuration for every
guest at inventory time, before any task runs, which on a large cluster is
the slowest part of a short play. Turn it on when you group on guest
configuration, and leave it off when you do not.
Guest lifecycle is a severity conversation
The lifecycle modules do exactly what they say, and the severity of the same module varies enormously with one parameter:
| Operation | Severity | Note |
|---|---|---|
proxmox_vm_info | READ-ONLY | Query only |
proxmox_kvm with state: present | CONFIGURATION | Creates or updates a definition |
proxmox_kvm with state: stopped | SERVICE-IMPACT | The guest goes away |
proxmox_snap | CONFIGURATION | Consumes storage; check free space first |
proxmox_kvm with state: absent | DATA-LOSS-RISK | Removes the guest and its referenced volumes |
That last row is the one to internalise. Per the module documentation,
volumes referenced in the guest configuration are always removed when the
guest is destroyed; a separate destroy_unreferenced_disks option, which
defaults to false, governs the disks that are not referenced.
So the safe-sounding reading — “it removes the VM definition, the disks
stick around” — is wrong for exactly the disks that matter. A play that
loops over a list of vmid values with state: absent and a variable that
resolved to the wrong list is not recoverable from the cluster; it is
recoverable from backups, if there are any, which is the entire subject of
the Proxmox course’s disaster-recovery part.
ansible-playbook -i inventory/proxmox.yml decommission.yml --list-hosts
ansible-playbook -i inventory/proxmox.yml decommission.yml --list-tasksThe rule that the boundary exists to protect
Here is the failure the boundary prevents, in one sentence: a guest whose configuration depends on which hypervisor it landed on cannot be rebuilt anywhere else.
It happens gradually and always for a good reason at the time. A role reads
proxmox_node from the inventory facts and picks a different NTP server
per node. A template branches on the storage backend because one node has
faster disks. A firewall rule is written against the bridge name that
happens to exist on the node where the guest was created.
Each of those is defensible in isolation. Together they mean the guest is not reproducible: migrate it, and it is subtly misconfigured; rebuild it on another cluster during a disaster, and it does not come up.
The discipline is to keep hypervisor facts out of guest roles entirely. Where a guest genuinely needs to differ — a database that must not share a node with its replica — express that as a property of the guest in inventory (a group, a variable, a tag), not as a lookup of where the hypervisor put it. The conditional logic that belongs in inventory lesson is the general form of this argument; Proxmox is where it bites hardest, because the platform is so willing to tell you where things are.
Knowledge check
Knowledge check · 4 questions
Q1. A play uses community.proxmox.proxmox_lxc to manage LXC containers and fails to resolve the module. What is going on?
Q2. A decommissioning play runs proxmox_kvm with state: absent against a list that a variable resolved incorrectly. What is the recovery position?
Q3. Which of these are real consequences of building inventory from the Proxmox cluster with the inventory plugin? Select all that apply.
Q4. A role that reads the Proxmox node a guest is running on, in order to choose a different NTP server per node, has introduced a portability defect.
Passing score: 75%. Answers are checked in this browser.