Frequently Asked Question

Ansible Automation
Last Updated 2 hours ago

Ansible can be used with Proxmox VE 9.2 in two main ways:

  • Configuration management on the Proxmox hosts themselves
  • API-driven automation against the Proxmox cluster

This allows playbooks to both:

  • enforce consistent host configuration
  • create or modify VMs and containers
  • query cluster state
  • make decisions dynamically based on API responses

A common pattern is:

  1. use Ansible over SSH to configure the Proxmox nodes
  2. use the Proxmox API from Ansible to inspect or change virtual infrastructure
  3. use the returned data in when conditions, loops, and facts

Typical use cases

Ansible is well suited to tasks such as:

  • configuring repositories, networking, NTP, sysctl settings, and storage on Proxmox hosts
  • ensuring packages are installed consistently across all nodes
  • creating VM or LXC guests from templates
  • starting, stopping, or migrating guests
  • checking whether a VM already exists before creating it
  • choosing a target node based on free memory, CPU, or storage
  • querying snapshots, backup status, or storage content before taking action

Prerequisites

For most deployments, the following are required:

  • SSH access to the Proxmox nodes for host configuration
  • a Proxmox API token for API-based automation
  • Python available on the Ansible control host
  • the relevant Ansible collection installed

Install the community collection commonly used for Proxmox automation:

ansible-galaxy collection install community.general

Depending on the modules used, the control host may also need Python libraries such as:

pip install proxmoxer requests

Authentication to the Proxmox API

For automation, API tokens are preferable to username/password authentication because they are easier to scope and safer for unattended use.

A typical token-based variable set in Ansible looks like this:

proxmox_api_host: "pve01.example.internal"
proxmox_api_user: "automation@pve"
proxmox_api_token_id: "ansible"
proxmox_api_token_secret: "REDACTED_SECRET"
proxmox_validate_certs: false

Store secrets in Ansible Vault rather than in plain text.

Example vaulted variable file creation:

ansible-vault create group_vars/proxmox/vault.yml

Example vaulted content:

vault_proxmox_api_token_secret: "REDACTED_SECRET"

Inventory example

A simple inventory might separate Proxmox nodes from other systems:

[proxmox]
pve01 ansible_host=192.0.2.10
pve02 ansible_host=192.0.2.11
pve03 ansible_host=192.0.2.12

[proxmox:vars]
ansible_user=root

Using Ansible to configure Proxmox hosts

This is standard configuration management over SSH. For example, to ensure useful packages and NTP are configured:

- name: Configure Proxmox hosts
  hosts: proxmox
  become: true
  tasks:
    - name: Ensure common packages are installed
      ansible.builtin.apt:
        name:
          - vim
          - curl
          - jq
          - chrony
        state: present
        update_cache: true

    - name: Ensure chrony is enabled
      ansible.builtin.service:
        name: chrony
        enabled: true
        state: started

This approach is best for:

  • package installation
  • file deployment
  • kernel parameters
  • service state
  • cluster node baseline configuration

Using Ansible to call Proxmox API functions

For infrastructure actions, Ansible can call the Proxmox API through modules or directly via HTTP.

There are two common methods:

  • Ansible modules such as community.general.proxmox_kvm
  • Generic API calls using ansible.builtin.uri

Using modules is usually simpler for guest lifecycle operations. Using uri is useful when a Proxmox API function is not covered cleanly by a module.


Example 1: Create a VM only if it does not already exist

This example uses the API to list VMs on a node, then creates one only when the target VMID is absent.

- name: Create VM only if missing
  hosts: localhost
  gather_facts: false
  vars:
    proxmox_api_host: "pve01.example.internal"
    proxmox_api_user: "automation@pve"
    proxmox_api_token_id: "ansible"
    proxmox_api_token_secret: "{{ vault_proxmox_api_token_secret }}"
    proxmox_validate_certs: false
    target_node: "pve01"
    target_vmid: 120
  tasks:
    - name: Query VMs on target node
      ansible.builtin.uri:
        url: "https://{{ proxmox_api_host }}:8006/api2/json/nodes/{{ target_node }}/qemu"
        method: GET
        validate_certs: "{{ proxmox_validate_certs }}"
        headers:
          Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
      register: proxmox_vms

    - name: Build list of existing VMIDs
      ansible.builtin.set_fact:
        existing_vmids: "{{ proxmox_vms.json.data | map(attribute='vmid') | list }}"

    - name: Create VM if it does not exist
      community.general.proxmox_kvm:
        api_host: "{{ proxmox_api_host }}"
        api_user: "{{ proxmox_api_user }}"
        api_token_id: "{{ proxmox_api_token_id }}"
        api_token_secret: "{{ proxmox_api_token_secret }}"
        validate_certs: "{{ proxmox_validate_certs }}"
        node: "{{ target_node }}"
        vmid: "{{ target_vmid }}"
        name: "app01"
        memory: 4096
        cores: 2
        sockets: 1
        net:
          net0: "virtio,bridge=vmbr0"
        scsihw: "virtio-scsi-pci"
        state: present
      when: target_vmid not in existing_vmids

What this does

  • calls the Proxmox API endpoint for QEMU guests on a node
  • extracts the existing vmid values
  • creates the VM only if the requested vmid is not present

This is a good example of using API data to make playbook decisions.


Example 2: Choose a node based on available memory

A common requirement in clustered environments is to place a new VM on the node with the most free RAM.

- name: Select best node by free memory
  hosts: localhost
  gather_facts: false
  vars:
    proxmox_api_host: "pve01.example.internal"
    proxmox_api_user: "automation@pve"
    proxmox_api_token_id: "ansible"
    proxmox_api_token_secret: "{{ vault_proxmox_api_token_secret }}"
    proxmox_validate_certs: false
  tasks:
    - name: Query cluster resource summary
      ansible.builtin.uri:
        url: "https://{{ proxmox_api_host }}:8006/api2/json/cluster/resources?type=node"
        method: GET
        validate_certs: "{{ proxmox_validate_certs }}"
        headers:
          Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
      register: cluster_nodes

    - name: Build list of online nodes with calculated free memory
      ansible.builtin.set_fact:
        candidate_nodes: >-
          {{
            cluster_nodes.json.data
            | selectattr('status', 'equalto', 'online')
            | map('combine', {'free_mem': 0})
            | list
          }}

    - name: Recalculate free memory per node
      ansible.builtin.set_fact:
        candidate_nodes: "{{ recalculated_nodes }}"
      vars:
        recalculated_nodes: >-
          {%- set result = [] -%}
          {%- for n in cluster_nodes.json.data if n.status == 'online' -%}
          {%- set _ = result.append(n | combine({'free_mem': (n.maxmem | int) - (n.mem | int)})) -%}
          {%- endfor -%}
          {{ result }}

    - name: Select node with most free memory
      ansible.builtin.set_fact:
        selected_node: "{{ (candidate_nodes | sort(attribute='free_mem') | last).node }}"

    - name: Show selected node
      ansible.builtin.debug:
        msg: "Selected node: {{ selected_node }}"

What this does

  • queries node resource data from the cluster API
  • filters to online nodes
  • calculates free memory for each node
  • selects the node with the highest free memory

This selected node can then be used in later tasks for VM creation.


Example 3: Clone a VM template to the selected node

This combines decision-making with an actual provisioning task.

- name: Clone template to best node
  hosts: localhost
  gather_facts: false
  vars:
    proxmox_api_host: "pve01.example.internal"
    proxmox_api_user: "automation@pve"
    proxmox_api_token_id: "ansible"
    proxmox_api_token_secret: "{{ vault_proxmox_api_token_secret }}"
    proxmox_validate_certs: false
    template_node: "pve01"
    template_vmid: 9000
    new_vmid: 130
    new_vm_name: "web01"
    selected_node: "pve02"
  tasks:
    - name: Clone from template
      community.general.proxmox_kvm:
        api_host: "{{ proxmox_api_host }}"
        api_user: "{{ proxmox_api_user }}"
        api_token_id: "{{ proxmox_api_token_id }}"
        api_token_secret: "{{ proxmox_api_token_secret }}"
        validate_certs: "{{ proxmox_validate_certs }}"
        node: "{{ template_node }}"
        clone: "{{ template_vmid }}"
        newid: "{{ new_vmid }}"
        name: "{{ new_vm_name }}"
        target: "{{ selected_node }}"
        full: true
        state: present

Notes

  • node is the node where the template currently exists
  • target is the destination node for the clone
  • this is a common pattern for golden-image deployments

Example 4: Query snapshots and act only if a named snapshot exists

Some workflows need to inspect guest state before acting.

- name: Check whether a VM snapshot exists
  hosts: localhost
  gather_facts: false
  vars:
    proxmox_api_host: "pve01.example.internal"
    proxmox_api_user: "automation@pve"
    proxmox_api_token_id: "ansible"
    proxmox_api_token_secret: "{{ vault_proxmox_api_token_secret }}"
    proxmox_validate_certs: false
    target_node: "pve01"
    target_vmid: 120
    snapshot_name: "pre-maintenance"
  tasks:
    - name: Query snapshots
      ansible.builtin.uri:
        url: "https://{{ proxmox_api_host }}:8006/api2/json/nodes/{{ target_node }}/qemu/{{ target_vmid }}/snapshot"
        method: GET
        validate_certs: "{{ proxmox_validate_certs }}"
        headers:
          Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
      register: snapshot_info

    - name: Extract snapshot names
      ansible.builtin.set_fact:
        snapshot_names: "{{ snapshot_info.json.data | map(attribute='name') | list }}"

    - name: Stop VM only if snapshot exists
      community.general.proxmox_kvm:
        api_host: "{{ proxmox_api_host }}"
        api_user: "{{ proxmox_api_user }}"
        api_token_id: "{{ proxmox_api_token_id }}"
        api_token_secret: "{{ proxmox_api_token_secret }}"
        validate_certs: "{{ proxmox_validate_certs }}"
        node: "{{ target_node }}"
        vmid: "{{ target_vmid }}"
        state: stopped
      when: snapshot_name in snapshot_names

Why this is useful

This avoids blind actions and allows playbooks to behave conditionally based on actual cluster state.


Example 5: Use raw API endpoints for unsupported or very specific functions

Not every Proxmox API function has a perfect Ansible module wrapper. In those cases, uri is often the simplest option.

For example, querying storage content:

- name: Query storage content
  hosts: localhost
  gather_facts: false
  vars:
    proxmox_api_host: "pve01.example.internal"
    proxmox_api_user: "automation@pve"
    proxmox_api_token_id: "ansible"
    proxmox_api_token_secret: "{{ vault_proxmox_api_token_secret }}"
    proxmox_validate_certs: false
    target_node: "pve01"
    storage_name: "local-lvm"
  tasks:
    - name: Get storage content
      ansible.builtin.uri:
        url: "https://{{ proxmox_api_host }}:8006/api2/json/nodes/{{ target_node }}/storage/{{ storage_name }}/content"
        method: GET
        validate_certs: "{{ proxmox_validate_certs }}"
        headers:
          Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
      register: storage_content

    - name: Show ISO and disk images
      ansible.builtin.debug:
        var: storage_content.json.data

This pattern is useful when:

  • retrieving detailed cluster data
  • using newer API features before module support catches up
  • building custom logic around Proxmox resources

Common design pattern for Proxmox automation

A robust Ansible workflow for Proxmox 9.2 often looks like this:

  1. Gather state
  • query nodes, storage, guests, snapshots, or backups via API
  1. Set facts
  • build lists such as available nodes, used VMIDs, or storage utilisation
  1. Make decisions
  • use when, failed_when, and calculated variables
  1. Apply changes
  • create, clone, start, stop, migrate, or reconfigure resources
  1. Verify
  • query the API again to confirm the expected result

Practical examples of decisions in playbooks

Examples of conditions driven by API data include:

  • create a VM only when the VMID is unused
  • place a VM only on nodes with more than a defined amount of free RAM
  • skip backup-related actions if no snapshot or backup exists
  • migrate workloads away from a node marked offline or in maintenance
  • choose storage with enough free capacity before cloning a disk
  • start a VM only if it is not already running

Example conditional expression:

when:
  - selected_node is defined
  - free_gb | int > 100
  - vm_status != "running"

Working with Proxmox host configuration and API automation together

A single Ansible project can contain both node configuration and virtual infrastructure tasks.

A common structure is:

inventory/
group_vars/
host_vars/
roles/
  proxmox_host_baseline/
  proxmox_network/
  proxmox_storage/
  proxmox_vm_provision/
site.yml

For example:

  • proxmoxhostbaseline configures the Proxmox nodes over SSH
  • proxmoxvmprovision talks to the API from localhost

This keeps the separation clear:

  • SSH tasks manage the Linux host
  • API tasks manage Proxmox objects

Important considerations for Proxmox VE 9.2

When automating Proxmox 9.2, keep the following in mind:

  • test API calls against the live API viewer on the cluster
  • token permissions must match the actions being performed
  • many API operations are asynchronous, so follow-up polling may be needed
  • if using self-signed certificates, set certificate validation deliberately rather than ignoring it by default
  • be careful with cluster-wide operations such as migration, HA, and storage changes
  • module support in Ansible collections may lag behind the newest Proxmox features, so uri remains important

Example of polling an asynchronous task

Some API operations return a task identifier rather than completing immediately. A polling pattern can be used.

- name: Start VM and wait for completion task
  hosts: localhost
  gather_facts: false
  vars:
    proxmox_api_host: "pve01.example.internal"
    proxmox_api_user: "automation@pve"
    proxmox_api_token_id: "ansible"
    proxmox_api_token_secret: "{{ vault_proxmox_api_token_secret }}"
    proxmox_validate_certs: false
    target_node: "pve01"
    target_vmid: 120
  tasks:
    - name: Start VM
      ansible.builtin.uri:
        url: "https://{{ proxmox_api_host }}:8006/api2/json/nodes/{{ target_node }}/qemu/{{ target_vmid }}/status/start"
        method: POST
        validate_certs: "{{ proxmox_validate_certs }}"
        headers:
          Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
      register: start_task

    - name: Poll task status
      ansible.builtin.uri:
        url: "https://{{ proxmox_api_host }}:8006/api2/json/nodes/{{ target_node }}/tasks/{{ start_task.json.data }}/status"
        method: GET
        validate_certs: "{{ proxmox_validate_certs }}"
        headers:
          Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
      register: task_status
      retries: 20
      delay: 3
      until: task_status.json.data.status == "stopped"

Best practice summary

  • use Ansible over SSH for Proxmox host configuration
  • use API tokens for infrastructure automation
  • prefer Ansible modules for standard VM and container tasks
  • use ansible.builtin.uri for advanced or less common API functions
  • base decisions on queried API data rather than assumptions
  • keep secrets in Ansible Vault
  • test carefully in a non-production cluster first

In summary

Ansible can leverage Proxmox VE 9.2 very effectively by combining:

  • host configuration management
  • API-driven orchestration
  • conditional logic based on live cluster state

This makes it possible to build playbooks that do more than just push settings. They can inspect Proxmox, evaluate conditions such as node health, free memory, storage availability, VM existence, or snapshot presence, and then take the correct action automatically.

This FAQ was generated and/or edited by GAIN, GENs Artificial Intelligence Network and should not be considered 100% accurate. Always check facts and do your research, things change all the time. If you are unsure about any information provided, please raise a support ticket for clarification.
This website relies on temporary cookies to function, but no personal data is ever stored in the cookies.
OK
Powered by GEN UK CLEAN GREEN ENERGY

Loading ...