Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Terraform Ubuntu
GUIDEInstall Guides

How to Install Terraform on Ubuntu 24.04 — Infrastructure as Code

16 min read

How to Install Terraform on Ubuntu 24.04 — Infrastructure as Code

If you still create VPS instances, DNS records, and Kubernetes clusters by clicking buttons in a dashboard, you are leaving speed, reproducibility, and sanity on the table. Terraform is the de facto standard for Infrastructure as Code (IaC) — a declarative language that lets you describe your entire stack in text files, version it in Git, review it in pull requests, and roll it out (or roll it back) with a single command.

In this guide you will install Terraform on Ubuntu 24.04 LTS three different ways, understand the 2023 licensing drama that led to the OpenTofu fork, and write your first real main.tf file that provisions actual cloud resources. By the end you will have a working IaC workflow, know how to manage remote state safely, and understand the commands that matter in production.

What Is Terraform?

Terraform is an open-source (well, formerly open-source — more on that in a moment) IaC tool created by HashiCorp in 2014. Instead of writing shell scripts or clicking through cloud consoles, you declare the desired state of your infrastructure in HashiCorp Configuration Language (HCL):

hcl
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  tags = {
    Name = "production-web"
  }
}

Terraform compares that desired state against reality (tracked in a state file), calculates the diff, and applies the changes. It works with over 3,000 providers — AWS, Azure, GCP, Contabo, Cloudflare, Kubernetes, GitHub, Datadog, Stripe, and almost every API you can imagine.

Why teams adopt Terraform:

  • Reproducibility — the same config spins up identical dev, staging, and prod environments.
  • Auditability — every change lives in Git with an author, timestamp, and review trail.
  • Disaster recovery — lose a region? terraform apply rebuilds it in minutes.
  • Multi-cloud — one language for AWS, GCP, Azure, and everything else.

The BSL License Controversy and the OpenTofu Fork

In August 2023, HashiCorp dropped a bomb on the IaC community: Terraform — along with Vault, Consul, and Nomad — was relicensed from the permissive Mozilla Public License 2.0 (MPL) to the Business Source License (BSL) 1.1. In plain English, the BSL prohibits anyone from offering a competing commercial product built on top of Terraform. It is not an open-source license by OSI standards.

The community revolted. Within weeks, a coalition including Gruntwork, Spacelift, Harness, and Env0 announced OpenTF — a hard fork of the last MPL-licensed Terraform commit. The project was quickly adopted by the Linux Foundation, renamed OpenTofu, and cut its first GA release (1.6.0) in January 2024.

OpenTofu is a drop-in replacement for Terraform. It reads the same HCL, uses the same provider protocol, and supports the same state format. Binaries are named tofu instead of terraform, but most teams alias them.

In April 2025, OpenTofu 1.9 shipped features Terraform still lacks — notably native OCI registry support for modules and providers, early variable evaluation, and provider-defined functions across all blocks.

Which should you pick? If you are starting fresh, OpenTofu is the safer long-term bet: truly open source, Linux Foundation governance, no vendor lock-in. If you rely on HCP Terraform (formerly Terraform Cloud) or specific enterprise integrations, stick with HashiCorp. We cover installing both below.

Prerequisites

  • Ubuntu 24.04 LTS (also works on 22.04)
  • sudo access
  • At least 512 MB of RAM and 1 GB of free disk
  • A target cloud account (AWS, GCP, Contabo, Cloudflare — anything with an API)
A CloudCore Starter VPS is more than enough to act as your IaC control node.

Method 1: Install Terraform via HashiCorp's Official APT Repository

This is the official HashiCorp-recommended path and the one you want for long-term, patched, apt-managed installs.

Step 1 — Install dependencies:

bash
sudo apt update
sudo apt install -y gnupg software-properties-common curl

Step 2 — Add HashiCorp's GPG key:

bash
wget -O- https://apt.releases.hashicorp.com/gpg | \
  sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

Step 3 — Verify the key fingerprint (should be 798A EC65 4E5C 1542 8C8E 42EE AA16 FCBC A621 E701):

bash
gpg --no-default-keyring \
  --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \
  --fingerprint

Step 4 — Add the repository:

bash
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

Step 5 — Install Terraform:

bash
sudo apt update
sudo apt install -y terraform

Step 6 — Verify:

bash
terraform version

Terraform v1.9.x

on linux_amd64

Enable tab-completion while you are here:

bash
terraform -install-autocomplete

Method 2: Install OpenTofu (The Linux Foundation Fork)

OpenTofu ships its own apt repo and a convenient installer script.

Option A — One-line installer:

bash
curl --proto '=https' --tlsv1.2 -fsSL \
  https://get.opentofu.org/install-opentofu.sh | sh -s -- --install-method deb

Option B — Manual apt repo:

bash
# Add the GPG keys
curl -fsSL https://get.opentofu.org/opentofu.gpg | \
  sudo tee /etc/apt/keyrings/opentofu.gpg >/dev/null
curl -fsSL https://packages.opentofu.org/opentofu/tofu/gpgkey | \
  sudo gpg --no-tty --batch --dearmor -o /etc/apt/keyrings/opentofu-repo.gpg >/dev/null

Add the repo

echo "deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] \ https://packages.opentofu.org/opentofu/tofu/any/ any main" | \ sudo tee /etc/apt/sources.list.d/opentofu.list >/dev/null

sudo apt update sudo apt install -y tofu

Verify:

bash
tofu version

OpenTofu v1.9.x

From here on, anywhere you see terraform <cmd> you can substitute tofu <cmd> — the UX is intentionally identical.

Method 3: Install tfenv for Multiple Versions

If you manage several projects pinned to different Terraform versions — which is common in agencies and large orgs — use tfenv, a version manager modelled after pyenv and rbenv.

bash
git clone https://github.com/tfutils/tfenv.git ~/.tfenv
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

List available versions

tfenv list-remote | head -20

Install a specific version

tfenv install 1.9.8 tfenv install 1.5.7 # last MPL-licensed release

Switch globally

tfenv use 1.9.8

tfenv respects a .terraform-version file in any project directory:

bash
cd ~/projects/legacy-infra
echo "1.5.7" > .terraform-version
terraform version   # automatically uses 1.5.7

For OpenTofu, the equivalent tool is tofuenv (git clone https://github.com/tofuutils/tofuenv.git ~/.tofuenv).

Your First Terraform File

Let's provision a real resource — a Cloudflare DNS record, because it is free, fast, and failure-safe.

bash
mkdir -p ~/terraform-demo && cd ~/terraform-demo

Create main.tf:

hcl
terraform {
  required_version = ">= 1.5.0"

required_providers { cloudflare = { source = "cloudflare/cloudflare" version = "~> 4.0" } } }

provider "cloudflare" { api_token = var.cloudflare_api_token }

variable "cloudflare_api_token" { description = "Cloudflare API token with DNS:Edit scope" type = string sensitive = true }

variable "zone_id" { description = "Cloudflare zone ID" type = string }

resource "cloudflare_record" "demo" { zone_id = var.zone_id name = "iac-demo" value = "192.0.2.1" type = "A" ttl = 300 proxied = false }

output "fqdn" { description = "Fully qualified domain name of the created record" value = cloudflare_record.demo.hostname }

Create terraform.tfvars (never commit this file):

hcl
cloudflare_api_token = "your-scoped-token-here"
zone_id              = "your-zone-id"

Add .gitignore:

gitignore
*.tfvars
*.tfstate
*.tfstate.backup
.terraform/
.terraform.lock.hcl.bak
crash.log

The init / plan / apply / destroy Workflow

Four commands carry 95% of your daily work.

1. terraform init — downloads providers, initialises the backend, and builds .terraform/:

bash
terraform init

2. terraform plan — shows exactly what Terraform will change, without changing anything:

bash
terraform plan -out=tfplan

Read this output carefully. Lines starting with + mean create, - means destroy, ~ means update-in-place, and -/+ means destroy-then-recreate (dangerous for stateful resources).

3. terraform apply — executes the saved plan:

bash
terraform apply tfplan

Without a saved plan, apply asks for interactive confirmation.

4. terraform destroy — tears down everything Terraform manages in that workspace:

bash
terraform destroy

Use this in ephemeral environments (preview branches, CI test sandboxes). Never run it against production without triple-checking -target flags and backups.

Popular Providers Worth Knowing

  • hashicorp/aws — AWS (EC2, S3, Lambda, IAM, RDS, EKS)
  • hashicorp/google — GCP
  • hashicorp/azurerm — Azure
  • cloudflare/cloudflare — DNS, WAF, Workers, R2
  • hashicorp/kubernetes — any Kubernetes cluster
  • hashicorp/helm — Helm chart installs
  • digitalocean/digitalocean — Droplets, Spaces, Kubernetes
  • hetznercloud/hcloud — Hetzner Cloud
  • ovh/ovh — OVHcloud
  • contabo/contabo — community Contabo provider
  • integrations/github — repos, branch protection, Actions secrets
  • vercel/vercel — Vercel projects and deployments
  • datadog/datadog — monitors, dashboards, SLOs
  • stripe/stripe — products, prices, webhooks (yes, really)
Browse the full registry at registry.terraform.io or OpenTofu Registry.

State Management: Local vs Remote

The state file (terraform.tfstate) is Terraform's memory. It maps your HCL resources to real-world IDs, stores attribute values, and tracks dependencies. Lose it and you lose the ability to safely manage your infrastructure.

Local State (Default)

Fine for solo learning. Never for team or production use — concurrent runs corrupt state, and the file often contains secrets in plain text.

Remote State with S3 Backend

hcl
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/network/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

S3 stores state; DynamoDB provides state locking so two engineers cannot apply at once. Enable bucket versioning and server-side encryption.

Remote State with GCS Backend

hcl
terraform {
  backend "gcs" {
    bucket = "my-tf-state"
    prefix = "prod/network"
  }
}

GCS includes native object-level locking — no separate lock table needed.

Other Backends

  • HCP Terraform / Terraform Cloud — free tier, built-in runs and state
  • Azure Blob Storage — native locking
  • Consul, etcd — for the HashiCorp faithful
  • Terraform HTTP backend — Spacelift, Scalr, Env0 all implement this
Initialise or migrate with:

bash
terraform init -migrate-state

Variables and Outputs

Variables make configs reusable. Three input styles:

hcl
variable "instance_count" {
  description = "Number of app instances"
  type        = number
  default     = 3
  validation {
    condition     = var.instance_count >= 1 && var.instance_count <= 10
    error_message = "instance_count must be between 1 and 10."
  }
}

variable "tags" { type = map(string) default = { Environment = "prod" } }

variable "allowed_cidrs" { type = list(string) default = ["10.0.0.0/8"] }

Values flow in from (highest precedence first): -var CLI flag, -var-file, terraform.tfvars, .auto.tfvars, environment (TF_VAR_), then default.

Outputs expose values to humans and to other modules:

hcl
output "db_endpoint" {
  description = "RDS endpoint for the application"
  value       = aws_db_instance.main.endpoint
  sensitive   = false
}

output "db_password" { value = aws_db_instance.main.password sensitive = true # redacted in CLI output }

Read outputs with terraform output or terraform output -json.

Modules

A module is a directory containing .tf files. Modules are the unit of reuse.

hcl
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.8.1"

name = "prod-vpc" cidr = "10.0.0.0/16"

azs = ["us-east-1a", "us-east-1b", "us-east-1c"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

enable_nat_gateway = true single_nat_gateway = true

tags = { Environment = "production" Terraform = "true" } }

Always pin the version — modules are mutable; pinning protects you from upstream breaks. Popular community modules: terraform-aws-modules/eks, terraform-aws-modules/rds, terraform-google-modules/network, cloudposse/label.

Workspaces

Workspaces give you multiple state files from one config — handy for dev, staging, prod variants:

bash
terraform workspace new dev
terraform workspace new prod
terraform workspace select prod
terraform workspace list

Reference the current workspace in HCL:

hcl
resource "aws_instance" "app" {
  instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"
  tags = {
    Env = terraform.workspace
  }
}

Caveat: workspaces share the same backend config and provider credentials. For stronger isolation between production and everything else, use separate root modules in different directories with different backends, not workspaces.

terraform fmt and validate

Two commands that belong in every commit:

bash
terraform fmt -recursive    # canonical formatting
terraform validate          # syntax and schema check

fmt is opinionated and non-negotiable in well-run teams. validate catches typos, missing required args, and type mismatches without calling any cloud APIs.

Pre-commit Hooks

Stop bad HCL from ever reaching main. Install pre-commit:

bash
pip install pre-commit

Create .pre-commit-config.yaml:

yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.89.1
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
      - id: terraform_tflint
      - id: terraform_trivy         # security scan
      - id: terraform_docs          # auto-generated README

Then:

bash
pre-commit install
pre-commit run --all-files

CI/CD Integration

GitHub Actions example:

yaml
name: Terraform

on: pull_request: paths: ["terraform/**"] push: branches: [main]

jobs: terraform: runs-on: ubuntu-24.04 defaults: run: working-directory: ./terraform steps: - uses: actions/checkout@v4

- uses: hashicorp/setup-terraform@v3 with: terraform_version: 1.9.8

- name: Terraform fmt run: terraform fmt -check -recursive

- name: Terraform init run: terraform init env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

- name: Terraform validate run: terraform validate

- name: Terraform plan if: github.event_name == 'pull_request' run: terraform plan -no-color

- name: Terraform apply if: github.ref == 'refs/heads/main' run: terraform apply -auto-approve

For OpenTofu, swap hashicorp/setup-terraform@v3 for opentofu/setup-opentofu@v1.

Drift Detection

"Drift" is when reality diverges from what Terraform believes — usually because someone clicked in a console. Detect it nightly:

bash
terraform plan -detailed-exitcode

exit 0 = no changes

exit 1 = error

exit 2 = drift detected

Wire that into a cron or a scheduled CI job and alert on exit 2. Tools like driftctl, Spacelift, and env0 offer richer drift UIs and continuous monitoring.

Destroy Command Safety

terraform destroy is the single most dangerous command in your toolbox. Guardrails:

  • Never run destroy against production without an explicit ticket and a second pair of eyes.
  • Use prevent_destroy on critical resources:
  • hcl
    resource "aws_db_instance" "main" {
         # ...
         lifecycle {
           prevent_destroy = true
         }
       }

  • In CI, require manual approval for destroy jobs (GitHub Environments with protection rules).
  • Use -target to remove one resource rather than everything.
  • Always back up state before destroy: cp terraform.tfstate terraform.tfstate.$(date +%s).bak.
  • Best Practices

    • Separate state files per environment and per domain. prod/network, prod/app, staging/network. Blast radius stays small.
    • Never commit .tfvars that contain secrets. Use environment vars, SOPS, Vault, or cloud secret managers.
    • Pin everything — Terraform version, provider versions, module versions. terraform.lock.hcl locks providers; commit it.
    • Use remote state with locking from day one. Even solo projects benefit.
    • Enable detailed logging when stuck: TF_LOG=DEBUG terraform apply 2> debug.log.
    • Run terraform fmt and validate on every commit via pre-commit.
    • Tag every resource with Environment, Owner, CostCentre, and ManagedBy=terraform.
    • Use data sources to read, not to write — look up existing VPCs, AMIs, secrets.
    • Small, focused modules beat megamodules. Aim for modules that do one thing well.
    • Write module READMEs with terraform-docs — auto-generated, always current.
    • Review plans, not applies. Plan output in PR comments is your friend.

    Troubleshooting

    "Error acquiring the state lock" — a previous run crashed. Inspect with terraform force-unlock <LOCK_ID>. Only do this after confirming no other run is active.

    "Provider produced inconsistent result after apply" — provider bug. Upgrade the provider: terraform init -upgrade.

    "Failed to install provider" — network, proxy, or registry mirror. Check ~/.terraformrc for mirror config.

    State file corrupted — restore from S3 versioning (always enable it) or from the automatic .backup sibling file.

    "Resource already exists" — something was created out-of-band. Import it:

    bash
    terraform import aws_s3_bucket.existing my-bucket-name

    Slow plans on large state — split the state. Refactor monoliths into root modules by domain.

    Provider auth failures — export credentials explicitly:

    bash
    export AWS_PROFILE=prod
    export TF_LOG=DEBUG
    terraform plan

    FAQ

    Terraform vs OpenTofu — is OpenTofu really a drop-in replacement? For 99% of configs, yes. OpenTofu 1.6 started from the last MPL Terraform commit and has stayed compatible. Diverging features (OCI registries, early variable evaluation) are additive — existing configs still work. Do a plan diff before switching production.

    Terraform vs Pulumi — which should I pick? Terraform uses HCL, a purpose-built declarative language. Pulumi uses real programming languages (TypeScript, Python, Go, C#). HCL is simpler and easier to review; Pulumi is more flexible for complex logic. The IaC ecosystem (modules, providers, tooling) is an order of magnitude larger for Terraform/OpenTofu.

    Is HCL hard to learn? No. You can be productive in a day. The difficulty lies in cloud-provider semantics, not HCL itself.

    What about CloudFormation, Bicep, or AWS CDK? Great if you are single-cloud and want native integration. Terraform wins the moment you touch more than one provider — which most teams eventually do.

    Is OpenTofu production-ready? Yes. Version 1.6 GA shipped in January 2024; the Linux Foundation backs it; companies including Spacelift, Gruntwork, and Harness run it in production. Many large orgs have migrated entirely.

    Can I run both Terraform and OpenTofu on the same state? Technically yes — they read the same format. In practice, pick one per project to avoid version-skew headaches.

    How do I handle secrets? Never in .tfvars committed to git. Options: environment variables (TF_VAR_db_password), SOPS + age, HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, or your CI/CD's encrypted secrets. Mark output and variable blocks sensitive = true.

    Do I need Terraform Cloud / HCP Terraform? No. S3 + DynamoDB (or GCS) provides the same core value — remote state and locking — for pennies. HCP Terraform adds policy-as-code, run management, and a UI that many teams find worth the cost.

    How do I upgrade Terraform versions? Bump required_version, test in staging, run terraform init -upgrade, run plan, review. Never skip major versions without reading the upgrade guide.

    Next Steps

    You have Terraform or OpenTofu installed, you understand the core workflow, and you know how to manage state safely. From here:

  • Provision a full VPS stack with Terraform — user, firewall, Docker, Caddy — on a CloudCore Starter from vps-server.host.
  • Move your state to an S3 + DynamoDB backend with encryption and locking.
  • Refactor into environment-specific root modules with shared child modules.
  • Wire GitHub Actions to run fmt, validate, and plan on every PR, and apply on merge to main.
  • Set up nightly drift detection and alerts.
  • Explore companion tools: tflint, trivy, terraform-docs, infracost, checkov.
  • Read HashiCorp's official Terraform: Up & Running (Yevgeniy Brikman) for deeper patterns.
  • Infrastructure as Code is not just a tool change — it is a cultural one. Commits become deployments, reviews become change control, and main becomes the source of truth for your entire production stack. Start small, pin everything, automate the boring parts, and let the declarative model do the heavy lifting.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket