▤ Homelab

OpenTofu and Terraform in the homelab: virtual machines as code

How to describe homelab VMs and containers in OpenTofu or Terraform files: Proxmox providers, state, variables, secrets and the plan and apply workflow.

Creating virtual machines through the web interface is perfectly fine for first experiments. After a few months, though, nobody remembers why one VM has 6 GB of RAM instead of 4, or which options were enabled on the reverse proxy's VM. Infrastructure as code solves this: machines are described in text files, and a tool creates, changes or deletes resources until reality matches the description.

Terraform and OpenTofu

Terraform is the tool that made this approach popular. In 2023 its developer changed its licence to a non-open-source one; in response OpenTofu was born, a fork that stayed open source and is hosted by the Linux Foundation. The two tools share the configuration language, called HCL, and largely the providers as well. For a homelab, OpenTofu is the natural choice if you want to stay with free software; the concepts below apply to both.

The fundamental concepts

  • Provider: the plugin that knows how to talk to a specific system, such as the Proxmox VE API. Community-maintained providers exist for Proxmox, and it is worth choosing an actively developed one after checking its documentation.
  • Resource: an object to manage, for example a virtual machine or a container.
  • State: a file where the tool records which resources it created and with which attributes. That is how it knows what to change next time.
  • Plan and apply: tofu plan shows the changes that would be made; tofu apply carries them out after confirmation.

An example workflow

A minimal configuration has three parts: the provider, variables and resources. In simplified form, a virtual machine cloned from a cloud-init template might be described like this:

resource "proxmox_virtual_environment_vm" "monitor" {
  name      = "monitor"
  node_name = "pve1"
  clone { vm_id = 9000 }
  cpu    { cores = 2 }
  memory { dedicated = 4096 }
  initialization {
    ip_config {
      ipv4 {
        address = "192.168.20.30/24"
        gateway = "192.168.20.1"
      }
    }
  }
}

The exact block names depend on the provider you choose, so adapt the example to its documentation. The workflow is always the same:

  1. tofu init downloads the provider;
  2. tofu plan shows that a VM will be created;
  3. tofu apply creates it;
  4. you change the memory in the file, tofu plan shows a single change, tofu apply applies it.

Credentials and secrets

The tool connects to the hypervisor API with an API token. Some rules:

  • create a dedicated user with only the permissions needed, rather than the main admin account;
  • do not write tokens and passwords in versioned files: pass them through environment variables or variable files excluded from the repository;
  • remember that the state file can contain sensitive values in plain text; protect it like a secret and never publish it.

OpenTofu also offers state file encryption, a feature Terraform does not include in its free edition. For a homelab whose repository lives on an external service, that is one more reason to prefer it.

Where to keep state

At home the simplest solution is local state, in a folder that is backed up. If you work from several computers, you can use an S3-compatible remote backend, such as a self-hosted object storage service. What matters is that there is a single authoritative copy: two diverging states lead the tool to create duplicates or delete resources unexpectedly.

Splitting the work with configuration management

OpenTofu creates the infrastructure: machines, disks, networks. Configuring what is inside the machines, meaning packages, files and services, is the job of configuration management tools such as Ansible, or of cloud-init itself. A clear split works well:

Layer Tool
Creating VMs and containers OpenTofu or Terraform
First boot, users, SSH keys cloud-init
Packages, configuration, services Ansible
Applications versioned Compose files

When it pays off, and when it does not

In my work I find infrastructure as code repays the effort when there are more than a handful of machines, when they are rebuilt often or when you want to be able to recreate everything after a failure. For a homelab with three VMs that never change, the gain is mostly educational, and that has value too, because these are the same skills used in the cloud.

A practical tip: do not import all your existing infrastructure at once. Start by managing only new machines with code, and bring the rest over one piece at a time.

In short

  • Machines are described in HCL files; plan shows the changes, apply carries them out.
  • OpenTofu is the open source fork of Terraform, with compatible language and providers.
  • Use API tokens with minimal permissions and keep secrets out of the repository.
  • The state file is sensitive and must exist as a single authoritative copy.
  • OpenTofu creates the machines; cloud-init and Ansible configure what is inside.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also