Architecture¶
See also
index | architecture | operations | security | index#Questions
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
.tfand.tf.jsonfiles 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_ondeclarations 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:
- Configuration -- what the HCL files declare.
- Prior State -- what was recorded after the last successful apply.
- 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¶
- OpenTofu GitHub repository
- Plugin protocol specification
- State encryption documentation
- Plan/apply architecture RFC
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¶
- Refresh -- For each resource in state, call
ReadResourceon the provider to get the current actual state - Diff -- Compare the refreshed state against the desired configuration in
.tffiles - Graph walk -- Traverse the DAG in dependency order, computing changes for each resource
- Output -- Present the plan: which resources will be created, updated (with attribute diffs), or destroyed
Apply Phase¶
- Graph reconstruction -- Rebuild the apply graph (may differ from plan graph due to destroy dependencies)
- Parallel execution -- Walk the graph, executing independent nodes concurrently (controlled by
-parallelism, default 10) - Provider calls -- For each node, call
ApplyResourceChangeon the provider via gRPC - State write -- After each successful resource operation, write the new state to the backend
- 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.