OpenTofu is an open-source fork of Terraform, maintained by the Linux Foundation under the MPL 2.0 license. We switched to it after Terraform Cloud’s per-resource pricing got too expensive for our team. This post covers how to install OpenTofu on WSL2 Ubuntu, why it exists, and the common commands you’ll use daily. Examples are run on WSL2 Ubuntu in Windows, but they work on any Ubuntu system.
OpenTofu vs Terraform: What’s Different?
Same HCL syntax, same provider ecosystem, nearly identical CLI. Just replace terraform with tofu. But there are real differences.
Licensing
| Aspect | OpenTofu | Terraform |
|---|---|---|
| License | MPL 2.0 (OSI-approved open source) | BSL 1.1 (source-available, not open source) |
| Commercial use | Unrestricted | Restricted for competitive offerings |
| Governance | Linux Foundation, community-driven | HashiCorp/IBM, single-vendor controlled |
Prerequisites
- WSL2 with Ubuntu — see How to Install Ubuntu 20.04 or 22.04 in WSL 2 on Windows 10
- AWS CLI configured — follow How to Install AWS CLI v2 on Ubuntu 22.04
- AWS credentials set up (
aws configureor SSO)
How to Install OpenTofu on WSL2 Ubuntu
Two options: the quick installer script, or manual APT repository setup.
Option 1: Installer Script (Quick)
curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh
chmod +x install-opentofu.sh
./install-opentofu.sh --install-method deb
rm -f install-opentofu.sh
The script downloads GPG keys, adds the repo, and installs the tofu package. The --install-method deb flag tells it to use APT.
Option 2: Manual APT Repository Setup
Step 1: Install dependencies.
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
Step 2: Add GPG keys.
sudo install -m 0755 -d /etc/apt/keyrings
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
sudo chmod a+r /etc/apt/keyrings/opentofu.gpg /etc/apt/keyrings/opentofu-repo.gpg
Step 3: Add the repository.
echo \
"deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] https://packages.opentofu.org/opentofu/tofu/any/ any main
deb-src [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 chmod a+r /etc/apt/sources.list.d/opentofu.list
Step 4: Install.
sudo apt-get update
sudo apt-get install -y tofu
Verify the Installation
tofu version
Expected output:
OpenTofu v1.11.5
on linux_amd64
The binary is tofu, not opentofu.
Running Your First tofu init
Create a project directory and a basic main.tf:
mkdir ~/my-infra && cd ~/my-infra
Add this to main.tf:
terraform {
required_version = ">= 1.11.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "your-tfstate-bucket"
key = "my-infra/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = "ap-southeast-1"
}
OpenTofu still uses the terraform {} block name — it’s backward compatible. Replace your-tfstate-bucket and terraform-locks with your actual values. No remote backend yet? Remove the backend "s3" block and it’ll use local state.
Initialize:
tofu init
This downloads providers, configures the backend, and creates the .terraform.lock.hcl lock file. Output looks like:
Initializing the backend...
Successfully configured the backend "s3"! OpenTofu will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.x.x...
- Installed hashicorp/aws v5.x.x
OpenTofu has been successfully initialized!
If you were previously using Terraform on WSL Ubuntu, same workflow — different command name.
Common OpenTofu Commands
Core Workflow
| Command | What It Does |
|---|---|
tofu init | Initialize project, download providers and modules |
tofu validate | Check configuration syntax |
tofu plan | Preview changes |
tofu apply | Apply changes to infrastructure |
tofu destroy | Tear down all managed resources |
Planning and Applying
Save a plan and apply it later (useful for CI/CD):
tofu plan -out=tfplan
tofu apply tfplan
Skip the confirmation prompt:
tofu apply -auto-approve
Target or exclude specific resources:
# Target a specific resource
tofu apply -target=aws_instance.web_server
# Exclude a resource (OpenTofu-only)
tofu plan -exclude=module.monitoring
Working with Variables
tofu plan -var 'instance_type=t3.medium'
tofu plan -var-file=production.tfvars
Importing Existing Resources
Bring manually-created infrastructure under OpenTofu management:
tofu import aws_instance.web_server i-0abc123def456789
You need the resource block written in your .tf file first. After importing, run tofu plan to check for drift and adjust your config to match.
Or use import blocks for a declarative approach — better for teams since the import is in code:
import {
to = aws_instance.web_server
id = "i-0abc123def456789"
}
resource "aws_instance" "web_server" {
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
}
Run tofu plan and tofu apply — import happens as part of the regular workflow.
State Management
# List all resources in state
tofu state list
# Show details of a specific resource
tofu state show aws_instance.web_server
# Download state from remote backend
tofu state pull > terraform.tfstate.backup
# Push local state to remote backend (use carefully)
tofu state push terraform.tfstate
# Rename a resource in state (prevents destroy + recreate)
tofu state mv aws_instance.old_name aws_instance.new_name
# Remove resource from state without destroying it
tofu state rm aws_instance.web_server
Other Useful Commands
# Format .tf files
tofu fmt
# Show outputs
tofu output
# Show current state in readable format
tofu show
# Workspace management
tofu workspace list
tofu workspace new staging
tofu workspace select staging
# Unlock a stuck state lock
tofu force-unlock LOCK_ID
Migrating from Terraform to OpenTofu
The migration is straightforward:
- Install OpenTofu alongside Terraform (different binaries, no conflict)
- Run
tofu initin your existing project — it reads the same.tffiles - Run
tofu plan— if the plan is clean, migration is done - Update CI/CD pipelines to use
tofuinstead ofterraform - Remove Terraform when you’re confident
Watch out for:
required_versionpinned to a specific Terraform version — update to>= 1.11.0or remove it- Provider hash mismatches — run
tofu init -upgradeto regenerate the lock file - Terraform Cloud backend — pull state first with
terraform state pull, configure a new backend (S3, GCS), then push withtofu state push
If you had Terraform set up before, check How to Upgrade Terraform to a Specific Version to understand how it was installed so you can cleanly remove it.
Bonus: Client-Side State Encryption
One of OpenTofu’s best features. With Terraform, state is plain JSON — even with S3 server-side encryption, the data travels unencrypted to the API. OpenTofu encrypts it locally before sending.
Basic example with passphrase-based encryption:
terraform {
encryption {
key_provider "pbkdf2" "main" {
passphrase = var.state_passphrase
}
method "aes_gcm" "main" {
keys = key_provider.pbkdf2.main
}
state {
method = method.aes_gcm.main
}
plan {
method = method.aes_gcm.main
}
}
}
For production, use AWS KMS or another KMS instead of a passphrase.
Conclusion
OpenTofu is a drop-in replacement for Terraform that’s genuinely open source, has features Terraform doesn’t, and costs nothing. If Terraform Cloud pricing is eating into your budget, switching to OpenTofu with an S3 backend is worth the 30 minutes it takes.
If you’re setting up AWS from scratch, check How to Configure AWS SSO CLI Access for Linux Ubuntu to avoid managing long-lived access keys. For managing multiple OpenTofu versions, tofuenv works like nvm but for tofu.