OpenTofu Fundamentals
Understand core OpenTofu concepts, the declarative IaC workflow (init, plan, apply, destroy), and run your first Docker container locally.
Table of Contents
OpenTofu is an open-source, community-driven Infrastructure as Code (IaC) tool forked from Terraform (under the MPL 2.0 license) and governed by the Linux Foundation. It uses a declarative approach powered by HCL (HashiCorp Configuration Language).
With declarative IaC, you specify the desired end state of your infrastructure rather than writing imperative, step-by-step procedures. OpenTofu calculates the delta between real-world infrastructure and your configuration, and reconciles the differences automatically.
Prerequisites
Before starting, make sure you have the following installed and running:
- OpenTofu CLI: Verify your installation from the terminal:
(On macOS or Linux, you can install it via Homebrew:tofu -versionbrew install opentofu). - Docker: Ensure Docker Engine or Docker Desktop is active:
docker info
Core Concepts
Every OpenTofu project is built around five fundamental concepts:
- Provider: A plugin that translates OpenTofu operations into target API calls (such as Docker, AWS, GCP, Cloudflare, Kubernetes, or Hetzner).
- Resource: A concrete infrastructure component to create and manage (e.g., a Docker container, virtual machine, subnet, or DNS record).
- Data Source: A component used to query and read data from existing infrastructure without managing or recreating it.
- State (
tofu.tfstate): A JSON-formatted ledger mapping your declarative configuration to real-world resources. This file acts as the single source of truth that OpenTofu consults to detect configuration drift. - Variables & Outputs: Input variables parameterize configurations to keep them reusable across environments, while outputs expose important values (like IP addresses, URLs, or generated resource IDs) after provisioning.
The Core Workflow
Everyday OpenTofu usage revolves around four sequential commands:
tofu init— Initializes the working directory, downloads required provider plugins into the.terraform/directory, and locks their versions in.terraform.lock.hcl.tofu plan— Evaluates your configuration against the current state to produce an execution plan. It previews which resources will be created (+), updated (~), or destroyed (-) without modifying any real infrastructure.tofu apply— Executes the planned changes against the target infrastructure after asking for your confirmation (yes), then updates the local state file.tofu destroy— Safely deletes all resources managed by the configuration.
Hands-on: Your First Infrastructure with Docker
The easiest, zero-cost way to practice OpenTofu without a cloud account or credit card is to use the Docker provider locally.
Create a fresh directory for the exercise, then create a main.tf file:
mkdir learn-opentofu && cd learn-opentofu
touch main.tf
Paste the following configuration into main.tf:
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 4.6.0"
}
}
}
provider "docker" {
# Default Unix socket for macOS and Linux
host = "unix:///var/run/docker.sock"
}
# Pull the Nginx Docker image
resource "docker_image" "nginx" {
name = "nginx:alpine"
keep_locally = false
}
# Run the Nginx container
resource "docker_container" "web" {
image = docker_image.nginx.image_id
name = "learn-opentofu-nginx"
ports {
internal = 80
external = 8080
}
}
# Expose the web access URL after apply
output "container_url" {
description = "URL to access the Nginx container"
value = "http://localhost:${docker_container.web.ports[0].external}"
}
Compatibility Note: The configuration block begins with
terraform { ... }because OpenTofu maintains 100% backward compatibility with the existing Terraform HCL ecosystem.
Step-by-Step Execution
1. Initialize the Project (tofu init)
Initialize the directory to download the Docker provider:
tofu init
OpenTofu resolves the kreuzwerker/docker provider, downloads the plugin binary to the .terraform/ directory, and writes version checksums to .terraform.lock.hcl.
2. Preview the Plan (tofu plan)
Generate and review the execution plan:
tofu plan
The terminal output will display green + indicators next to docker_image.nginx and docker_container.web, confirming that two new resources are scheduled for creation.
3. Apply the Configuration (tofu apply)
Apply the changes to start provisioning:
tofu apply
When prompted for approval, type yes and press Enter.
Once provisioning finishes, OpenTofu prints the output summary:
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
container_url = "http://localhost:8080"
Open your browser and navigate to http://localhost:8080 (or run curl http://localhost:8080). You should see the standard “Welcome to nginx!” page.
Dissecting the Configuration
1. The Provider Block and the host Argument
The host argument instructs OpenTofu where to send Docker API commands:
- On Linux and macOS, the default Unix socket is
unix:///var/run/docker.sock. - On Windows or when using Docker Desktop with standard environment variables, you can leave the block empty:
provider "docker" {}. The provider will automatically read standard variables likeDOCKER_HOST.
2. Resource Syntax and the Two Labels
Every resource block in HCL requires exactly two string labels:
resource "<RESOURCE_TYPE>" "<LOCAL_NAME>" {
# ... attribute configuration ...
}
Example:
resource "docker_container" "web" { ... }
▲ ▲
│ └─ 2. Local Name (Unique reference alias)
└────────────────── 1. Resource Type (Provided by the provider)
- Resource Type (
docker_container): The resource type defined by the provider plugin. - Local Name (
web): An arbitrary identifier you choose to reference this resource elsewhere in your OpenTofu code.
3. Implicit Dependency
Look closely at this attribute inside the docker_container resource:
image = docker_image.nginx.image_id
Instead of hardcoding an image ID, we reference the exported image_id attribute from docker_image.nginx.
Through this reference, OpenTofu constructs a Dependency Graph and automatically deduces that the web container depends on the nginx image. OpenTofu guarantees the image is downloaded before the container attempts to boot—no manual ordering required.
Inspecting and Managing State
Once tofu apply succeeds, OpenTofu generates a terraform.tfstate file in your project directory. You can inspect tracked resources:
- List all managed resources:
tofu state list - View all attributes and live configuration details (IP address, container IDs, ports):
tofu show
Security Warning: Never modify
*.tfstatefiles manually with a text editor. Always add*.tfstateand*.tfstate.backupto your.gitignore, as state files can store sensitive attributes and credentials in plaintext.
Cleaning Up Resources (tofu destroy)
When you finish practicing and want to remove the running container and downloaded image:
tofu destroy
Confirm with yes when prompted. OpenTofu stops and removes the Nginx container, freeing up your local resources cleanly.
Summary & Next Steps
In this guide, you learned:
- The declarative philosophy behind OpenTofu and Infrastructure as Code.
- The four stages of the core workflow:
init,plan,apply, anddestroy. - How to configure the Docker provider, pull an image, run a container, and define outputs.
- How OpenTofu tracks infrastructure using state and manages implicit dependencies.
In Part 2, we will explore Variables and Parameterization in OpenTofu to make configurations dynamic, reusable, and ready for multi-environment deployments (development, staging, and production).