Open-Source Wikis

/

Terraform

/

Systems

/

Backends

hashicorp/terraform

Backends

Active contributors: terraform-core team, terraform-aws (S3), terraform-azure, tf-eco-hybrid-cloud (GCS, Kubernetes), tf-core-cloud.

Purpose

internal/backend/ is the pluggable state-storage layer. A backend tells Terraform where to keep the state file (and, for a few backends, where to run operations). The CLI selects a backend at terraform init time based on the terraform { backend "..." {} } or cloud {} block in the configuration; subsequent commands use that backend's state manager and (sometimes) operation runner.

Directory layout

internal/backend/
├── backend.go               # Backend interface, basic helpers
├── testing.go               # shared test scaffolding
├── backendbase/             # base type providing common behaviors
├── backendrun/              # interfaces for backends that execute operations
├── init/                    # registry of backend constructors (init.go)
├── local/                   # the default local backend (also wraps state-only backends)
├── remote/                  # the legacy remote backend (HCP Terraform pre-cloud-block)
├── remote-state/            # state-only backends:
│   ├── azure/, gcs/, http/, s3/, kubernetes/, oss/, cos/, consul/,
│   │   inmem/, oci/, pg/
└── pluggable/               # provider-implemented state storage (newer)

Key types

Type File Description
backend.Backend backend.go Bare-bones interface: ConfigSchema(), PrepareConfig(...), Configure(...), StateMgr(workspace string), Workspaces(), DeleteWorkspace(name string, force bool).
backendrun.OperationsBackend backendrun/operations_backend.go Optional interface for backends that run operations: Operation(ctx, op) returns a RunningOperation. Implemented by local, remote, and cloud.
backendrun.Operation backendrun/operation.go The operation request: type (plan/apply/refresh/…), workspace, config, prior plan, hooks, view.
backendrun.RunningOperation backendrun/operation.go The handle returned: cancellation, completion channel, accumulated diagnostics.
backend.Enhanced (see init.go) A backend with both state storage and operation execution.
init.Backend(name string) backend.Backend init/init.go The registry: maps "s3" to s3.New, "local" to local.New, etc.

How it works

graph TD
    Cfg[terraform { backend "..." {} } / cloud {}] --> Meta[command.Meta.Backend]
    Meta --> Init[internal/backend/init.Backend]
    Init --> Ctor[constructor for selected name]
    Ctor --> Cfg2[Configure with merged settings]
    Cfg2 --> SM[StateMgr for workspace]
    Meta --> Wrap[wrap in local.Local if not OperationsBackend]
    Meta --> Op[backendrun.Operation]
    Op --> Local[local.Local.Operation]
    Local --> Ctx[terraform.Context.Plan / Apply]
    Local --> SM
    SM --> Persist[durable storage]

init/ — the registry

internal/backend/init/init.go is a single file mapping backend names to their constructors. Adding a backend means adding an entry here and writing the implementation under a sibling directory.

The cloud and remote entries are special: they're labeled "enhanced" and wired to also implement OperationsBackend.

State-only vs operations backends

Most backends (s3, gcs, azure, http, kubernetes, pg, oss, consul, cos, oci, inmem) implement only Backend — they store state. When the CLI is asked to run an operation against one of these, it wraps the backend in local.Local and runs the operation locally. The wrapping logic is in internal/command/meta_backend.go.

The local backend itself (internal/backend/local/) implements both Backend and OperationsBackend. As an operations backend it:

  1. Reads the prior state from its own state manager (which delegates to whatever inner state-only backend wraps it).
  2. Loads the configuration via internal/command/meta_config.go.
  3. Builds a terraform.Context.
  4. Calls the appropriate Plan / Apply / Refresh / Validate / Eval method.
  5. Renders results through the operation's view.

The remote backend (internal/backend/remote/) and the cloud integration (internal/cloud/) implement both interfaces by sending the operation to HCP Terraform for execution. See cloud.

Workspace semantics

Backend.Workspaces() ([]string, error) returns the list of workspaces this backend exposes. Backends that don't natively support multiple workspaces typically simulate them via key-prefixing or a marker file:

  • s3: workspace default lives at <key>; other workspaces live at <workspace_key_prefix>/<name>/<key>.
  • gcs: similar — a configurable prefix.
  • kubernetes: each workspace is a Kubernetes Secret.
  • local: each workspace is a separate terraform.tfstate.d/<name>/terraform.tfstate file in the working directory.

StateMgr(name) returns a statemgr.Full for the named workspace.

Backend configuration

Backend config arrives from three sources, merged in the order:

  1. The terraform { backend "..." {} } or cloud {} block in the configuration.
  2. -backend-config=KEY=VALUE flags or -backend-config=path/to/file.tfvars pointing at an extra HCL fragment.
  3. Previously-stored values in .terraform/terraform.tfstate (the local marker file used to remember what init configured).

The merge logic is in internal/command/meta_backend.go. Sensitive values (credentials) are only kept in memory; the on-disk marker stores their hashes.

Migration

When the user changes their backend block (or switches between cloud {} and backend ...), meta_backend_migrate.go prompts them through migrating state from the old backend to the new one. The flow accommodates several scenarios:

  • Local→remote: copy the local state to the new backend, then delete the local file.
  • Remote→local: copy the remote state to a local file.
  • Remote→remote: workspace-by-workspace migration with confirmation.
  • Local-with-workspaces→cloud: workspace mapping rules.

Pluggable state storage

internal/backend/pluggable/ is the newer extension point that lets providers implement state storage. Configurations declare state_store {} blocks pointing at a provider plus a "store type" — the engine talks to the provider over gRPC to read and write state instead of using a built-in backend. The schemas are exchanged through the regular plugin protocol (internal/configs/state_store.go + the provider's GetProviderSchema response). This keeps the project's surface stable (no new state-only backends merged into the CLI) while still letting third parties ship their own.

Maintenance status of individual backends

Per CODEOWNERS:

Backend Maintainer Status
local Core team Maintained
remote Core team + tf-core-cloud Maintained
s3 Core team + terraform-aws Maintained, accepts contributions
azure (azurerm) Core team + terraform-azure Maintained
gcs Core team + tf-eco-hybrid-cloud Maintained
kubernetes Core team + tf-eco-hybrid-cloud Maintained
http Core team Maintained
consul None Unmaintained
cos, oss, pg, oci Third parties Reviewed only when their maintainers are active

The team explicitly does not accept new state-storage backends; the plug-gable state storage subsystem is the recommended path forward.

Integration points

  • CLI: command.Meta.Backend selects, configures, and migrates backends. The local-wrap logic is in meta_backend.go.
  • State: every backend exposes statemgr.Full instances; see state.
  • Engine: local.Local.Operation is the bridge between operation backends and the engine. See terraform-core.
  • HCP Terraform: the remote and cloud backends call out to HCP Terraform; see cloud.

Entry points for modification

  • Adding a feature to an existing backend: edit the corresponding directory under internal/backend/remote-state/<name>/. Tests in the same directory typically gate on TF_ACC=1 to avoid hitting the real cloud.
  • Changing backend selection or migration: internal/command/meta_backend.go and meta_backend_migrate.go.
  • Adding a new operation kind: extend backendrun.OperationType and update both local and remote to handle it.
  • Adding state-store-style support to a provider: the host-side glue is in internal/backend/pluggable/; provider authors implement the matching gRPC methods.

For provider-side state storage and the cloud {} block, see HCP Terraform. For everything inside local.Local, see terraform-core.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Backends – Terraform wiki | Factory