Mastering Proxmox Automation: CLI Tools and Infrastructure as Code for Your Homelab
Transform your Proxmox homelab from manual management to fully automated infrastructure with CLI tools, Terraform, Ansible, and modern DevOps practices.
Table of Contents
- The Manual Management Trap
- The Proxmox CLI Landscape
- The Core Tools
- pvesh: Your Automation Swiss Army Knife
- qm and pct: VM and Container Operations
- Infrastructure as Code: The Declarative Approach
- Terraform + OpenTofu: Provisioning Foundation
- Ansible: Configuration Management
- Packer: Automated Template Creation
- Putting It Together: Automation Workflows
- Workflow: CI/CD Pipeline for Test Environments
- Workflow: GitOps for Proxmox Configuration
- Backup Automation and Disaster Recovery
- Proxmox Backup Server Integration
- Offsite Replication
- Monitoring: See What’s Happening
- Deploy PVE Exporter
- Community Tools Worth Knowing
- Community Scripts Repository
- Getting Started: Your First Week
- Conclusion: From Homelab to Infrastructure
The Manual Management Trap
You’ve built an impressive homelab. Your Proxmox cluster hums along, running a dozen VMs and containers for everything from Home Assistant to your media server. But there’s a problem lurking beneath the surface: everything is configured manually.
When you need to spin up a new test VM, you click through the web UI. When a drive fails, you scramble to remember how you set up that ZFS pool six months ago. When you want to migrate a service, you’re manually exporting configs and hoping nothing breaks.
This isn’t sustainable. Worse, it’s brittle. One wrong click in the production VM’s settings, and you’re spending your Saturday night restoring from backup.
The solution? Automation and Infrastructure as Code.
In this guide, we’ll transform your Proxmox management from ad-hoc clicks to repeatable, version-controlled infrastructure. You’ll learn the native CLI tools that power Proxmox, how to integrate Terraform and Ansible for declarative management, and build workflows that make your homelab self-documenting and disaster-resistant.
The Proxmox CLI Landscape
Before diving into third-party tools, let’s understand what Proxmox provides out of the box. The native CLI tools are powerful—they’re what the web UI uses under the hood.
The Core Tools
Proxmox provides several command-line utilities that form the foundation of any automation:
| Tool | Purpose | Your Automation Foundation |
|---|---|---|
pvesh | REST API shell interface | Direct API access, scripting backbone |
qm | QEMU/KVM VM management | Create, clone, modify VMs |
pct | LXC container management | Container lifecycle operations |
pveceph | Ceph storage management | Distributed storage automation |
pveperf | Performance benchmarking | Capacity planning data |
pvesh: Your Automation Swiss Army Knife
pvesh is the most powerful native tool because it mirrors the entire REST API. Anything you can do in the web UI, you can do with pvesh:
# Get cluster information
pvesh get /version
# List all containers on a node
pvesh get /nodes/pve1/lxc
# Create a new user programmatically
pvesh create /access/users --userid automation@pve
# Create and start a container in one sequence
pvesh create /nodes/pve1/lxc \
-vmid 200 \
-hostname "monitoring" \
--storage local-lvm \
--password "secure-password-here" \
--ostemplate "local:vztmpl/ubuntu-24.04-standard.tar.gz" \
--memory 2048 \
--cores 2 \
--net0 "name=eth0,bridge=vmbr0,ip=dhcp"
pvesh create /nodes/pve1/lxc/200/status/start
Pro tip: Use pvesh get to explore available endpoints. Start with pvesh get / to see the top-level paths, then drill down. The API structure mirrors the web UI navigation.
qm and pct: VM and Container Operations
For day-to-day operations, qm (for VMs) and pct (for containers) are more convenient:
# Clone a VM template for testing
qm clone 9000 120 --name "ci-test-ubuntu" --full 1
# Apply cloud-init configuration
qm set 120 \
--cipassword 'TemporaryPassword!' \
--sshkeys /root/.ssh/authorized_keys \
--ipconfig0 ip=10.0.0.120/24,gw=10.0.0.1
# Start it up
qm start 120
# When testing is done, clean up
qm stop 120 --skiplock 1
qm destroy 120 --purge 1
For containers, the workflow is similar:
# Pull a container template
pveam download local ubuntu-24.04-standard
# Create a container
pct create 300 local:vztmpl/ubuntu-24.04-standard.tar.gz \
--hostname "webserver" \
--memory 1024 \
--cores 1 \
--rootfs local-lvm:8 \
--net0 "name=eth0,bridge=vmbr0,ip=dhcp"
# Execute commands inside
pct exec 300 -- apt update && apt install -y nginx
Be careful with --skiplock: It bypasses Proxmox’s safety mechanisms. Only use it in scripts where you’re certain the VM isn’t running critical operations. For production, let the graceful shutdown complete.
Infrastructure as Code: The Declarative Approach
While the CLI tools are powerful, they’re imperative—you’re describing how to do something. Infrastructure as Code (IaC) tools let you describe what you want, and the tool figures out the how.
This distinction matters. With imperative scripts, changing a VM’s memory requires remembering what command to run. With declarative IaC, you update a configuration file and apply it—the tool detects the change and makes only the necessary updates.
Terraform + OpenTofu: Provisioning Foundation
The BPG Terraform Provider has become the gold standard for Proxmox integration. It supports both Terraform and OpenTofu (the open-source Terraform fork), giving you flexibility in your toolchain.
# main.tf
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~> 0.60"
}
}
}
provider "proxmox" {
endpoint = "https://proxmox.local:8006/api2/json"
username = "root@pam"
api_token = var.proxmox_api_token
insecure = true # Only for homelab with self-signed certs
}
# Define a reusable VM module
module "ubuntu_server" {
source = "./modules/vm"
name = "webserver-01"
target_node = "pve1"
vm_id = 100
cpu_cores = 2
memory_mb = 4096
disk_gb = 20
network_bridge = "vmbr0"
storage_pool = "local-lvm"
template_id = 9000
}
The power becomes apparent when provisioning multiple similar resources:
# Provision 3 worker nodes for a Kubernetes cluster
locals {
k8s_workers = ["worker-01", "worker-02", "worker-03"]
}
resource "proxmox_virtual_environment_vm" "k8s_worker" {
for_each = toset(local.k8s_workers)
name = each.value
node_name = "pve1"
vm_id = 200 + index(local.k8s_workers, each.value)
clone {
vm_id = 9000 # Your base template
full = true
}
cpu {
cores = 4
type = "host"
}
memory {
dedicated = 8192
}
# Cloud-init for automatic IP assignment
initialization {
ip_config {
ipv4 {
address = "10.0.0.${201 + index(local.k8s_workers, each.value)}/24"
gateway = "10.0.0.1"
}
}
user_account {
username = "admin"
ssh_keys = [file("~/.ssh/id_rsa.pub")]
}
}
}
Apply it:
tofu init
tofu plan
tofu apply
Need to scale up? Add another entry to the list, run tofu apply, and Proxmox provisions the new VM with identical configuration.
Start with templates: Create a minimal Ubuntu/Debian template with cloud-init pre-configured. All your VMs will then derive from a consistent base. The BPG provider handles cloud-init customization automatically.
Ansible: Configuration Management
Terraform excels at provisioning, but once VMs exist, you need to configure them. That’s where Ansible shines.
# playbooks/proxmox-provision.yml
---
- name: Create Proxmox VMs
hosts: localhost
connection: local
tasks:
- name: Create web server VM
community.general.proxmox_kvm:
api_host: "proxmox.local"
api_user: "root@pam"
api_token_id: "{{ vault_proxmox_token }}"
api_token_secret: "{{ vault_proxmox_secret }}"
node: "pve1"
vmid: "100"
name: "webserver"
cores: 2
memory: 4096
disk: "local-lvm:20,format=qcow2"
netif: '{"net0":"virtio,bridge=vmbr0"}'
state: present
clone: "ubuntu-24.04-template"
- name: Configure web server
hosts: webservers
become: true
tasks:
- name: Install NGINX
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Deploy application
ansible.builtin.git:
repo: "https://github.com/yourorg/webapp"
dest: /var/www/webapp
version: main
The real power comes from combining both tools in a pipeline:
- Terraform provisions VMs from templates with basic networking
- Ansible installs software and configures services
- Both configurations are version-controlled in Git
Store secrets properly: Never commit API tokens or passwords to Git. Use Ansible Vault, HashiCorp Vault, or environment variables. The extra setup time pays off when you need to share configs with others or recover after a disaster.
Packer: Automated Template Creation
Templates shouldn’t be created manually. Packer automates building consistent base images:
# ubuntu-24.04.pkr.hcl
packer {
required_plugins {
proxmox = {
version = ">= 1.1.8"
source = "github.com/hashicorp/proxmox"
}
}
}
source "proxmox-iso" "ubuntu" {
proxmox_url = "https://proxmox.local:8006/api2/json"
username = "root@pam"
token = var.proxmox_api_token
node = "pve1"
vm_name = "ubuntu-24.04-golden"
vm_id = 9000
iso_url = "https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso"
iso_storage_pool = "local"
iso_checksum = "file:https://releases.ubuntu.com/24.04/SHA256SUMS"
unmount_iso = true
format = "qcow2"
ssh_username = "installer"
ssh_password = "temporary"
boot_command = [
"<wait><enter>",
"<wait5>o<enter>",
"install<enter>"
]
# Cloud-init preparation
cloud_init = true
cloud_init_storage_pool = "local-lvm"
}
build {
sources = ["source.proxmox-iso.ubuntu"]
provisioner "shell" {
inline = [
"apt-get update",
"apt-get install -y qemu-guest-agent cloud-init",
"systemctl enable qemu-guest-agent"
]
}
}
Build templates on-demand:
packer init .
packer build -var "proxmox_api_token=${TF_VAR_proxmox_api_token}" .
Now you have reproducible templates. Security patch released? Rebuild the template, re-apply Terraform, and every new VM inherits the patches.
Putting It Together: Automation Workflows
Let’s walk through practical workflows you can implement today.
Workflow: CI/CD Pipeline for Test Environments
Create disposable test environments on every commit:
# .gitlab-ci.yml
variables:
PROXMOX_API_ENDPOINT: "https://proxmox.local:8006/api2/json"
stages:
- provision
- test
- destroy
provision_test_vm:
stage: provision
script:
- tofu init
- tofu apply -auto-approve -var="vm_name=test-${CI_COMMIT_SHA}"
- echo "TEST_VM_ID=$(tofu output vm_id)" > deploy.env
artifacts:
reports:
dotenv: deploy.env
run_tests:
stage: test
script:
- ansible-playbook -i "${TEST_VM_ID}," test-playbook.yml
needs:
- provision_test_vm
cleanup:
stage: destroy
script:
- tofu destroy -auto-approve
when: always # Run even if previous stages fail
Workflow: GitOps for Proxmox Configuration
Version control your Proxmox host configurations:
#!/bin/bash
# /opt/scripts/sync-pve-config.sh
CONFIG_REPO="/opt/pve-config-git"
LOG_FILE="/var/log/pve-config-sync.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
cd "$CONFIG_REPO" || exit 1
# Sync critical configs
rsync -a /etc/pve/storage.cfg "${CONFIG_REPO}/storage.cfg"
rsync -a /etc/pve/datacenter.cfg "${CONFIG_REPO}/datacenter.cfg"
rsync -a /etc/pve/qemu-server/ "${CONFIG_REPO}/qemu-server/"
rsync -a /etc/pve/lxc/ "${CONFIG_REPO}/lxc/" --exclude='*.pending'
rsync -a /etc/network/interfaces "${CONFIG_REPO}/interfaces"
# Commit changes
git add -A
if git diff --staged --quiet; then
log "No changes detected"
else
git commit -m "Auto-sync: $(date '+%Y-%m-%d %H:%M')"
git push origin main
log "Changes committed and pushed"
fi
Add to cron:
# Sync every 6 hours
0 */6 * * * /opt/scripts/sync-pve-config.sh
Exclude sensitive files: Don’t sync files containing passwords or API tokens. Use .gitignore in your config repo and consider separate secret management.
Backup Automation and Disaster Recovery
Automation isn’t just about convenience—it’s about resilience. Your backup strategy should be as automated as everything else.
Proxmox Backup Server Integration
Proxmox Backup Server (PBS) provides enterprise-grade backup for free:
- Incremental forever: Only changed blocks are backed up
- Deduplication: Save storage across similar VMs
- Encryption: Files are encrypted at rest
- Scheduling: Set-and-forget retention policies
# Add PBS to Proxmox
pvesm add pbs pbs-backup \
--server pbs.local \
--datastore main \
--username root@pam \
--password "${PBS_PASSWORD}" \
--fingerprint "${PBS_FINGERPRINT}"
# Configure automatic backups
pvesh create /cluster/backup \
--storage pbs-backup \
--mode schedule \
--dow 'mon,tue,wed,thu,fri' \
--starttime "02:00" \
--compress zstd \
--mode snapshot
Offsite Replication
For true disaster recovery, replicate backups offsite:
# On your PBS server, configure remote sync
# /etc/proxmox-backup/sync-remote.cfg
[remote:offsite]
remote = offsite-pbs.example.com
remote-store = backup-store
schedule = "daily" 02:00
This implements the 3-2-1 rule: 3 copies of data, on 2 different media types, with 1 stored offsite.
Test your restores: The most important backup statistic isn’t how many you’ve made—it’s how many you’ve successfully restored. Schedule quarterly restore tests and document the results.
Monitoring: See What’s Happening
Automated infrastructure needs automated monitoring. The standard stack remains Prometheus + Grafana.
Deploy PVE Exporter
# Install on each Proxmox node
apt install -y prometheus-pve-exporter
# Configure API access
cat > /etc/default/prometheus-pve-exporter << 'EOF'
PVE_USER=root@pam
PVE_PASSWORD="${PVE_PASSWORD}"
PVE_VERIFY_SSL=false
EOF
systemctl enable --now prometheus-pve-exporter
Add to your Prometheus config:
# prometheus.yml
scrape_configs:
- job_name: 'proxmox'
static_configs:
- targets:
- 'pve1:9221'
- 'pve2:9221'
- 'pve3:9221'
relabel_configs:
- source_labels: [__address__]
target_label: instance
replacement: "${1}:8006"
Import a pre-built Grafana dashboard, and you have real-time visibility into CPU, memory, storage, and VM states across your cluster.
Community Tools Worth Knowing
Beyond the core tools, the Proxmox community has built utilities worth exploring:
| Tool | Purpose | Why It Matters |
|---|---|---|
| ProxMenux | Terminal management UI | k9s-like interface for Proxmox |
| PVE-AutoSnap | Snapshot automation | Retention policies without manual cleanup |
| Community Scripts | 400+ setup scripts | Quick deployments of popular containers |
| Proxmox Ntfy | Push notifications | Get alerts on your phone without complex setup |
Community Scripts Repository
The Proxmox Community Scripts project provides ready-to-use automation for common setups:
# Install Home Assistant in LXC
bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/homeassistant.sh)"
# Install Pi-hole
bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/pihole.sh)"
# Post-install optimizations
bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/misc/post-pve-install.sh)"
Review before running: Always read the script source before executing. These are community-contributed, and while most are excellent, understanding what you’re running is good hygiene.
Getting Started: Your First Week
Ready to start automating? Here’s a realistic first-week plan:
Day 1-2: Learn the CLI
- Practice
qm,pct, andpveshcommands - Create and destroy test VMs from the command line
- Understand the API structure with
pvesh get
Day 3-4: Set up Infrastructure as Code
- Install Terraform or OpenTofu
- Create a simple VM template
- Write your first resource definition
- Run
tofu planandtofu apply
Day 5: Add Ansible
- Write a playbook to install software on your Terraform VMs
- Integrate with your existing configs
Day 6-7: GitOps and Backups
- Initialize a Git repo for your configs
- Set up PBS if you haven’t already
- Schedule automated backups
Conclusion: From Homelab to Infrastructure
The journey from manual management to automated infrastructure transforms more than just your workflow—it changes how you think about your systems.
When every VM is defined in code, you stop asking “what did I configure?” and start asking “what do I want to configure?” Template-based provisioning means you can rebuild any service in minutes, not hours. Version control means you can see exactly what changed and when. And automated backups mean you’re not hoping disasters won’t happen—you’re prepared for when they do.
The tools we covered—Proxmox’s native CLI, Terraform/OpenTofu, Ansible, Packer, and backup automation—work together to create a cohesive workflow:
- Define your desired state in code
- Provision resources consistently with templates
- Configure services automatically with Ansible
- Monitor health with Prometheus
- Protect everything with automated backups
Your homelab doesn’t need enterprise complexity to benefit from these practices. Start small—automate one VM’s provisioning with Terraform. Add a backup schedule to PBS. Version control your /etc/pve configs.
Before you know it, you’ll have an infrastructure that’s not just automated—it’s self-documenting, reproducible, and resilient.
And when someone asks “how did you set this up?” you can finally answer: “Let me show you the code.”

Comments
Powered by GitHub Discussions