
September 08, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
You need a repeatable way to spin up Ubuntu VPS instances without clicking through five provider dashboards every time. This Terraform for VPS Provisioning Beginners Guide shows how to define servers, firewalls, and SSH access in version-controlled files. If you already ship Laravel or WordPress on bare VPS hosts, infrastructure as code with Terraform turns that manual setup into something you can replay in minutes. The examples below target PHP application stacks on Ubuntu 24.04, but the pattern works for any Linux workload.
What is Terraform for VPS provisioning and why use it?
Terraform is HashiCorp's open-source tool for declaring cloud resources in HCL files. You describe what you want—a VPS, a floating IP, a firewall rule—and Terraform talks to the provider API to create or update it. The same files work on your laptop, in CI, and on a teammate's machine.
On real client projects I maintain several sister sites on shared EC2 infrastructure with Deployer 7 and GitLab CI. Before Terraform, each new staging box meant manual clicks in the provider panel. After codifying the VPS layer, spinning up a mirror environment takes one pipeline job instead of an afternoon.
Terraform handles provisioning: creating the virtual machine, attaching storage, opening ports 22 and 443. It does not install PHP-FPM, Nginx, or MySQL. That work belongs to configuration management. The split is intentional and matches how most production teams operate. See the dedicated comparison in our Terraform vs Ansible guide for the full picture.
How do you install Terraform and pick a VPS provider?
Start with Terraform CLI 1.9 or later and a provider account. DigitalOcean and Hetzner Cloud are popular for budget VPS work. AWS EC2 fits when you already run other services in the same region. Pick one provider for your first project; multi-cloud can wait.
Install Terraform on Ubuntu or macOS
On Ubuntu 24.04, use HashiCorp's official apt repository:
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform Verify with terraform version. You should see Terraform v1.9+ and a provider list after your first init.
Generate provider API tokens
Each cloud needs a token stored outside your repo:
- DigitalOcean: Personal Access Token with read/write scope.
- Hetzner Cloud: Project API token from the Hetzner Cloud Console.
- AWS: IAM user with EC2, VPC, and security-group permissions.
Export tokens as environment variables. Never hard-code them in .tf files or commit terraform.tfvars with secrets to Git.
| Provider | Typical 2 vCPU / 4 GB VPS | Best for | Terraform provider |
|---|---|---|---|
| DigitalOcean | ~USD 24/month (~Rs 3,200) | Simple API, good docs, Nepal-friendly billing | digitalocean/digitalocean |
| Hetzner Cloud | ~USD 8/month (~Rs 1,100) | Lowest cost EU/US regions | hetznercloud/hcloud |
| AWS EC2 | ~USD 30+/month (~Rs 4,000) | Existing AWS workloads, IAM integration | hashicorp/aws |
| Linode (Akamai) | ~USD 24/month (~Rs 3,200) | Predictable pricing, solid support | linode/linode |
For Nepal-based clients on tight budgets, Hetzner or DigitalOcean often beat local dedicated hardware on total cost once you factor in power and backup. Our domain and hosting service covers the DNS side once your VPS IP is ready.
How do you write your first Terraform VPS configuration?
Create a project folder with a clear layout. Keep provider config, server definition, and variables in separate files from day one. You will thank yourself when the project grows.
Recommended file structure
terraform-vps/
├── main.tf # provider + droplet/resource
├── variables.tf # input declarations
├── terraform.tfvars # non-secret values (size, region)
├── outputs.tf # IP address, hostname
├── versions.tf # terraform + provider pins
└── .gitignore # .terraform/, *.tfstate, *.tfvars with secrets DigitalOcean example: Ubuntu VPS with firewall
Pin provider versions in versions.tf. This prevents surprise breaking changes during terraform init:
terraform {
required_version = ">= 1.9.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.40"
}
}
} The main resource block in main.tf:
provider "digitalocean" {
token = var.do_token
}
resource "digitalocean_ssh_key" "deploy" {
name = "deploy-key"
public_key = file("~/.ssh/id_ed25519.pub")
}
resource "digitalocean_droplet" "app" {
name = "laravel-staging"
region = var.region
size = var.droplet_size
image = "ubuntu-24-04-x64"
ssh_keys = [digitalocean_ssh_key.deploy.fingerprint]
tags = ["staging", "terraform-managed"]
}
resource "digitalocean_firewall" "app" {
name = "app-firewall"
droplet_ids = [digitalocean_droplet.app.id]
inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = ["YOUR_OFFICE_IP/32"]
}
inbound_rule {
protocol = "tcp"
port_range = "443"
source_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
} Declare variables in variables.tf. Our Terraform variables and outputs guide covers locals, defaults, and sensitive flags in depth.
variable "do_token" {
type = string
sensitive = true
}
variable "region" {
type = string
default = "sgp1"
}
variable "droplet_size" {
type = string
default = "s-2vcpu-4gb"
} Output the IP so your CI pipeline or Ansible inventory can consume it:
output "droplet_ip" {
value = digitalocean_droplet.app.ipv4_address
} How do you run Terraform safely with state and secrets?
State is Terraform's memory of what it created. Lose it and Terraform forgets your droplet exists. Commit it to a public repo and you expose internal IP addresses and resource IDs. Treat state like a database backup.
Local state for learning only
Your first terraform apply writes terraform.tfstate locally. That is fine for a throwaway lab box. Delete the VPS with terraform destroy when you finish experimenting.
Remote backend for anything shared
Once two people touch the same stack, move state to a remote backend. Options include AWS S3 plus DynamoDB locking, Terraform Cloud free tier, or a self-hosted OpenTofu-compatible backend. Our guide on managing Terraform state safely walks through S3 encryption and state locking step by step.
Pass secrets via environment variables, not files in Git:
export TF_VAR_do_token="dop_v1_xxxxxxxx"
terraform plan -out=plan.tfplan
terraform apply plan.tfplan The -out flag saves the approved plan. CI pipelines apply exactly what was reviewed, with no surprise drift. Use a strong password generator for database credentials you inject later via Ansible, not via Terraform outputs logged in plain text.
Workspaces and environments
Separate staging from production with Terraform workspaces or separate root modules. I have seen teams reuse one state file for both and accidentally destroy production during a staging experiment. Workspaces are cheap insurance. Read Terraform workspaces and environments before you add a second VPS.
How do you connect Terraform to your application deployment pipeline?
Provisioning stops at a booted Ubuntu box with SSH open. Your Laravel, Symfony, or WordPress stack still needs Nginx, PHP-FPM 8.3 or 8.5, MySQL 8.4, and a Deployer or GitLab CI hook. Chain the tools instead of forcing Terraform to run shell provisioners for everything.
Step-by-step: from apply to running app
- Run
terraform applyand capture the output IP. - Point your domain A record at that IP. Use your registrar or Cloudflare.
- Run an Ansible playbook or follow Laravel on Ubuntu VPS with Nginx.
- Configure GitLab CI or GitHub Actions to deploy on push, as in Deploy a Laravel app with GitLab CI/CD to a VPS.
- Enable nightly database dumps and monitor disk space. Small VPS instances fill up fast with log files.
I've used this exact split on sister sites sharing Deployer 7 and GitLab CI on shared EC2. Terraform creates the instance and security group. Ansible installs the LAMP or LEMP stack. Deployer handles zero-downtime releases with symlinked directories.
For WordPress migrations off managed hosting, provision the VPS first, then follow our WordPress migration to VPS guide. Symfony teams can mirror the same flow using the Symfony on Ubuntu VPS tutorial.
What mistakes do Terraform beginners make on VPS projects?
Most failures I troubleshoot are not Terraform syntax errors. They are operational gaps: missing lifecycle rules, open SSH to the world, or applying production changes without a plan review.
Common gotchas and fixes
- Hard-coded IPs in firewall rules. Use variables and update them when your office IP changes.
- No
prevent_destroyon production. Add a lifecycle block on critical droplets. One mistypeddestroyends badly. - Provisioner abuse. Remote-exec provisioners are fragile. Use Ansible playbooks for PHP server provisioning instead.
- Unpinned provider versions. A major provider upgrade during init can break your plan. Pin versions in
versions.tf. - Skipping
terraform fmtand validate. Add both to CI before apply, as shown in Terraform CI/CD with GitHub Actions.
Extract repeated patterns into modules once you manage more than two similar VPS stacks. The Terraform modules guide shows how to parameterise region, size, and tags without copy-pasting blocks.
Validate JSON outputs from your CI webhook handlers with a JSON formatter during pipeline debugging. Misformatted payload files cause more late-night pages than Terraform itself.
Sister sites such as Notary Kathmandu and Translation Nepal share the same deploy pipeline pattern. Terraform is not part of every small brochure site, but any portal handling bookings, documents, and payments benefits from reproducible infrastructure. See more examples on the portfolio page.
Official references worth bookmarking: the HashiCorp Terraform documentation and the DigitalOcean Terraform provider docs. Both stay current with provider schema changes.
If Terraform licensing concerns your team, evaluate OpenTofu as a drop-in fork. Functionality for basic VPS provisioning remains equivalent for most beginner stacks.
Ongoing server care—PHP version upgrades, opcache reloads after deploy, disk alerts—still needs a human or a managed service. Our Linux system administration service and support and maintenance plans cover that layer after Terraform hands you the keys.
Key Takeaways
- Define your VPS, SSH key, and firewall in HCL; run init, plan, then apply.
- Store state remotely with locking once anyone besides you touches the stack.
- Never commit API tokens—use TF_VAR environment variables or a secret manager.
- Terraform provisions the box; Ansible or shell scripts install PHP, Nginx, and MySQL.
- Pin provider versions and add terraform fmt plus validate to CI before apply.
- Chain Terraform output IPs into Deployer or GitLab CI for the same deploy path you use today.
People Also Ask
Can Terraform install PHP and Nginx on my VPS?
Technically yes, via remote-exec or cloud-init user data. In practice, Ansible or a well-tested shell script is more maintainable. Keep Terraform focused on the provider resources—droplet, firewall, volume, DNS record—and let configuration management handle packages and services.
Is Terraform free for personal VPS projects?
The Terraform CLI is free and open source. You pay only for the VPS itself—often USD 8–30 per month (~Rs 1,100–4,000). Terraform Cloud offers a free tier for remote state and CI integration if you outgrow local state files.
What is the difference between Terraform and Docker for VPS hosting?
Terraform creates the virtual machine and network rules on a cloud provider. Docker runs application containers inside an already-provisioned host. Many teams use both: Terraform for the VPS, Docker for app isolation, Nginx as reverse proxy on the host.
Should beginners start with Terraform or Ansible?
Start with Terraform if you need new VPS instances on demand. Start with Ansible if you already have a server and need to install software. Most production PHP stacks use Terraform first, then Ansible, then Deployer or CI for application code.
Ship reproducible VPS infrastructure this week
You now have a complete Terraform for VPS Provisioning Beginners Guide path: pick a provider, write HCL, protect state, and hand off to your existing deploy toolchain. Start with one staging droplet, destroy it cleanly, then codify production once the plan output looks right.
Need help wiring Terraform into a Laravel legal portal, WooCommerce store, or multi-site Deployer pipeline? Contact us or explore enterprise application development to scope the full stack—from VPS to production deploy.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

