Skip to main content
RunBook Academy

AnsibleXLII · Ansible Beyond Linux ServersBeyond Linux servers

Ansible and Proxmox VE: drawing the line

Advanced⏱ ~27 minansible-coreansible-galaxyansible-inventory

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

Not yet marked complete on this device.

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 typeModule
QEMU/KVM virtual machinescommunity.proxmox.proxmox_kvm
LXC containerscommunity.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.

Read-only / Safeconfirm what your controller actually has
ansible-galaxy collection list community.proxmox
ansible-doc -t inventory community.proxmox.proxmox

Authenticate 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:

  1. It can be scoped by role and path, so an inventory token can be permitted to read guest configuration and nothing else.
  2. It can be revoked independently of the account, without locking a person out.
  3. It appears distinctly in the cluster’s task log, so “who created this VM” has an answer that is not “the shared automation account”.
  4. 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:

Read-only / Safethe mandatory first step with any dynamic inventory
ansible-inventory -i inventory/proxmox.yml --graph
ansible-inventory -i inventory/proxmox.yml --list | head -50

Three 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:

OperationSeverityNote
proxmox_vm_infoREAD-ONLYQuery only
proxmox_kvm with state: presentCONFIGURATIONCreates or updates a definition
proxmox_kvm with state: stoppedSERVICE-IMPACTThe guest goes away
proxmox_snapCONFIGURATIONConsumes storage; check free space first
proxmox_kvm with state: absentDATA-LOSS-RISKRemoves 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.

Read-only / Safewhat any destroy play must be preceded by
ansible-playbook -i inventory/proxmox.yml decommission.yml --list-hosts
ansible-playbook -i inventory/proxmox.yml decommission.yml --list-tasks

The 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

  1. Q1. A play uses community.proxmox.proxmox_lxc to manage LXC containers and fails to resolve the module. What is going on?

  2. Q2. A decommissioning play runs proxmox_kvm with state: absent against a list that a variable resolved incorrectly. What is the recovery position?

  3. Q3. Which of these are real consequences of building inventory from the Proxmox cluster with the inventory plugin? Select all that apply.

  4. 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.