# How to Deploy Your First Proxmox Virtual Machine Using Terraform

## 🧠 Why Terraform for Proxmox?

While Proxmox has a great web UI, infrastructure-as-code lets you:

* Automate repeatable VM deployments
    
* Keep configurations under version control
    
* Easily spin up multi-VM Setups
    
* Reduce human error
    

## **👋 Quick heads-up!**

This guide is **Part 2 of a multi-part series**.

In this article, we’ll walk through How to Deploy a Virtual Machine in proxmox Using Terraform

👉 [**Jump to Part 1:** How to Set Up Proxmox with Terraform **→**](https://automatestack.dev/how-to-set-up-proxmox-with-terraform-a-step-by-step-guide)

## 📦 Prerequisites

Before you begin, make sure you have:

✅ A running **Proxmox VE** host or cluster  
✅ A **user with API access** (e.g., `terraform-user@pve`)  
✅ A **cloud-init template VM** ready to be cloned  
✅ Terraform installed on your machine  
✅ Installed the **Telmate Proxmox Terraform provider**

## 🗂 Project Directory Structure

[Here’s the structure](https://automatestack.dev/how-to-deploy-your-first-proxmox-virtual-machine-using-terraform) of the deployment repo we’ll use:

```bash
proxmox-vm-deploy/
├── main.tf
├── provider.tf
├── variables.tf
├── terraform.tfvars
```

Let’s go through each file.

## 🧩 provider.tf – Connect Terraform to Proxmox

```bash
terraform {
  required_providers {
    proxmox = {
      source = "Telmate/proxmox"
      version = "3.0.2-rc01" # use the latest version available
    }
  }
}


provider "proxmox" {
  pm_api_url = var.proxmox_api_url # the variable is defined in terraform.tfvars.This should match the URL in your Proxmox web interface, typically something like "https://<proxmox-ip>:8006/api2/json"
  pm_parallel = 1
  pm_debug = false
  pm_tls_insecure = true
}
```

This sets up the connection to your Proxmox host. Make sure:

* The user exists in Proxmox (`pveum user add terraform-user@pve`)
    
* A suitable role (e.g., `Terraform_Provisioner`) with VM permissions is assigned
    
* load the API tokens for connecting to proxmox as environment variables
    

👉 [**Check out part-1 of the series for a step-by-step guide on creating the required users, roles and tokens to connect via terraform**](https://automatestack.dev/how-to-set-up-proxmox-with-terraform-a-step-by-step-guide)

## 📜 variables.tf – Input Configuration

```bash

variable "vm_name" {
  type = string
}

variable "clone" {
  type = string
}

variable "ipconfig0" {
  type = string
}

variable "vmid" {
  type = number
}

variable "memory" {
  type = number
}

variable "cores" {
  type = number
}

variable "disk_size" {
  type = string
  description = "The size of the disk, should be at least as big as the disk in the template"
  default = "20G"
  
}

variable "storage" {
  type = string
  description = "the storage where the VM disk will be created"
  
}

variable "ssh-public-key" {
    type = string
    description = "SSH public key for the VMs"
    sensitive = true
  
}

variable "proxmox_api_url" {
    type        = string
    description = "Proxmox API URL"
}

variable "target_node" {
    type        = string
    description = "The Proxmox node where the VM will be created"
  
}

variable "nameserver" {
    type        = string
    description = "Nameserver for the VM"
    default     = "1.1.1.1 8.8.8.8"
  
}

variable "cicustom" {
    type        = string
    description = "Cloud-Init custom configuration"
    default     = "vendor=local:snippets/qemu-guest-agent.yml"
  
}

variable "cipassword" {
    type        = string
    description = "Cloud-Init password for the VM"
    sensitive = true
  
}
```

These variables define how your VM will look—name, clone template, IP config, memory, CPU, etc.

## ⚙️ terraform.tfvars – Your Custom Values

```bash
ssh-public-key = "ssh-ed25519 AAAAC3NzaC1lZXXXXXXXXXXXXXXXXXXXXXXwSOCiZ/OkpPDR3bR2tK4STIm+gnJk"
target_node = "proxmox-server-IP" # The Proxmox node where the VM will be created
cicustom = "value=local:snippets/install-packages.yml" # /var/lib/vz/snippets/install-packages.yml #
cipassword = "ubuntu"
ipconfig0 = "ip=dhcp"
vmid = 1000 # optional, if not set, Proxmox will assign a random VMID
vm_name = "ubuntu-vm"
clone = "ubuntu-24-04-cloudinit-copy" # The template to clone from
cores = 4 # Number of CPU cores
memory = 4096 # Memory in MB
nameserver = "1.1.1.1 8.8.8.8"
disk_size = "20G" # The size of the disk, should be at least as big as the disk in the template
storage = "hdd-vm-data" # The storage where the VM disk will be created
```

This is your configuration layer—values you want to pass to variables. Keep this file out of version control (`.gitignore`) if it includes sensitive info.

## 🏗 main.tf – Create the Virtual Machine

```bash
#create a new VM from a template with cloud-init enabled
resource "proxmox_vm_qemu" "ubuntu-vm" {

  # Basic VM configuration
  vmid        = var.vmid
  name        = var.vm_name
  target_node = var.target_node # The node where the VM will be created
  agent       = 1 # Enable the QEMU guest agent
  cpu {
    cores = var.cores
    sockets = 1
    numa = true
    type = "x86-64-v2-AES"
  }
  memory      = var.memory # Memory in MB
  bios        = "ovmf" # Use OVMF for UEFI support
  boot        = "order=scsi0" # has to be the same as the OS disk of the template
  clone       = var.clone # The template to clone from
  scsihw      = "virtio-scsi-single" # Use VirtIO SCSI controller
  vm_state    = "running" # "running" or "stopped"
  automatic_reboot = true

  # Cloud-Init configuration
  cicustom   = var.cicustom
  ciupgrade  = true # it will upgrade the OS to the latest version
  nameserver = var.nameserver
  ipconfig0  = var.ipconfig0
  skip_ipv6  = true
  ciuser     = "root" # The user to use for the cloud-init script
  cipassword = var.cipassword # Password for the cloud-init user
  sshkeys    = var.ssh-public-key # The SSH public key to be added to the VM

  # Most cloud-init images require a serial device for their display
  serial {
    id = 0
  }

  # EFI disk for UEFI boot
  # This is required for cloud-init images that use UEFI
  # If your template does not use UEFI, you can remove this block
  efidisk {
    efitype = "4m" 
    storage = "hdd-vm-data"
  }

  # Disk configuration
  disks {
    scsi {
      scsi0 {
        # We have to specify the disk from our template, else Terraform will think it's not supposed to be there
        disk {
          storage = var.storage
          # The size of the disk should be at least as big as the disk in the template. If it's smaller, the disk will be recreated
          size    = var.disk_size
        }
      }

  # Some images require a cloud-init disk on the IDE controller, others on the SCSI or SATA controller
      scsi1 {
        cloudinit {
          storage = "hdd-vm-data"
        }
      }
    }
  }

  network {
    id = 0
    bridge = "vmbr0"
    model  = "virtio"
  }
}
```

Here’s what it does:

* Clones an existing **cloud-init-enabled VM template**
    
* Assigns VM name, IP, memory, CPU, etc.
    
* Injects **cloud-init config**, such as SSH key and default user
    
* Enables **QEMU guest agent** to enhance functionality
    

## ▶️ How to Run It

Open a terminal inside the project directory and follow these steps:

```bash
# use single quotes for the API token ID because of the exclamation mark
export PM_API_TOKEN_ID='terraform-user@pve!tf_token'
export PM_API_TOKEN_SECRET="XXXXXX-XXXX-XXXXX-XXXX-XXXXXXXXXXX"
```

```bash

# Initialize Terraform
terraform init
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1753675736255/8ec2c6e9-1606-41d3-91fe-67db3cd407e3.png align="left")

```bash

# Review execution plan
terraform plan

# Apply the configuration
terraform apply
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1753677529158/2fd2d130-891b-40b6-acb5-482f17396e32.png align="left")

## ✅ Verify in Proxmox

* Go to your Proxmox Web UI
    
* You’ll see a the VM
    
* Confirm network and SSH access
    
* Check if the VM booted from your template and has your custom config
    

---

## 🛠 Troubleshooting Tips

* **SSH not working?** Ensure cloud-init was enabled in your template and your public SSH key is valid.
    
* **Error: Permission denied?** Double-check the Proxmox user's role and permissions.
    
* **Wrong IP?** Validate your `ipconfig0` syntax (should follow `ip=x.x.x.x/xx,gw=x.x.x.x`).
    

---

## 🧪 What’s Next?

Once you get one VM working, you can:

* Create multiple VMs using `for_each`
    
* Automate post-deploy scripts via `null_resource` and `remote-exec`
    
* Turn this into a Kubernetes cluster or homelab infra stack!
