Skip to content

Architecture

OpenTofu is a community-driven fork of Terraform, created after HashiCorp switched the Terraform license to BSL 1.1 in August 2023. It retains the same HCL syntax, provider ecosystem, and core execution model while adding features such as native state encryption and an open registry.


Component Overview

graph TB
    subgraph CLI["CLI Layer"]
        CMD[tofu CLI]
    end

    subgraph CORE["OpenTofu Core"]
        HCL[HCL Parser]
        CFG[Config Loader]
        GRAPH[Graph Builder]
        EVAL[Evaluator]
        PLAN[Planning Engine]
        APPLY[Apply Engine]
    end

    subgraph PLUGINS["Plugin System"]
        PV["Provider Plugins<br/>(gRPC)"]
        PROV["Provisioner Plugins"]
    end

    subgraph STATE["State Layer"]
        SF["State File<br/>(JSON / encrypted)"]
        BE["Backend<br/>(S3, GCS, Azure, local, etc.)"]
        ENC["Encryption Layer<br/>(AES-GCM, KMS)"]
    end

    subgraph REG["Registry"]
        OREG["OpenTofu Registry"]
    end

    CMD --> HCL
    HCL --> CFG
    CFG --> GRAPH
    GRAPH --> EVAL
    EVAL --> PLAN
    PLAN --> APPLY
    APPLY --> PV
    APPLY --> PROV
    PV -->|gRPC| PV
    PLAN --> SF
    APPLY --> SF
    SF --> ENC
    ENC --> BE
    CMD -->|resolve providers| OREG

Core Engine

The core engine is written in Go and follows the same architectural patterns established by Terraform up to version 1.5.x. Key subsystems:

HCL Parser and Config Loader

  • Parses .tf and .tf.json files into an intermediate configuration structure.
  • The config loader resolves module sources (local paths, git repositories, the OpenTofu Registry) and merges them into a single configuration tree.
  • Variable definitions, output values, and provider configuration blocks are all parsed at this stage.

Graph Builder

  • Constructs a directed acyclic graph (DAG) of resource dependencies.
  • Nodes represent resources, data sources, providers, variables, and outputs.
  • Edges represent explicit depends_on declarations plus implicit references detected by analyzing HCL expressions.
  • Separate graph builders exist for plan, apply, and destroy operations:
  • PlanGraphBuilder -- determines what changes are needed.
  • ApplyGraphBuilder -- executes the planned changes.
  • DestroyGraphBuilder -- reverses the order of the apply graph.

Evaluator

  • Walks the graph and evaluates HCL expressions.
  • Handles count, for_each, and conditional resource creation.
  • Resolves variable values, local values, and data source outputs.
  • Produces "desired state" representations for each resource instance.

Planning Engine

The planning engine compares three representations:

  1. Configuration -- what the HCL files declare.
  2. Prior State -- what was recorded after the last successful apply.
  3. Refreshed State -- what the provider reports as the current real-world state.

From these inputs, the engine produces a plan: a list of create, update, and delete actions.

Apply Engine

  • Takes a saved plan and walks the apply graph.
  • For each node, invokes the appropriate provider gRPC call (PlanResourceChange, ApplyResourceChange).
  • Updates the state file incrementally as each resource operation completes.
  • If an error occurs mid-apply, partial state is preserved so the user can re-run without duplication.

Plugin Protocol

OpenTofu communicates with provider and provisioner plugins over gRPC (protocol version 5.x, codenamed tfplugin5).

sequenceDiagram
    participant Core as OpenTofu Core
    participant Plugin as Provider Plugin

    Core->>Plugin: Launch process (stdout handshake)
    Plugin-->>Core: gRPC address + protocol version
    Core->>Plugin: Negotiate protocol version
    Core->>Plugin: GetSchema
    Plugin-->>Core: Resource schemas
    Core->>Plugin: PrepareProviderConfig
    Plugin-->>Core: Validated config
    Core->>Plugin: PlanResourceChange
    Plugin-->>Core: Planned change
    Core->>Plugin: ApplyResourceChange
    Plugin-->>Core: New state
    Core->>Plugin: Graceful shutdown

Protocol compatibility

The protocol version must be negotiated during handshake. OpenTofu supports protocol v5 and v6. Providers compiled for Terraform protocol v5 are generally compatible with OpenTofu without modification.

Key protocol RPCs

RPC Purpose
GetSchema Returns the provider's resource and data source schemas
PrepareProviderConfig Validates and defaults provider configuration
ValidateResourceTypeConfig Validates a resource's configuration
PlanResourceChange Computes the diff between prior and proposed state
ApplyResourceChange Executes the planned change against the real infrastructure
ReadResource Refreshes a single resource's current state
ImportResourceState Imports an existing resource into state

Plan / Apply Lifecycle

sequenceDiagram
    participant User
    participant CLI as tofu CLI
    participant Core as OpenTofu Core
    participant State as State Storage
    participant Prov as Provider Plugins

    User->>CLI: tofu plan
    CLI->>Core: Parse config
    Core->>State: Load prior state
    Core->>Prov: Refresh (ReadResource per instance)
    Prov-->>Core: Current real-world state
    Core->>Core: Compute diff (config vs refreshed state)
    Core->>Core: Build execution graph
    Core->>Prov: PlanResourceChange (per resource)
    Prov-->>Core: Planned changes
    Core-->>CLI: Plan output
    CLI-->>User: Plan summary

    User->>CLI: tofu apply
    CLI->>Core: Load saved plan
    Core->>Core: Walk apply graph
    loop For each resource change
        Core->>Prov: ApplyResourceChange
        Prov-->>Core: New resource state
        Core->>State: Write partial state
    end
    Core->>State: Write final state
    Core-->>CLI: Apply complete
    CLI-->>User: Apply summary

Key insight

The plan phase produces a deterministic, saveable plan file. The apply phase can be run separately (even on a different machine with a remote backend), guaranteeing that exactly the planned changes are executed.


State Management

State File Format

  • JSON-based, containing the serialized state of every managed resource instance.
  • Includes provider configuration hashes, resource dependencies, and output values.
  • Sensitive values are stored in plaintext by default (unlike Pulumi's per-secret encryption), unless the encryption feature is enabled.

Backends

OpenTofu supports multiple backend types for remote state storage:

Backend State Locking Notes
local No Default, stores terraform.tfstate on disk
s3 Yes (DynamoDB or S3 object lock) Most common for AWS deployments
gcs Yes (native) Google Cloud Storage
azurerm Yes (blob lease) Azure Blob Storage
cos Yes Tencent Cloud Object Storage
oss Yes Alibaba Cloud OSS
pg Yes (PostgreSQL advisory lock) PostgreSQL backend
http Varies Generic HTTP backend

State Encryption

A feature unique to OpenTofu (not available in upstream Terraform). Encryption is configured in the terraform block:

terraform {
  encryption {
    key_provider "aws_kms" "my_key" {
      kms_key_id = "alias/my-key"
      key_spec   = "AES_256"
      region     = "us-east-1"
    }
    method "aes_gcm" "default" {
      keys = key_provider.aws_kms.my_key
    }
    state { method = method.aes_gcm.default }
    plan  { method = method.aes_gcm.default }
  }
}

Supported key providers: PBKDF2 (passphrase), AWS KMS, GCP KMS, Azure Key Vault, OpenBao.


OpenTofu Registry

  • The OpenTofu Registry (registry.opentofu.org) is a community-operated index of providers and modules.
  • Mirrors the Terraform Registry API structure, so existing Terraform provider lookups often work unchanged.
  • Providers are addressed as registry.opentofu.org/<namespace>/<name>.
  • Supports GPG signature verification for module integrity.

Comparison with Terraform

Aspect OpenTofu Terraform
License MPL 2.0 BSL 1.1 (transitioning)
State encryption Native (built-in) Not available
Provider protocol gRPC v5/v6 (compatible) gRPC v5/v6
Registry registry.opentofu.org registry.terraform.io
Language server OpenTofu LS Terraform LS
Key differentiator Community-governed, encryption-first HashiCorp ecosystem, HCP Terraform

Migration path

Migrating from Terraform to OpenTofu typically requires only renaming terraform blocks and installing the tofu binary. State files, provider plugins, and HCL configurations are fully compatible.


References


How It Works

HCL parsing, plan/apply execution model, provider gRPC protocol, DAG-based dependency resolution, and client-side state encryption.

Core Execution Flow

sequenceDiagram
    participant User as User (CLI)
    participant Parser as HCL Parser
    participant Graph as DAG Builder
    participant Plan as Plan Engine
    participant Provider as Provider Plugin (gRPC)
    participant State as State Backend

    User->>Parser: tofu plan / tofu apply
    Parser->>Parser: Parse .tf files → AST
    Parser->>Graph: Extract resource references
    Graph->>Graph: Build DAG (topological sort)
    Graph->>Plan: Walk graph, compare desired vs actual state
    Plan->>Provider: ReadResource (refresh current state)
    Provider-->>Plan: Actual resource state
    Plan->>Plan: Compute diff (create / update / delete / no-op)
    Plan-->>User: Plan output (changes to apply)
    User->>Plan: Approve apply
    Plan->>Provider: ApplyResourceChange (per graph node)
    Provider-->>Plan: New resource state
    Plan->>State: Write updated state

Plan/Apply Execution Model

OpenTofu separates operations into two phases:

Plan Phase

  1. Refresh -- For each resource in state, call ReadResource on the provider to get the current actual state
  2. Diff -- Compare the refreshed state against the desired configuration in .tf files
  3. Graph walk -- Traverse the DAG in dependency order, computing changes for each resource
  4. Output -- Present the plan: which resources will be created, updated (with attribute diffs), or destroyed

Apply Phase

  1. Graph reconstruction -- Rebuild the apply graph (may differ from plan graph due to destroy dependencies)
  2. Parallel execution -- Walk the graph, executing independent nodes concurrently (controlled by -parallelism, default 10)
  3. Provider calls -- For each node, call ApplyResourceChange on the provider via gRPC
  4. State write -- After each successful resource operation, write the new state to the backend
  5. Output -- Report results for each resource

Provider gRPC Protocol

Providers communicate with the OpenTofu engine via a gRPC protocol defined in the terraform-plugin-go specification:

RPC Purpose
GetProviderSchema Returns resource and data source schemas
ValidateResourceConfig Validates resource configuration before plan
UpgradeResourceState Migrates state from older schema versions
ReadResource Fetches current state of an existing resource
PlanResourceChange Computes the proposed new state for an update
ApplyResourceChange Creates, updates, or deletes a resource
ConfigureProvider Passes provider configuration (credentials, region, etc.)

Each provider runs as a separate process, launched by the engine via exec.Command. Communication happens over a gRPC connection on a local unix socket or shared pipe.

State Encryption

OpenTofu adds a client-side encryption layer not present in Terraform. This layer encrypts the state blob before it is written to the backend.

sequenceDiagram
    participant CLI as OpenTofu CLI
    participant Enc as Encryption Layer
    participant KMS as Key Provider (AWS/GCP KMS, age, PBKDF2)
    participant Backend as Remote Backend (S3, GCS)

    CLI->>CLI: Compute state changes
    CLI->>Enc: Serialize state JSON
    Enc->>KMS: Request encryption key
    KMS-->>Enc: DEK (data encryption key)
    Enc->>Enc: AES-GCM encrypt state
    Enc->>Backend: Write encrypted state blob

    Note over Enc,Backend: Attacker with backend access<br/>sees only ciphertext

Encryption Configuration

terraform {
  encryption {
    key_provider "aes_gcm" "mykey" {
      keys = [
        {
          key = base64decode(var.encryption_key)
        }
      ]
    }
    method "aes_gcm" "mymethod" {
      keys = key_provider.aes_gcm.mykey
    }
    state {
      method = method.aes_gcm.mymethod
      fallback {
        method = method.aes_gcm.oldmethod  # For key rotation
      }
    }
  }
}

Key Rotation

flowchart LR
    Old["Old Key\n(fallback)"] --> Read["Decrypt with\nold key"]
    Read --> Reencrypt["Re-encrypt with\nnew key"]
    Reencrypt --> New["New Key\n(primary)"]

    style Old fill:#c62828,color:#fff
    style New fill:#2e7d32,color:#fff

OpenTofu supports a fallback key configuration to enable seamless key rotation without downtime. On the next state write, the state is re-encrypted with the new primary key.

Sources


Benchmarks

Scope

Performance characteristics, scaling limits, and resource consumption for OpenTofu.

Plan/Apply Performance

State Size (resources) Plan Time Apply Time (parallel=10)
50 < 5s 30s-2m
200 10-30s 2-5m
1,000 1-3m 10-30m
5,000 5-15m 30m-2h

Provider Performance

Provider Init Time Resource Create Notes
AWS 2-5s 5-30s per resource API rate limits apply
Azure 3-8s 10-60s per resource Slower API responses
GCP 2-5s 5-30s per resource Similar to AWS
Kubernetes 1-3s 1-5s per resource Fast for small objects

State File Scaling

Resources State File Size Refresh Time
100 100KB-1MB 10-30s
1,000 1-10MB 1-5m
10,000 10-100MB 10-30m

Warning

State files beyond 50MB significantly degrade plan performance. Split into multiple state files.

Sourcing Status

Unsourced Performance Data

The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.

Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.

Sources