Proxmox VEXX · CLI & AutomationConfiguration management
Ansible for Proxmox: from ad-hoc commands to full fleet management
What you'll learn
- Install and configure the community.proxmox Ansible collection
- Manage PVE nodes and VMs with declarative Ansible playbooks
- Use dynamic inventories to discover VMs automatically
- Build idempotent roles that work at fleet scale
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-07
Ansible for Proxmox: from ad-hoc commands to full fleet management
Ansible is the most common configuration management tool for
operational teams. The community.proxmox collection provides modules
that wrap the PVE API and PBS API into Ansible tasks. This lesson
shows how to use them at fleet scale.
Installing the collection
# Install Ansible
apt install -y ansible python3-pip
# Install the community.proxmox collection
ansible-galaxy collection install community.proxmox
# Install the Proxmox API helper (for some modules)
pip install proxmoxer
Verify with a ping:
# inventory.ini
[pve]
pve-01.cluster.example.com
pve-02.cluster.example.com
pve-03.cluster.example.com
[pve:vars]
ansible_python_interpreter=/usr/bin/python3
ansible pve -i inventory.ini -m ping
One-off operations
# Get cluster status
ansible pve-01 -i inventory.ini -m community.proxmox.proxmox_cluster_info
# List VMs on each node
ansible pve -i inventory.ini -m community.proxmox.proxmox_vm_info \
-a "node={\{ inventory_hostname \}}"
# Reboot a specific node
ansible pve-02 -i inventory.ini -m community.proxmox.proxmox_node \
-a "node=pve-02 command=reboot"
Dynamic inventory
The community.proxmox collection ships with a dynamic inventory
script:
# Get the script
wget https://raw.githubusercontent.com/ansible-collections/community.proxmox/main/scripts/inventory/proxmox.py -O proxmox-inventory.py
chmod +x proxmox-inventory.py
Configure it:
# proxmox.ini
[proxmox]
url=https://pve-01.cluster.example.com:8006/api2/json
token=ansible@pve!inventory=12345678-...
# Validate cert
validate_certs=True
# Filter VMs to a subset (optional)
# filter_groups=tag=production
Use it:
ansible all -i proxmox.py -m ping
# Lists every VM and container as an inventory host
The script creates groups based on VM tags, node, type (qemu vs lxc), and other attributes. You can target groups in playbooks:
# All production VMs
ansible 'group_production' -i proxmox.py -m ping
# All VMs on a specific node
ansible 'pve_01' -i proxmox.py -m ping
# All qemu VMs (not containers)
ansible 'type_qemu' -i proxmox.py -m ping
Playbook: cluster maintenance
A playbook that updates all PVE nodes in a rolling fashion:
# cluster-update.yml
---
- name: PVE cluster maintenance
hosts: pve
serial: 1 # One node at a time
become: true
vars:
maintenance_window: "Sun 02:00-04:00"
tasks:
- name: Check if we\'re in maintenance window
ansible.builtin.fail:
msg: "Outside maintenance window"
when:
- ansible_date_time.weekday not in ['Sunday']
- ansible_date_time.hour < 2 or ansible_date_time.hour >= 4
ignore_errors: true
register: in_window
- name: Skip if outside window
ansible.builtin.meta: end_play
when: in_window is failed
- name: Drain VMs from this node
ansible.builtin.command:
cmd: ha-manager group modify production-grp
--nodes "pve-01:0,pve-02:100,pve-03:100"
delegate_to: pve-01
changed_when: false
- name: Migrate VMs off this node
community.proxmox.proxmox_vm_info:
node: "{\{ inventory_hostname \}}"
register: vms
- name: Migrate each VM
community.proxmox.proxmox_kvm:
api_host: pve-01
api_user: ansible@pve
api_token_id: automation
api_token_secret: "{\{ proxmox_token \}}"
vmid: "{{ item.vmid }}"
state: started
migrate: true
migration_target: pve-02
loop: "{{ vms.proxmox_vms }}"
loop_control:
pause: 30
when: vms.proxmox_vms | length > 0
- name: Upgrade packages
ansible.builtin.apt:
upgrade: dist
update_cache: yes
- name: Reboot if needed
ansible.builtin.command: /usr/bin/needs-restarting -r
register: needs_restart
failed_when: false
changed_when: needs_restart.rc == 1
- name: Reboot
ansible.builtin.reboot:
reboot_timeout: 600
when: needs_restart.rc == 1
Playbook: VM lifecycle
# vm-lifecycle.yml
---
- name: Create production web tier
hosts: localhost
gather_facts: false
vars:
api_host: pve-01.cluster.example.com
api_user: ansible@pve
api_token_id: automation
api_token_secret: "{\{ vault_proxmox_token \}}"
tasks:
- name: Create 3 web VMs from template
community.proxmox.proxmox_kvm:
api_host: "{\{ api_host \}}"
api_user: "{\{ api_user \}}"
api_token_id: "{\{ api_token_id \}}"
api_token_secret: "{\{ api_token_secret \}}"
vmid: "{{ 200 + item }}"
name: "web-{{ '%02d' | format(item) }}"
node: "pve-01"
clone: "debian-12-web-template"
full: false
storage: "local-zfs"
cores: 2
memory: 2048
net:
net0: "virtio,bridge=vmbr0,tag=100"
ipconfig:
ipconfig0: "ip=dhcp"
state: present
loop: [1, 2, 3]
- name: Wait for VMs to be ready
community.proxmox.proxmox_vm_info:
api_host: "{\{ api_host \}}"
api_user: "{\{ api_user \}}"
api_token_id: "{\{ api_token_id \}}"
api_token_secret: "{\{ api_token_secret \}}"
vmid: "{{ 200 + item }}"
name: "web-{{ '%02d' | format(item) }}"
register: vm_info
until: vm_info.proxmox_vms[0].status == "running"
retries: 30
delay: 10
loop: [1, 2, 3]
- name: Configure VMs with Ansible
hosts: "web-*"
become: true
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
- name: Deploy nginx config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/default
- name: Enable and start nginx
ansible.builtin.service:
name: nginx
state: started
enabled: yes
Roles for reuse
A role packages tasks, vars, defaults, and templates into a reusable unit. For Proxmox:
roles/
├── proxmox-cluster/
│ ├── tasks/main.yml # Update, patch, configure PVE nodes
│ ├── handlers/main.yml # Service restart handlers
│ ├── defaults/main.yml # Defaults for storage, network names
│ └── vars/main.yml # Cluster-specific variables
├── proxmox-vm/
│ ├── tasks/main.yml # Create / migrate / destroy VMs
│ ├── defaults/main.yml
│ └── templates/cloud-init.yml.j2
└── proxmox-backup/
├── tasks/main.yml # Configure backup jobs, verify schedules
└── defaults/main.yml
Use them:
# site.yml
- hosts: pve
roles:
- proxmox-cluster
- hosts: localhost
roles:
- role: proxmox-vm
vars:
vmid: 100
vm_name: "web-01"
cores: 2
memory: 2048
Idempotency
Ansible modules for Proxmox should be idempotent — running the same playbook twice should not create duplicates. The community.proxmox modules handle this:
- name: Ensure VM exists
community.proxmox.proxmox_kvm:
name: "web-01"
node: "pve-01"
state: present
# First run: creates the VM
# Second run: no change (idempotent)
For state-checking:
- name: Verify VM is running
community.proxmox.proxmox_vm_info:
name: "web-01"
register: info
- name: Start VM if stopped
community.proxmox.proxmox_kvm:
name: "web-01"
state: started
when: info.proxmox_vms[0].status != "running"
Production considerations
- Use Ansible Vault for secrets. API tokens are sensitive; never in plaintext.
- Limit concurrent operations.
serial: 1for cluster-wide changes;throttle: 1for tasks that touch many nodes. - Test in dry-run mode. Use
check_mode: truefor playbooks that modify resources. - Inventory versioning. Commit inventory to git so changes are traceable.
- Separate control node. Run Ansible from a dedicated control host, not from one of the PVE nodes. The control host should have SSH access to all PVE nodes and VMs.
Common mistakes
- Using the wrong auth method. The community.proxmox modules support both password and API token. API token is more secure.
- Mixing
command:and modules. Use modules for idempotency.command:runs every time. - No rollback plan. Before applying, snapshot or backup the
affected VMs.
ansible-playbook --checkshows what would change without applying. - No timeouts. Long-running tasks without timeouts hang Ansible forever.
Key takeaways
- community.proxmox wraps the PVE API in Ansible modules.
- Use dynamic inventory for automatic VM discovery.
- Use Vault for API tokens and other secrets.
- Always use
serial: 1for cluster-wide changes.
Knowledge check
Knowledge check · 4 questions
Q1. What is the standard Ansible collection for Proxmox?
Q2. An idempotent Ansible module can be run multiple times safely.
Q3. Which of these are good Ansible practices? (Select all that apply)
Q4. Name the ansible.cfg setting that limits concurrent host execution.
Passing score: 75%. Answers are checked in this browser.