TerraformPilot

DevOps

Terraform for Proxmox VE: Provision Virtual Machines from Templates

Provision VMs on Proxmox VE with Terraform using the bpg/proxmox provider: API token auth, cloud-init, LXC containers, and storage pools.

LLuca Berton1 min read

Proxmox VE is the most popular open-source hypervisor for homelabs and small/mid-business virtualization in 2026. The community-maintained bpg/proxmox Terraform provider has effectively become the standard, replacing the older telmate/proxmox. This guide shows how to provision VMs and LXC containers on Proxmox with Terraform using API tokens and cloud-init.

Quick Pattern (TL;DR)

#
terraform {
  required_providers {
    proxmox = {
      source  = "bpg/proxmox"
      version = "~> 0.66"
    }
  }
}
 
provider "proxmox" {
  endpoint  = "https://pve.lab.example.com:8006/"
  api_token = "terraform@pve!tf=${var.pve_token_secret}"
  insecure  = false
 
  ssh {
    agent    = true
    username = "root"
  }
}

Create an API Token in Proxmox

#

On the Proxmox host:

pveum user add terraform@pve
pveum aclmod / -user terraform@pve -role PVEVMAdmin
pveum user token add terraform@pve tf --privsep 0
# Save the printed secret to TF_VAR_pve_token_secret

Cloud-Init VM from a Template

#
resource "proxmox_virtual_environment_vm" "web" {
  name        = "web-01"
  description = "Provisioned by Terraform"
  node_name   = "pve1"
  vm_id       = 9001
 
  agent { enabled = true }
 
  cpu {
    cores = 2
    type  = "host"
  }
 
  memory {
    dedicated = 4096
  }
 
  clone {
    vm_id = 9000   # ubuntu-24.04 template
    full  = true
  }
 
  disk {
    datastore_id = "local-lvm"
    interface    = "scsi0"
    size         = 32
  }
 
  network_device {
    bridge = "vmbr0"
  }
 
  initialization {
    ip_config {
      ipv4 {
        address = "10.0.10.21/24"
        gateway = "10.0.10.1"
      }
    }
 
    user_account {
      username = "ubuntu"
      keys     = [trimspace(file("~/.ssh/id_ed25519.pub"))]
    }
  }
 
  operating_system {
    type = "l26"
  }
}

LXC Container

#
resource "proxmox_virtual_environment_container" "ct" {
  description = "Web LXC"
  node_name   = "pve1"
  vm_id       = 9100
 
  initialization {
    hostname = "web-ct"
 
    ip_config {
      ipv4 {
        address = "10.0.10.31/24"
        gateway = "10.0.10.1"
      }
    }
 
    user_account {
      keys     = [trimspace(file("~/.ssh/id_ed25519.pub"))]
      password = var.ct_password
    }
  }
 
  network_interface {
    name   = "veth0"
    bridge = "vmbr0"
  }
 
  operating_system {
    template_file_id = "local:vztmpl/ubuntu-24.04-standard_24.04-1_amd64.tar.zst"
    type             = "ubuntu"
  }
 
  cpu { cores = 2 }
  memory { dedicated = 1024 }
  disk { datastore_id = "local-lvm"; size = 8 }
}

Best Practices

#
  • Use API tokens, not root passwords — and keep privsep = 0 only when the role grants enough.
  • Build your template with Packer (cloud-init enabled, qemu-guest-agent installed) — Terraform clones, never installs.
  • Pin bpg/proxmox to a minor version — the provider iterates fast.
  • Stagger vm_id by environment (lab 9000s, staging 8000s) so PVE backups stay readable.
  • Enable agent and IP reporting so Terraform sees the IP without asking DHCP.
#
#Terraform#Proxmox#Virtualization#Cloud-init#Homelab

Share this article