Skip to content
Back to Blog
Proxmox

Automating Proxmox VE with Terraform

Use the Terraform Proxmox provider to provision VMs, containers, and storage as code with declarative infrastructure management.

Sep 2025
13 min read

Introduction

The Proxmox Terraform provider (bpg/proxmox) allows you to manage Proxmox VE resources — VMs, containers, storage, networks — using Infrastructure as Code. This enables repeatable, version-controlled infrastructure deployments.

Prerequisites

  • Proxmox VE 7.x or 8.x cluster
  • Terraform 1.x installed
  • API token from Proxmox

Creating a Proxmox API Token

In Proxmox UI: Datacenter → Permissions → API Tokens → Add

BASH
# Or via CLI
pveum apitoken add terraform@pve!terraform --privsep 0
pveum aclmod / -user terraform@pve -role Administrator

Terraform Provider Configuration

HCL
# versions.tf
terraform {
  required_providers {
    proxmox = {
      source  = "bpg/proxmox"
      version = "~> 0.46"
    }
  }
}

# provider.tf
provider "proxmox" {
  endpoint  = "https://proxmox.company.com:8006"
  api_token = "terraform@pve!terraform=UUID-TOKEN-HERE"
  insecure  = false   # Set true if self-signed cert
}

Creating a VM from Template

HCL
# main.tf
resource "proxmox_virtual_environment_vm" "web_server" {
  name      = "web-server-01"
  node_name = "pve-node1"
  vm_id     = 201

  clone {
    vm_id   = 9000   # Source template ID
    full    = true   # Full clone
  }

  cpu {
    cores   = 4
    sockets = 1
    type    = "x86-64-v2-AES"
  }

  memory {
    dedicated = 4096  # MB
    floating  = 512   # Ballooning minimum
  }

  disk {
    datastore_id = "local-zfs"
    interface    = "scsi0"
    size         = 50      # GB
    iothread     = true
    discard      = "on"
  }

  network_device {
    bridge   = "vmbr0"
    vlan_id  = 10
    model    = "virtio"
  }

  initialization {
    ip_config {
      ipv4 {
        address = "192.168.10.50/24"
        gateway = "192.168.10.1"
      }
    }
    user_account {
      username = "admin"
      keys     = [file("~/.ssh/id_rsa.pub")]
    }
    dns {
      servers = ["8.8.8.8", "8.8.4.4"]
    }
  }

  started = true
}

Creating Multiple VMs with count

HCL
variable "web_server_count" {
  default = 3
}

resource "proxmox_virtual_environment_vm" "web_cluster" {
  count     = var.web_server_count
  name      = "web-${count.index + 1}"
  node_name = "pve-node${(count.index % 3) + 1}"  # Distribute across 3 nodes
  vm_id     = 201 + count.index

  clone {
    vm_id = 9000
    full  = true
  }

  cpu {
    cores = 2
  }

  memory {
    dedicated = 2048
  }

  initialization {
    ip_config {
      ipv4 {
        address = "192.168.10.${50 + count.index}/24"
        gateway = "192.168.10.1"
      }
    }
  }
}

output "vm_ips" {
  value = [for vm in proxmox_virtual_environment_vm.web_cluster : 
           vm.initialization[0].ip_config[0].ipv4[0].address]
}

LXC Container with Terraform

HCL
resource "proxmox_virtual_environment_container" "app_container" {
  description = "Application container"
  node_name   = "pve-node1"

  initialization {
    hostname = "app-container-01"
    
    ip_config {
      ipv4 {
        address = "192.168.10.100/24"
        gateway = "192.168.10.1"
      }
    }
    
    user_account {
      keys     = [file("~/.ssh/id_rsa.pub")]
      password = "SecurePassword123"
    }
  }

  network_interface {
    name   = "eth0"
    bridge = "vmbr0"
  }

  disk {
    datastore_id = "local-zfs"
    size         = 20
  }

  cpu {
    cores = 2
  }

  memory {
    dedicated = 1024
    swap      = 512
  }

  operating_system {
    template_file_id = "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"
    type             = "ubuntu"
  }

  started  = true
  unprivileged = true
}

Remote State and Workspaces

HCL
# backend.tf - Store state in S3 or HTTP backend
terraform {
  backend "s3" {
    bucket = "terraform-state"
    key    = "proxmox/production/terraform.tfstate"
    region = "us-east-1"
  }
}
BASH
# Initialize and apply
terraform init
terraform plan -out=tfplan
terraform apply tfplan

# Destroy specific resource
terraform destroy -target=proxmox_virtual_environment_vm.web_server

# Import existing VM
terraform import proxmox_virtual_environment_vm.existing_vm pve-node1/qemu/200