Managing Proxmox VMs and LXCs with Terraform
There are a few Proxmox provider implementations available on the Terraform registry, this post specifically uses this one: https://registry.terraform.io/providers/loeken/proxmox/latest/docs Other implementations might behave slightly differently but I haven't tried. This particular implementation has a weird behavior: it tries to recreate the disk for a virtual machine when running terraform apply - which then fails on the server's side. Fortunately it doesn't stop me from creating more resources e.g. when I add a new LXC to the file, it will still create the LXC just fine, so it kinda "works". Compared to other services that support IaC (e.g. an AWS EC2 instance), I'm not sure if it's this particular implementation, or if it's the API on Proxmox's side, the whole experience does not feel smooth. It wants to do some "updates" when I haven't changed a single letter, which then doesn't actually seem to change anything, again it mostly "works", and it's better than manually clicking through a few steps each time I want to create a new VM/LXC through the web UI, so it's good enough to me. My Terraform file looks like this, hopefully it's helpful to you if you are trying to do something similar: terraform { required_providers { proxmox = { source = "loeken/proxmox" version = ">=2.9.0" } } required_version = ">= 0.14" } provider "proxmox" { # Default HTTPS port of Proxmox is 8006, yours might be different pm_api_url = "https://xxxx:8006/api2/json" # I thought this was required as my Proxmox server uses a self-signed certificate. But apparently it works without this set to true anyway #pm_tls_insecure = true # Obviously not the best practice, you should use environment variables PM_USER and PM_PASS instead. I only do this because I am in a home lab environment pm_user = "root@pam" pm_password = "secureultrapluspromax" } # https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/vm_qemu resource "proxmox_vm_qemu" "resource_name_here" { name = "VM name here" target_node = "Name of the node (under the Datacenter node)" vmid = 200 cores = 16 memory = 65536 # MiB os_type = "ubuntu" # The volume name under the target node "local", then the storage type "ISO Images", then the image's name iso = "local:iso/ubuntu-24.10-live-server-amd64.iso" disk { type = "scsi" size = "50G" storage = "local-lvm" } network { bridge = "vmbr0" firewall = false link_down = false model = "e1000" } } # https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/lxc resource "proxmox_lxc" "resource_name_here" { target_node = "Name of the node (under the Datacenter node)" hostname = "hostname" # The volume name under the target node "local", then the storage type "CT Templates", then the template's name ostemplate = "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst" # Root password password = "secureultrapluspromax" unprivileged = true vmid = 201 cores = 4 memory = 4096 # MiB // Terraform will crash without rootfs defined rootfs { storage = "local-lvm" size = "2G" } features { mount = "nfs" } # Comments from the provider's doc, I keep it here to remind myself of this weird bug # // NFS share mounted on host # // Without 'volume' defined, Proxmox will try to create a volume with # // the value of 'storage' + : + 'size' (without the trailing G) - e.g. # // "/srv/host/bind-mount-point:256". # // This behaviour looks to be caused by a bug in the provider. mountpoint { key = "0" slot = 0 storage = "/mnt/mountpoint" volume = "/mnt/mountpoint" mp = "/mnt/data" # Unintuitively (and if I remember correctly), this does not work without specifying a size size = "1T" } network { name = "eth0" bridge = "vmbr0" ip = "dhcp" } }
There are a few Proxmox provider implementations available on the Terraform registry, this post specifically uses this one: https://registry.terraform.io/providers/loeken/proxmox/latest/docs
Other implementations might behave slightly differently but I haven't tried. This particular implementation has a weird behavior: it tries to recreate the disk for a virtual machine when running terraform apply
- which then fails on the server's side. Fortunately it doesn't stop me from creating more resources e.g. when I add a new LXC to the file, it will still create the LXC just fine, so it kinda "works".
Compared to other services that support IaC (e.g. an AWS EC2 instance), I'm not sure if it's this particular implementation, or if it's the API on Proxmox's side, the whole experience does not feel smooth. It wants to do some "updates" when I haven't changed a single letter, which then doesn't actually seem to change anything, again it mostly "works", and it's better than manually clicking through a few steps each time I want to create a new VM/LXC through the web UI, so it's good enough to me.
My Terraform file looks like this, hopefully it's helpful to you if you are trying to do something similar:
terraform {
required_providers {
proxmox = {
source = "loeken/proxmox"
version = ">=2.9.0"
}
}
required_version = ">= 0.14"
}
provider "proxmox" {
# Default HTTPS port of Proxmox is 8006, yours might be different
pm_api_url = "https://xxxx:8006/api2/json"
# I thought this was required as my Proxmox server uses a self-signed certificate. But apparently it works without this set to true anyway
#pm_tls_insecure = true
# Obviously not the best practice, you should use environment variables PM_USER and PM_PASS instead. I only do this because I am in a home lab environment
pm_user = "root@pam"
pm_password = "secureultrapluspromax"
}
# https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/vm_qemu
resource "proxmox_vm_qemu" "resource_name_here" {
name = "VM name here"
target_node = "Name of the node (under the Datacenter node)"
vmid = 200
cores = 16
memory = 65536 # MiB
os_type = "ubuntu"
# The volume name under the target node "local", then the storage type "ISO Images", then the image's name
iso = "local:iso/ubuntu-24.10-live-server-amd64.iso"
disk {
type = "scsi"
size = "50G"
storage = "local-lvm"
}
network {
bridge = "vmbr0"
firewall = false
link_down = false
model = "e1000"
}
}
# https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/lxc
resource "proxmox_lxc" "resource_name_here" {
target_node = "Name of the node (under the Datacenter node)"
hostname = "hostname"
# The volume name under the target node "local", then the storage type "CT Templates", then the template's name
ostemplate = "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"
# Root password
password = "secureultrapluspromax"
unprivileged = true
vmid = 201
cores = 4
memory = 4096 # MiB
// Terraform will crash without rootfs defined
rootfs {
storage = "local-lvm"
size = "2G"
}
features {
mount = "nfs"
}
# Comments from the provider's doc, I keep it here to remind myself of this weird bug
# // NFS share mounted on host
# // Without 'volume' defined, Proxmox will try to create a volume with
# // the value of 'storage' + : + 'size' (without the trailing G) - e.g.
# // "/srv/host/bind-mount-point:256".
# // This behaviour looks to be caused by a bug in the provider.
mountpoint {
key = "0"
slot = 0
storage = "/mnt/mountpoint"
volume = "/mnt/mountpoint"
mp = "/mnt/data"
# Unintuitively (and if I remember correctly), this does not work without specifying a size
size = "1T"
}
network {
name = "eth0"
bridge = "vmbr0"
ip = "dhcp"
}
}