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 (can 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, and others)

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 for 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

Split state files at 50 MB. Larger files degrade plan performance.

Sourcing Status

Unsourced Performance Data

Do not plan capacity from these numbers. We estimated them 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


Security

This note covers the security model of OpenTofu, including state encryption, provider credential handling, module verification, and backend security.


Threat Model Overview

Threat Surface Mitigation
State file exposure (at rest) Built-in encryption (AES-GCM with KMS/PBKDF2)
State file tampering Lineage + serial integrity checks
Provider credential leakage Environment variables, vault integration, IAM roles
Supply chain (malicious modules/providers) GPG verification, lock file checksums
Network interception TLS for all remote backend and registry connections
Concurrent state corruption State locking on all supported backends

State Encryption

State encryption is OpenTofu's flagship security feature and the primary differentiator from Terraform OSS. It encrypts state and plan files at rest using a configurable key provider and encryption method.

Configuration

terraform {
  encryption {
    # Key provider: where the encryption key comes from
    key_provider "aws_kms" "prod_key" {
      kms_key_id = "alias/tofu-state-key"
      key_spec   = "AES_256"
      region     = "us-east-1"
    }

    # Encryption method: how data is encrypted
    method "aes_gcm" "default" {
      keys = key_provider.aws_kms.prod_key
    }

    # Apply encryption to state and plan files
    state { method = method.aes_gcm.default }
    plan  { method = method.aes_gcm.default }
  }
}

Supported Key Providers

Key Provider Use Case Key Generation
pbkdf2 Local/dev environments Passphrase-derived (configurable iterations, SHA-256/512)
aws_kms AWS deployments AWS KMS generates data keys
gcp_kms GCP deployments Google Cloud KMS generates data keys
azure_keyvault Azure deployments Azure Key Vault key wrapping
openbao Self-hosted vault OpenBao (Vault fork) transit secrets engine

Encryption Method: AES-GCM

  • Algorithm: AES-256-GCM (Galois/Counter Mode).
  • Provides both confidentiality and authenticity (AEAD).
  • Each encryption operation uses a unique nonce.
  • Encrypted data is stored as a base64-encoded envelope containing the ciphertext, nonce, and key provider metadata.

Migration from Unencrypted State

OpenTofu supports a fallback mechanism for migrating existing unencrypted states:

terraform {
  encryption {
    key_provider "aws_kms" "my_key" {
      kms_key_id = "alias/my-key"
      key_spec   = "AES_256"
    }
    method "aes_gcm" "new_method" {
      keys = key_provider.aws_kms.my_key
    }
    method "unencrypted" "legacy" {}

    state {
      method   = method.aes_gcm.new_method
      fallback = method.unencrypted.legacy
    }
  }
}

Key rotation

To rotate encryption keys, add a new key provider and use the fallback field to reference the old key during the transition period. OpenTofu will decrypt with the fallback key and re-encrypt with the new key on the next write.


Provider Credential Management

Providers (AWS, GCP, Azure, and others) require credentials to interact with cloud APIs. OpenTofu does not store provider credentials in state or configuration files.

Credential Sources (AWS Example)

Priority Source Recommended For
1 Static credentials in provider block Not recommended (do not use in production)
2 Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) CI/CD pipelines
3 Shared credentials file (~/.aws/credentials) Local development
4 IAM role for EC2/ECS Compute-hosted OpenTofu
5 Assume role (STS) Cross-account access

Never hardcode credentials

Do not embed credentials in .tf files. Use environment variables, IAM roles, or a secrets manager. The sensitive attribute can mark variables as sensitive to prevent CLI output from displaying values.

Vault Integration

OpenTofu can retrieve dynamic credentials from HashiCorp Vault (or OpenBao) using the Vault provider:

provider "vault" {
  address = "https://vault.YOUR_DOMAIN:8200"
}

data "vault_generic_secret" "aws_creds" {
  path = "aws/creds/my-role"
}

provider "aws" {
  access_key = data.vault_generic_secret.aws_creds.data["access_key"]
  secret_key = data.vault_generic_secret.aws_creds.data["secret_key"]
}

Module Verification

Lock File Integrity

The .terraform.lock.hcl file pins provider versions and records SHA256 hashes of provider packages:

provider "registry.opentofu.org/hashicorp/aws" {
  version     = "5.45.0"
  constraints = "~> 5.0"
  hashes = [
    "h1:abc123...",
    "zh:def456...",
  ]
}
  • h1: hashes are computed from the provider zip content.
  • zh: hashes are computed from the provider zip content using a platform-specific scheme.
  • Running tofu providers lock generates this file. Commit the file to version control.

GPG Verification

The OpenTofu Registry supports GPG signature verification for modules. When enabled, OpenTofu verifies that module packages are signed by the expected author before downloading and extracting them.


Backend Security

Remote State Backends

Backend Encryption at Rest Encryption in Transit State Locking
S3 SSE-S3, SSE-KMS, SSE-C TLS (HTTPS) DynamoDB or S3 object lock
GCS Google-managed or CMEK TLS Native object versioning + generation
Azure Blob SSE (Microsoft-managed or CMK) TLS Blob lease
PostgreSQL Disk encryption (pg configuration) TLS (required) Advisory locks
Consul Optional (base64) TLS (recommended) Session-based locks

State Locking

State locking prevents concurrent writes that can corrupt state:

  • All remote backends except local and http support state locking.
  • Locks are acquired before any write operation (plan, apply, refresh).
  • If a lock cannot be acquired, the operation fails with a clear error message.
  • tofu force-unlock is available as a last resort when locks are stuck (use with caution).

Backend Authentication

Each backend type has its own authentication mechanism:

# S3 backend with IAM role assumption
terraform {
  backend "s3" {
    bucket         = "my-tofu-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "tofu-locks"
  }
}

The encrypt = true option enables S3 server-side encryption (SSE-S3 by default, SSE-KMS if kms_key_id is specified). This is separate from OpenTofu's built-in state encryption and provides a defense-in-depth layer.


Sensitive Data in State

Even with encryption, certain best practices apply:

  • Use sensitive = true on variables and outputs to prevent values from appearing in CLI output.
  • Review state files for inadvertently stored sensitive values (some providers can store passwords in plaintext attributes).
  • Restrict file system permissions on local state files (chmod 600 terraform.tfstate).
  • Use remote backends with encryption for all production workloads.
  • Consider using data sources to fetch secrets at plan/apply time rather than storing them in configuration.

Security Checklist

  • Enable state encryption with a KMS-backed key provider
  • Use remote backend with state locking for all team environments
  • Enable backend encryption at rest (S3 SSE-KMS, GCS CMEK, and others)
  • Never hardcode provider credentials in .tf files
  • Use IAM roles or Vault for dynamic credential generation
  • Commit .terraform.lock.hcl to version control
  • Mark all sensitive variables and outputs with sensitive = true
  • Restrict local state file permissions to owner-only
  • Use GPG verification for modules from external sources
  • Rotate encryption keys periodically using the fallback mechanism

References