Docker & DevOps

Terraform basics: provision VMs and infrastructure as code

Learn infrastructure as code with Terraform: providers, resources, variables and state, the init-plan-apply workflow, provisioning VMs with cloud-init, and keeping state safe.

IntermediateCloud and virtualization platforms2 min read

Terraform (and its open-source fork OpenTofu) lets you describe servers, networks and DNS records in code, then creates and updates them through each platform's API. This guide covers the Terraform basics you need to provision VMs reproducibly instead of clicking through panels.

Before you start

  • An account on a platform with a Terraform provider: most clouds, Proxmox VE (community providers), libvirt, and many DNS and CDN services.
  • An API token with limited permissions.
  • Terraform or OpenTofu installed from the official releases.

Core concepts

TermMeaning
ProviderPlugin that talks to a platform's API
ResourceSomething to create: a VM, disk, firewall, DNS record
Variable / outputInputs and returned values
StateTerraform's record of what it created, mapping code to real objects
ModuleReusable group of resources

A minimal configuration

The structure is the same for every provider; resource names and arguments differ, so check the provider's documentation. A generic example:

terraform {
  required_providers {
    example = {
      source  = "example/cloud"
    }
  }
}

variable "api_token" {
  type      = string
  sensitive = true
}

provider "example" {
  token = var.api_token
}

resource "example_server" "web" {
  name      = "web01"
  image     = "ubuntu-24.04"
  size      = "small"
  region    = "de"
  user_data = file("cloud-init.yaml")
}

output "web_ip" {
  value = example_server.web.ipv4_address
}

The user_data file bootstraps the VM; see cloud-init user-data.

The workflow

terraform init
terraform plan -out tfplan
terraform apply tfplan

init downloads providers, plan shows exactly what will be created, changed or destroyed, and apply executes it. To remove everything: terraform destroy.

Pass secrets safely

Provide tokens through environment variables (TF_VAR_api_token) or a secrets manager, never in committed files. Add *.tfvars containing secrets and .terraform/ to .gitignore.

Protect your state

  • State can contain IPs, passwords and keys. Do not commit terraform.tfstate to Git.
  • For teams, use a remote backend with locking (for example an S3-compatible bucket; see object storage).
  • Never edit state by hand; use terraform state commands.

Terraform and Ansible together

Terraform creates the VM and DNS records; Ansible configures software inside. See Ansible for VPS configuration.

Troubleshooting

  • Plan wants to replace a server: you changed an argument that forces recreation (such as image). Read the plan carefully.
  • State lock errors: another run is in progress, or a crashed run left a lock; release it only when sure.
  • Resource exists outside Terraform: bring it in with terraform import or an import block.

Frequently asked questions

Terraform or OpenTofu?

OpenTofu is an open-source fork compatible with most Terraform configurations. Choose based on licensing preferences and provider support.

Can Terraform manage Proxmox?

Yes, through community providers that use the Proxmox API.

Does Terraform install software?

Not really; use cloud-init or a configuration tool for that.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.