Manually creating LXC containers and VMs through the Proxmox web UI works for a handful of servers, but once you cross twenty containers across multiple VLANs, you need something repeatable. That is where GitOps comes in: your infrastructure definition lives in Git, and every push triggers an automated pipeline that applies the changes.
This guide covers the full stack: Proxmox API token creation, Terraform/OpenTofu provider setup, LXC and VM resource definitions, remote state management, and a CI/CD pipeline that turns git push into deployed infrastructure.
All examples target Proxmox VE 8.x with Terraform 1.6+ or OpenTofu 1.6+. I recommend OpenTofu for homelabs — it is the fully open-source fork with no licence changes to worry about.
1. Proxmox API Token for Terraform
Terraform needs API access to your Proxmox host or cluster. Create a dedicated user and token so you can revoke access without touching your root credentials.
|
|
The last command prints a token ID and secret. Save the secret immediately — Proxmox will not show it again. Store it in your password manager or encrypted vault.
For a more restricted setup, create a custom role with only the permissions Terraform needs (VM.Allocate, Datastore.AllocateSpace, Sys.Audit) instead of full Administrator.
2. Provider and Backend Configuration
The community provider bpg/proxmox is the most actively maintained
option and supports both LXC and QEMU resources.
Create providers.tf:
|
|
Create terraform.tfvars with your actual values (add to
.gitignore):
|
|
3. LXC Container with Declarative Config
Define an LXC container in containers/web01/main.tf:
|
|
For multiple similar containers, use a module:
|
|
4. VM Deployment from Template
For full VMs, first build a template with Packer, then clone it with Terraform. This separates the image build (Packer) from the deployment (Terraform).
Packer template snippet proxmox-debian.pkr.hcl:
|
|
Terraform VM resource cloning the template:
|
|
5. Remote State — Required for GitOps
Local terraform.tfstate does not work in a GitOps pipeline because
every CI runner starts fresh. You need a remote backend.
Option A — S3-compatible (MinIO, garage):
|
|
Option B — HTTP backend (Gitea or GitLab):
|
|
6. Repository Layout
iac/
├── environments/
│ ├── prod/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── terraform.tfvars # gitignored
│ │ └── terraform.tfvars.example
│ └── staging/
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
├── modules/
│ ├── lxc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── vm/
│ ├── main.tf
│ └── variables.tf
├── .github/workflows/ (or .gitea/workflows/)
├── providers.tf
└── README.md
Each environment has its own .tfvars with VLAN IDs, IP ranges, and
datastore paths specific to that environment.
7. Gitea Actions CI/CD Pipeline
A Gitea Actions workflow that validates, plans, and applies on push
to the main branch:
|
|
The environment: production step adds a manual approval gate in
Gitea/GitHub before Terraform applies changes. Pull requests run only
validate and plan — you see exactly what will change before merging.
8. Full Workflow in Action
Here is what happens when you update a container’s CPU allocation:
- Edit
environments/prod/main.tf— changecores = 2tocores = 4 - Commit and push to a feature branch
- The CI pipeline runs
terraform fmt --check,validate, andplan - The plan shows: “proxmox_virtual_environment_container.web01 will be updated”
- Open a pull request, review the plan output
- Merge to
main - The apply job runs with manual approval
- Terraform updates the container — CPU limit changed, zero downtime
Adding a new LXC is just adding a new resource block to the configuration:
|
|
Commit, push, approve — the cache container appears on your Proxmox host. The same workflow works for VM resource changes, network updates, or datastore reconfigurations.
9. Proxmox GitOps Benefits
| Manual workflow | GitOps workflow |
|---|---|
| Click through web UI | Edit YAML/HCL in Git |
| No change history | Full git log of every change |
| Easy to skip steps | CI enforces validation |
| Human error risk | Idempotent applies |
| No rollback plan | git revert restores state |
Conclusion
A Proxmox GitOps workflow with Terraform or OpenTofu eliminates the guesswork from infrastructure management. Every LXC and VM is defined declaratively, tracked in Git, and deployed through a repeatable CI/CD pipeline that validates before applying.
Start small: create an API token, define one LXC container with the
bpg/proxmox provider, and set up remote state in MinIO. Once that
works, add the CI pipeline and expand to VMs. The infrastructure
drift that plagues manual setups disappears when commits drive
changes.
For the next step, pair this with Ansible for post-deployment configuration management — Terraform provisions the hardware, Ansible configures the software inside it.