AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible for Proxmox: Automate VM and Container Management Complete Guide

By Luca Berton · Published 2024-01-01 · Category: installation

Automate Proxmox VE with Ansible. Create VMs and LXC containers, manage storage, configure networking, automate templates, cluster operations, backups.

Why Automate Proxmox with Ansible?

Proxmox VE is one of the most popular open-source virtualization platforms, widely used in homelabs, SMBs, and enterprises. Managing VMs and containers manually through the web UI works for a few machines — but when you need to provision dozens of VMs, maintain consistent configurations, or rebuild environments reliably, you need automation.

Ansible's community.general collection includes Proxmox modules that let you manage the full lifecycle: create VMs and LXC containers, configure networks and storage, manage templates, handle snapshots and backups — all from YAML playbooks.

See also: Ansible on Proxmox VE 8 Automation Complete Guide

Prerequisites

# Install the collection
ansible-galaxy collection install community.general

# Install proxmoxer Python library (required on control node) pip install proxmoxer requests

Authentication

# group_vars/proxmox.yml
proxmox_host: "pve1.home.lab"
proxmox_user: "ansible@pam"
proxmox_password: "{{ vault_proxmox_password }}"
# Or use API token (recommended):
proxmox_token_id: "ansible@pam!automation"
proxmox_token_secret: "{{ vault_proxmox_token }}"

Create Virtual Machines

Clone from Template

---
- name: Create VMs from template
  hosts: localhost
  gather_facts: false

vars: proxmox_host: "pve1.home.lab" proxmox_token_id: "ansible@pam!automation" proxmox_token_secret: "{{ vault_proxmox_token }}" template_vmid: 9000

vms: - { name: "web-01", vmid: 100, cores: 2, memory: 4096, ip: "10.0.1.10/24" } - { name: "web-02", vmid: 101, cores: 2, memory: 4096, ip: "10.0.1.11/24" } - { name: "db-01", vmid: 200, cores: 4, memory: 8192, ip: "10.0.1.20/24" }

tasks: - name: Clone VM from template community.general.proxmox_kvm: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" clone: "ubuntu-template" name: "{{ item.name }}" vmid: "{{ item.vmid }}" full: true storage: "local-lvm" timeout: 300 loop: "{{ vms }}"

- name: Configure VM resources community.general.proxmox_kvm: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" vmid: "{{ item.vmid }}" cores: "{{ item.cores }}" memory: "{{ item.memory }}" update: true loop: "{{ vms }}"

- name: Configure cloud-init networking community.general.proxmox_kvm: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" vmid: "{{ item.vmid }}" ipconfig: ipconfig0: "ip={{ item.ip }},gw=10.0.1.1" nameservers: "10.0.1.1" searchdomains: "home.lab" sshkeys: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}" update: true loop: "{{ vms }}"

- name: Start VMs community.general.proxmox_kvm: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" vmid: "{{ item.vmid }}" state: started loop: "{{ vms }}"

Create VM from Scratch

    - name: Create VM with specific hardware
      community.general.proxmox_kvm:
        api_host: "{{ proxmox_host }}"
        api_token_id: "{{ proxmox_token_id }}"
        api_token_secret: "{{ proxmox_token_secret }}"
        node: "pve1"
        name: "custom-vm"
        vmid: 300
        cores: 4
        sockets: 1
        memory: 8192
        balloon: 4096
        bios: ovmf
        machine: q35
        cpu: host
        ostype: l26
        scsihw: virtio-scsi-single
        scsi:
          scsi0: "local-lvm:32,iothread=1,discard=on,ssd=1"
        ide:
          ide2: "local:iso/ubuntu-24.04-server.iso,media=cdrom"
        net:
          net0: "virtio,bridge=vmbr0,firewall=1"
        efidisk0:
          storage: local-lvm
          format: raw
          efitype: 4m
          pre_enrolled_keys: false
        agent: true
        onboot: true
        state: present

See also: Ansible docker_container Module: Manage Docker Containers (Guide)

Create LXC Containers

---
- name: Create LXC containers
  hosts: localhost
  gather_facts: false

vars: containers: - { name: "pihole", vmid: 400, template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst", cores: 1, memory: 512, disk: 8, ip: "10.0.1.40/24" } - { name: "nginx-proxy", vmid: 401, template: "local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst", cores: 2, memory: 1024, disk: 16, ip: "10.0.1.41/24" } - { name: "monitoring", vmid: 402, template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst", cores: 2, memory: 2048, disk: 32, ip: "10.0.1.42/24" }

tasks: - name: Create LXC container community.general.proxmox: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" hostname: "{{ item.name }}" vmid: "{{ item.vmid }}" ostemplate: "{{ item.template }}" storage: "local-lvm" disk: "{{ item.disk }}" cores: "{{ item.cores }}" memory: "{{ item.memory }}" swap: 512 netif: '{"net0":"name=eth0,bridge=vmbr0,ip={{ item.ip }},gw=10.0.1.1"}' nameserver: "10.0.1.1" searchdomain: "home.lab" pubkey: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}" unprivileged: true features: - nesting=1 onboot: true state: present loop: "{{ containers }}"

- name: Start containers community.general.proxmox: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" vmid: "{{ item.vmid }}" state: started loop: "{{ containers }}"

Create Cloud-Init Templates

---
- name: Create Ubuntu cloud-init template
  hosts: proxmox_nodes
  become: true

vars: template_vmid: 9000 cloud_image_url: "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"

tasks: - name: Download cloud image ansible.builtin.get_url: url: "{{ cloud_image_url }}" dest: /tmp/ubuntu-cloud.img

- name: Create VM for template ansible.builtin.command: cmd: > qm create {{ template_vmid }} --name ubuntu-template --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-single --agent enabled=1 --ostype l26 creates: /etc/pve/qemu-server/{{ template_vmid }}.conf

- name: Import cloud image as disk ansible.builtin.command: cmd: qm importdisk {{ template_vmid }} /tmp/ubuntu-cloud.img local-lvm register: import_result changed_when: "'Successfully imported' in import_result.stdout"

- name: Attach disk to VM ansible.builtin.command: cmd: qm set {{ template_vmid }} --scsi0 local-lvm:vm-{{ template_vmid }}-disk-0,discard=on,ssd=1,iothread=1

- name: Add cloud-init drive ansible.builtin.command: cmd: qm set {{ template_vmid }} --ide2 local-lvm:cloudinit

- name: Set boot order ansible.builtin.command: cmd: qm set {{ template_vmid }} --boot order=scsi0

- name: Resize disk ansible.builtin.command: cmd: qm resize {{ template_vmid }} scsi0 32G

- name: Convert to template ansible.builtin.command: cmd: qm template {{ template_vmid }}

See also: Ansible Molecule Docker: Test Roles in Containers (Guide)

Manage Snapshots

    - name: Create snapshot before upgrade
      community.general.proxmox_snap:
        api_host: "{{ proxmox_host }}"
        api_token_id: "{{ proxmox_token_id }}"
        api_token_secret: "{{ proxmox_token_secret }}"
        hostname: "web-01"
        snapname: "pre-upgrade-{{ ansible_date_time.date }}"
        description: "Snapshot before OS upgrade"
        vmstate: false
        state: present

- name: Rollback to snapshot community.general.proxmox_snap: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" hostname: "web-01" snapname: "pre-upgrade-{{ ansible_date_time.date }}" state: rollback when: upgrade_failed | default(false)

Dynamic Inventory

Use the Proxmox dynamic inventory plugin to automatically discover VMs and containers:

# proxmox.yml (inventory file)
plugin: community.general.proxmox
url: https://pve1.home.lab:8006
token_id: "ansible@pam!automation"
token_secret: "{{ vault_proxmox_token }}"
validate_certs: false

# Group by tags groups: webservers: "'web' in (proxmox_tags_parsed | default([]))" databases: "'db' in (proxmox_tags_parsed | default([]))"

# Set connection variables compose: ansible_host: proxmox_ipconfig0.ip | default(proxmox_net0.ip) | ansible.utils.ipaddr('address')

# Only include running VMs filters: - proxmox_status == "running"

# Include both VMs and containers want_facts: true want_proxmox_nodes_ans_groups: true

# Test dynamic inventory
ansible-inventory -i proxmox.yml --graph
ansible-inventory -i proxmox.yml --list

Backup Automation

---
- name: Automated Proxmox backups
  hosts: localhost
  gather_facts: false

vars: backup_storage: "nfs-backup" backup_mode: "snapshot" vms_to_backup: - 100 # web-01 - 101 # web-02 - 200 # db-01

tasks: - name: Create backup for each VM community.general.proxmox_kvm: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" vmid: "{{ item }}" state: current loop: "{{ vms_to_backup }}"

- name: Trigger vzdump backup via API ansible.builtin.uri: url: "https://{{ proxmox_host }}:8006/api2/json/nodes/pve1/vzdump" method: POST headers: Authorization: "PVEAPIToken={{ proxmox_token_id }}={{ proxmox_token_secret }}" body_format: form-urlencoded body: vmid: "{{ item }}" storage: "{{ backup_storage }}" mode: "{{ backup_mode }}" compress: zstd notes-template: "Ansible backup {{ ansible_date_time.iso8601 }}" validate_certs: false loop: "{{ vms_to_backup }}"

- name: Clean old backups (keep last 7) ansible.builtin.shell: | # List backups sorted by date, remove all but last 7 ls -t /mnt/backup/dump/vzdump-qemu-{{ item }}-*.zst 2>/dev/null | tail -n +8 | xargs -r rm -f delegate_to: "{{ proxmox_host }}" loop: "{{ vms_to_backup }}"

Full Infrastructure as Code Example

# homelab.yml - Complete homelab infrastructure
---
- name: Deploy complete homelab
  hosts: localhost
  gather_facts: false

vars_files: - vars/proxmox-credentials.yml - vars/network.yml

vars: infrastructure: templates: - { vmid: 9000, name: "ubuntu-24.04", image: "noble-server-cloudimg-amd64.img" } - { vmid: 9001, name: "debian-12", image: "debian-12-genericcloud-amd64.qcow2" }

vms: - { name: "k3s-master", vmid: 110, cores: 4, memory: 8192, disk: 64, ip: "10.0.1.110/24", tags: ["k8s", "master"] } - { name: "k3s-worker-1", vmid: 111, cores: 4, memory: 8192, disk: 64, ip: "10.0.1.111/24", tags: ["k8s", "worker"] } - { name: "k3s-worker-2", vmid: 112, cores: 4, memory: 8192, disk: 64, ip: "10.0.1.112/24", tags: ["k8s", "worker"] } - { name: "nas", vmid: 120, cores: 2, memory: 4096, disk: 32, ip: "10.0.1.120/24", tags: ["storage"] }

containers: - { name: "pihole", vmid: 400, cores: 1, memory: 512, disk: 8, ip: "10.0.1.2/24", tags: ["dns"] } - { name: "wireguard", vmid: 401, cores: 1, memory: 256, disk: 4, ip: "10.0.1.3/24", tags: ["vpn"] } - { name: "prometheus", vmid: 402, cores: 2, memory: 2048, disk: 64, ip: "10.0.1.42/24", tags: ["monitoring"] } - { name: "grafana", vmid: 403, cores: 1, memory: 1024, disk: 16, ip: "10.0.1.43/24", tags: ["monitoring"] }

tasks: - name: Create VMs community.general.proxmox_kvm: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" clone: "ubuntu-24.04" name: "{{ item.name }}" vmid: "{{ item.vmid }}" cores: "{{ item.cores }}" memory: "{{ item.memory }}" full: true storage: "local-lvm" ipconfig: ipconfig0: "ip={{ item.ip }},gw=10.0.1.1" sshkeys: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}" tags: "{{ item.tags | join(';') }}" onboot: true state: present loop: "{{ infrastructure.vms }}"

- name: Create LXC containers community.general.proxmox: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" hostname: "{{ item.name }}" vmid: "{{ item.vmid }}" ostemplate: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst" storage: "local-lvm" disk: "{{ item.disk }}" cores: "{{ item.cores }}" memory: "{{ item.memory }}" netif: '{"net0":"name=eth0,bridge=vmbr0,ip={{ item.ip }},gw=10.0.1.1"}' pubkey: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}" unprivileged: true features: - nesting=1 tags: "{{ item.tags | join(';') }}" onboot: true state: present loop: "{{ infrastructure.containers }}"

- name: Start all VMs community.general.proxmox_kvm: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" vmid: "{{ item.vmid }}" state: started loop: "{{ infrastructure.vms }}"

- name: Start all containers community.general.proxmox: api_host: "{{ proxmox_host }}" api_token_id: "{{ proxmox_token_id }}" api_token_secret: "{{ proxmox_token_secret }}" node: "pve1" vmid: "{{ item.vmid }}" state: started loop: "{{ infrastructure.containers }}"

Proxmox Cluster Management

---
- name: Configure Proxmox cluster nodes
  hosts: proxmox_nodes
  become: true

tasks: - name: Configure cluster networking ansible.builtin.template: src: interfaces.j2 dest: /etc/network/interfaces notify: restart networking

- name: Set NTP synchronization ansible.builtin.lineinfile: path: /etc/chrony/chrony.conf line: "server {{ ntp_server }} iburst" notify: restart chrony

- name: Configure backup schedule ansible.builtin.copy: content: | # /etc/pve/vzdump.cron # Backup all VMs at 2 AM daily 0 2 * * * root vzdump --all --mode snapshot --storage nfs-backup --compress zstd --mailnotification failure --mailto admin@company.com dest: /etc/pve/vzdump.cron mode: '0644'

handlers: - name: restart networking ansible.builtin.systemd: name: networking state: restarted

- name: restart chrony ansible.builtin.systemd: name: chrony state: restarted

FAQ

Which Ansible module manages Proxmox VMs?

Use community.general.proxmox_kvm for KVM virtual machines and community.general.proxmox for LXC containers. Both require the proxmoxer Python library on your Ansible control node. Install with pip install proxmoxer requests.

Can Ansible manage Proxmox clusters?

Yes. Use the Proxmox modules for VM/container operations across any node in the cluster, and standard Ansible modules (template, systemd, package) for node configuration. The dynamic inventory plugin discovers all VMs/containers across the cluster automatically.

How do I use cloud-init templates with Ansible and Proxmox?

Create a cloud-init enabled template VM (import cloud image, add cloud-init drive, convert to template), then use community.general.proxmox_kvm with clone to create VMs from it. Set ipconfig, sshkeys, and nameservers parameters for automatic network and SSH configuration.

Is the Proxmox API token or password more secure?

API tokens are more secure — they can have limited permissions, don't expire with password changes, and can be revoked independently. Create a token: Datacenter → Permissions → API Tokens. Assign only the permissions needed (VM.Allocate, VM.Clone, Datastore.AllocateSpace).

Conclusion

Ansible's Proxmox modules turn your virtualization platform into a fully automated infrastructure-as-code environment. Define your VMs, containers, networks, and storage in YAML, version control the configuration, and provision or rebuild entire environments with a single command. Combined with dynamic inventory and cloud-init templates, you get a scalable, repeatable infrastructure pipeline for everything from homelabs to production Proxmox clusters.

Related Articles

Ansible vs Terraform: Complete Comparison GuideAnsible VMware Automation GuideAnsible Dynamic Inventory Complete GuideAnsible for KubernetesInstall Ansible Complete Guide

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home