DevOps

Install Terraform / OpenTofu on Ubuntu 26.04 LTS

The IaC landscape split in 2023 when HashiCorp moved Terraform from the Mozilla Public License to the Business Source License (BSL 1.1). That decision sparked OpenTofu, a community-maintained fork under the Linux Foundation that kept the original MPL-2.0 license. Ubuntu 26.04 users now have two solid choices for infrastructure as code, and both install cleanly from APT.

Original content from computingforgeeks.com - post 166055

This guide walks through installing both Terraform and OpenTofu on Ubuntu 26.04 LTS, then covers the practical IaC workflow: creating resources, previewing and applying changes, managing state, the Docker provider, and Terragrunt as a wrapper. If you have already completed the initial server setup, you are ready to go.

Tested July 2026 on Terraform 1.15.8 and OpenTofu 1.12.5 (Ubuntu 26.04 LTS). The APT steps use codename detection, so the same commands work on Ubuntu 24.04.

terraform version, tofu version and terragrunt version output on Ubuntu 26.04 LTS

Prerequisites

Before starting, make sure you have the following in place:

  • Ubuntu 26.04 LTS server or desktop with root or sudo access
  • Tested on: Ubuntu 26.04 LTS (Resolute Raccoon), kernel 7.0.0-15-generic
  • Internet connectivity to reach the HashiCorp and OpenTofu APT repositories
  • Docker installed if you plan to follow the Docker provider section (install Docker on Ubuntu 26.04)

Install Terraform on Ubuntu 26.04

Terraform ships through HashiCorp’s official APT repository. HashiCorp now publishes packages for the 26.04 resolute codename directly, so the earlier workaround of borrowing the 24.04 noble packages is no longer needed.

Install the tools needed to fetch and verify the repository key:

sudo apt-get update
sudo apt-get install -y gnupg curl

Import the HashiCorp signing key, then add the repository using your own codename so the same block works on 24.04:

. /etc/os-release
wget -qO- 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 ${UBUNTU_CODENAME} main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

Refresh the package index and install Terraform:

sudo apt-get update
sudo apt-get install -y terraform

Confirm the binary and version:

terraform version

The current stable release reports:

Terraform v1.15.8
on linux_amd64

Install OpenTofu on Ubuntu 26.04

OpenTofu ships a standalone installer script that handles the GPG keys and repository setup for you. This is the method the OpenTofu project recommends.

Download the installer over a pinned TLS connection, then run it against the deb method:

curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh
chmod +x install-opentofu.sh
sudo ./install-opentofu.sh --install-method deb

The script adds the OpenTofu keys, configures the APT source, and installs the tofu package. Remove the installer once it finishes:

rm -f install-opentofu.sh

Check the installed version:

tofu version

You should see the current OpenTofu release:

OpenTofu v1.12.5
on linux_amd64

If you prefer not to pipe a script into a shell, the OpenTofu deb install docs also document the manual APT repository steps, which suit locked-down CI runners.

Create Your First Terraform Configuration

The fastest way to prove both tools work is a minimal configuration. This example uses the local provider to write a file on disk, so it needs no cloud credentials.

Create a project directory:

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

Open the configuration file. Keep it in your own home directory, so no sudo is needed:

vim main.tf

Add the following HCL:

terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

resource "local_file" "hello" {
  content  = "Hello from Terraform on Ubuntu 26.04!\n"
  filename = "${path.module}/hello.txt"
}

output "file_path" {
  value = local_file.hello.filename
}

The configuration declares a dependency on the local provider, creates a text file, and outputs its path. Save the file and move on to the workflow.

Terraform Workflow: Init, Plan, Apply, Destroy

Every Terraform project follows the same lifecycle: initialize, plan, apply, and optionally destroy. Here is each step against the config above.

Initialize the working directory

terraform init downloads the required providers and sets up the backend:

terraform init

It resolves and installs the local provider, then writes a lock file:

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.5"...
- Installing hashicorp/local v2.9.0...
- Installed hashicorp/local v2.9.0 (signed by HashiCorp)

Terraform has been successfully initialized!

Preview changes with plan

terraform plan shows what will be created, changed, or destroyed without touching anything:

terraform plan

The plan ends with a one-line summary of the intended changes:

  # local_file.hello will be created
  + resource "local_file" "hello" {
      + content              = "Hello from Terraform on Ubuntu 26.04!\n"
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./hello.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Apply the configuration

Run terraform apply to create the resources. The -auto-approve flag skips the confirmation prompt, which suits automation, but review the plan first in production:

terraform apply -auto-approve

Terraform creates the file and prints the output:

local_file.hello: Creating...
local_file.hello: Creation complete after 0s [id=2138eaf75c666a9671d08eaebbf8d80bb2347ad1]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

file_path = "./hello.txt"

Confirm the file landed on disk:

cat hello.txt

This prints:

Hello from Terraform on Ubuntu 26.04!

Tear down resources

terraform destroy removes everything Terraform manages. This is the cleanup step:

terraform destroy -auto-approve

The output confirms the resource is gone:

local_file.hello: Destroying... [id=2138eaf75c666a9671d08eaebbf8d80bb2347ad1]
local_file.hello: Destruction complete after 0s

Destroy complete! Resources: 1 destroyed.

Run the Same Example with OpenTofu

OpenTofu uses identical HCL and the same provider registry. The command is tofu instead of terraform, but the workflow is the same.

Create a separate directory and reuse the same configuration:

mkdir -p ~/tofu-demo && cd ~/tofu-demo
cp ~/terraform-demo/main.tf .

Initialize the directory:

tofu init

OpenTofu pulls the same provider from the registry:

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.5"...
- Installing hashicorp/local v2.9.0...
- Installed hashicorp/local v2.9.0 (signed, key ID 0C0AF313E5FD9F80)

OpenTofu has been successfully initialized!

Apply the configuration:

tofu apply -auto-approve

The result matches Terraform exactly:

local_file.hello: Creating...
local_file.hello: Creation complete after 0s [id=2138eaf75c666a9671d08eaebbf8d80bb2347ad1]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

file_path = "./hello.txt"

Clean up when done:

tofu destroy -auto-approve

The identical resource ID across both tools comes from the deterministic content hash, not a coincidence. The lock file and state file formats are compatible between Terraform and OpenTofu, though mixing both against the same state file is not recommended.

Docker Provider Example

A more practical test manages Docker containers from Terraform. This section needs Docker installed on the host.

Create a new project directory:

mkdir -p ~/docker-demo && cd ~/docker-demo

Open the configuration file:

vim main.tf

Add a configuration that pulls an Nginx image and runs a container:

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx:alpine"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "terraform-nginx"

  ports {
    internal = 80
    external = 8080
  }
}

output "container_id" {
  value = docker_container.nginx.id
}

output "container_name" {
  value = docker_container.nginx.name
}

Initialize and apply:

terraform init
terraform apply -auto-approve

Terraform pulls the image and starts the container:

docker_image.nginx: Creating...
docker_image.nginx: Creation complete after 3s [id=sha256:4a73073bd557...nginx:alpine]
docker_container.nginx: Creating...
docker_container.nginx: Creation complete after 1s [id=5b9dd6a3f84c...]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

container_id = "5b9dd6a3f84c2bf338f8400ca81423e446a324ac6f516afd8c9c2a4cf6325a76"
container_name = "terraform-nginx"

Confirm the container is up with docker ps:

docker ps

The container is listed and serving on port 8080:

CONTAINER ID   IMAGE          STATUS          PORTS                    NAMES
5b9dd6a3f84c   4a73073bd557   Up 17 seconds   0.0.0.0:8080->80/tcp     terraform-nginx

Check that Nginx responds:

curl -s http://localhost:8080 | head -4

You get the Nginx welcome page HTML:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

State Management Commands

Terraform tracks every managed resource in a state file (terraform.tfstate). These commands inspect that state.

List everything Terraform is tracking:

terraform state list

For the Docker example, this returns both resources:

docker_container.nginx
docker_image.nginx

Inspect a single resource in detail:

terraform state show docker_container.nginx

The output opens with the resource header and its attributes:

# docker_container.nginx:
resource "docker_container" "nginx" {
    attach                                      = false
    command                                     = [
        "nginx",
        "-g",
        "daemon off;",
    ]
    container_read_refresh_timeout_milliseconds = 15000
    cpu_shares                                  = 0
    ...
}

The state file is the source of truth for what Terraform manages. Never edit it by hand. When you finish the Docker demo, destroy the resources:

terraform destroy -auto-approve

Formatting and Validation

Both tools ship built-in formatting and validation that catch issues before plan or apply.

Check whether your HCL follows the canonical style:

terraform fmt -check

No output means the files are already formatted. Drop the -check flag to rewrite files in place. Next, validate syntax and internal consistency:

terraform validate

A valid configuration returns:

Success! The configuration is valid.

The same commands work with OpenTofu as tofu fmt and tofu validate. Running fmt in CI catches style drift before code review. If you write custom providers in Go on Ubuntu 26.04, these commands validate the HCL that consumes them too.

Install Terragrunt

Terragrunt is a thin wrapper around Terraform and OpenTofu that reduces duplication across multi-environment deployments. It adds DRY backend configuration, dependency orchestration between modules, and before/after hooks.

Fetch the latest release and install the binary:

TGVER=$(curl -sL https://api.github.com/repos/gruntwork-io/terragrunt/releases/latest | grep tag_name | head -1 | sed 's/.*"v\([^"]*\)".*/\1/')
echo "Installing Terragrunt v${TGVER}"
sudo wget -q "https://github.com/gruntwork-io/terragrunt/releases/download/v${TGVER}/terragrunt_linux_amd64" -O /usr/local/bin/terragrunt
sudo chmod +x /usr/local/bin/terragrunt

Verify the installed version:

terragrunt --version

The output confirms the release:

terragrunt version v1.1.1

Terragrunt works with either engine. The v1 line calls tofu by default, so OpenTofu users need no extra configuration. If you installed only Terraform, point Terragrunt at it with terraform_binary = "terraform" in your terragrunt.hcl, set the environment variable TG_TF_PATH=terraform (the v1.0 CLI redesign renamed the old TERRAGRUNT_TFPATH), or pass --tf-path terraform. Configuration tools like Ansible pair well with Terragrunt for post-provisioning steps.

Terraform vs OpenTofu: Which One to Use

Both tools share the same roots (OpenTofu forked from Terraform 1.5.7) and stay highly compatible. The decision comes down to licensing requirements and organizational preferences.

CriteriaTerraformOpenTofu
LicenseBSL 1.1 (Business Source License)MPL-2.0 (Mozilla Public License)
GovernanceHashiCorp (IBM)Linux Foundation
Provider registryregistry.terraform.ioSame registry, plus the OpenTofu registry
State file formatCompatibleCompatible
HCL syntaxIdenticalIdentical, plus state encryption
CLI commandterraformtofu (drop-in replacement)
Unique featuresHCP Terraform (formerly Terraform Cloud), Sentinel policiesClient-side state encryption, OCI registry for providers and modules
Commercial useRestricted for competing productsNo restrictions
Enterprise supportHashiCorp / IBMMultiple vendors (Spacelift, env0, Scalr)

If your organization builds a product that competes with HashiCorp offerings, the BSL license restricts you from using Terraform. OpenTofu carries no such restriction. For most internal infrastructure teams, both tools work equally well. OpenTofu’s client-side state encryption is a genuine advantage when you store state in S3 or GCS and want encryption at the application layer rather than relying only on the backend’s server-side encryption.

One practical point: if HCP Terraform (formerly Terraform Cloud) or Terraform Enterprise is already in your stack, moving to OpenTofu means giving up those managed services. If you are starting fresh or self-managing state backends, OpenTofu gives you the same capabilities without the license concern.

Keep reading

Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) Ubuntu Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) UFW Firewall Commands with Examples on Ubuntu 24.04 / 22.04 Security UFW Firewall Commands with Examples on Ubuntu 24.04 / 22.04 Backup and Restore Linux Systems with Timeshift Debian Backup and Restore Linux Systems with Timeshift NVIDIA GPU Monitoring with DCGM Exporter, Prometheus, and Grafana DevOps NVIDIA GPU Monitoring with DCGM Exporter, Prometheus, and Grafana Best Platform Engineering Books for 2026 Books Best Platform Engineering Books for 2026 Deploy Etcd Cluster on Ubuntu / Debian / Rocky / Alma Containers Deploy Etcd Cluster on Ubuntu / Debian / Rocky / Alma

Leave a Comment

Press ESC to close