OpenMCF logoOpenMCF

Loading...

State Management

Every IaC deployment tracks state -- the mapping between what your manifest declares and what actually exists in the cloud. State enables OpenMCF to know what has changed between deployments, what needs to be created, updated, or destroyed, and what the current outputs of a deployment are.

How state is stored depends on which IaC engine you use. Pulumi and OpenTofu/Terraform have different backend systems, and OpenMCF configures them through manifest labels.

Pulumi State Backends

Pulumi organizes state by stack -- a named instance of a deployment identified by a fully qualified domain name (FQDN) in the format {organization}/{project}/{stack-name}.

Configuring Pulumi State

Pulumi state configuration is provided through manifest labels:

metadata:
  labels:
    openmcf.org/provisioner: pulumi
    pulumi.openmcf.org/organization: acme
    pulumi.openmcf.org/project: platform
    pulumi.openmcf.org/stack.name: production.KubernetesPostgres.session-store

Or as a single FQDN using the --stack flag:

openmcf pulumi up -f postgres.yaml --stack acme/platform/production

Supported Pulumi Backends

BackendDescription
Pulumi CloudManaged state storage with history, audit trail, and team collaboration. Default backend.
S3Self-managed state in an AWS S3 bucket.
GCSSelf-managed state in a Google Cloud Storage bucket.
Azure BlobSelf-managed state in Azure Blob Storage.
LocalState stored on the local filesystem. Suitable for development only.

The Pulumi backend is configured through Pulumi's standard mechanisms (environment variables, pulumi login, or configuration files). OpenMCF's role is to set the stack FQDN from the manifest labels, not to configure the backend connection itself.

OpenTofu/Terraform State Backends

OpenTofu and Terraform use a backend block in the Terraform configuration to determine where state is stored. OpenMCF generates this configuration from manifest labels.

Configuring Tofu/Terraform State

State backend configuration is provided through manifest labels with the tofu.openmcf.org/backend.* prefix:

metadata:
  labels:
    openmcf.org/provisioner: tofu
    tofu.openmcf.org/backend.type: s3
    tofu.openmcf.org/backend.bucket: my-tfstate-bucket
    tofu.openmcf.org/backend.key: prod/postgres/terraform.tfstate
    tofu.openmcf.org/backend.region: us-east-1

The CLI reads these labels, writes a backend.tf file in the workspace, and passes the configuration to tofu init.

Legacy terraform.openmcf.org/backend.* labels are also supported for backward compatibility.

Supported Backends and Required Fields

S3

LabelRequiredDescriptionExample
backend.typeYesMust be s3s3
backend.bucketYesS3 bucket namemy-terraform-state-bucket
backend.keyYesState file path within bucketenv/prod/terraform.tfstate
backend.regionYesAWS region (or auto for S3-compatible)us-west-2
backend.endpointOnly if region=autoCustom S3-compatible endpointhttps://acct.r2.cloudflarestorage.com

GCS

LabelRequiredDescriptionExample
backend.typeYesMust be gcsgcs
backend.bucketYesGCS bucket namemy-terraform-state
backend.keyYesPrefix path within bucketterraform/state

Azure Storage (azurerm)

LabelRequiredDescriptionExample
backend.typeYesMust be azurermazurerm
backend.bucketYesAzure Storage container nametfstate
backend.keyYesState file blob nameprod.terraform.tfstate

Local

LabelRequiredDescription
backend.typeYesMust be local

No other fields are required for local backends. State is stored in the workspace directory.

S3-Compatible Backends (Cloudflare R2, MinIO)

OpenMCF supports S3-compatible backends for teams using Cloudflare R2, MinIO, or other S3-compatible object stores. To use an S3-compatible backend, set the region to auto and provide the custom endpoint:

metadata:
  labels:
    tofu.openmcf.org/backend.type: s3
    tofu.openmcf.org/backend.bucket: my-r2-state
    tofu.openmcf.org/backend.key: prod/terraform.tfstate
    tofu.openmcf.org/backend.region: auto
    tofu.openmcf.org/backend.endpoint: https://your-account-id.r2.cloudflarestorage.com

When region=auto is detected, the CLI automatically configures the S3 backend with the compatibility flags that S3-compatible backends require.

State Lifecycle

Both engines follow the same conceptual lifecycle:

PhasePulumi CommandTofu/Terraform CommandWhat Happens
Initializeopenmcf pulumi initopenmcf tofu initSet up backend, download providers
Previewopenmcf pulumi previewopenmcf tofu planCompare manifest against state, show planned changes
Applyopenmcf pulumi upopenmcf tofu applyExecute changes, update state
Refreshopenmcf pulumi refreshopenmcf tofu refreshSync state with actual cloud resources
Destroyopenmcf pulumi destroyopenmcf tofu destroyDelete all resources, clean up state

The preview/plan step is where state management provides its key value: by comparing your manifest against the stored state, the engine can tell you exactly what will change before you commit to the deployment.

Multi-Environment State Isolation

Each environment should have its own isolated state. In Pulumi, this is achieved through different stack names:

# Production
pulumi.openmcf.org/stack.name: production.KubernetesPostgres.session-store

# Staging
pulumi.openmcf.org/stack.name: staging.KubernetesPostgres.session-store

In OpenTofu/Terraform, this is achieved through different state file keys:

# Production
tofu.openmcf.org/backend.key: production/postgres/terraform.tfstate

# Staging
tofu.openmcf.org/backend.key: staging/postgres/terraform.tfstate

Both approaches ensure that a deployment to staging never reads or modifies the production state, and vice versa.

What's Next

  • Dual IaC Engines -- How the Pulumi and OpenTofu/Terraform engines work
  • Manifests -- How manifest labels configure state backends
  • Module System -- How IaC modules are resolved before state operations run

Next article

CLI

CLI The openmcf CLI is a single binary that handles the full deployment lifecycle: manifest loading, validation, module resolution, provisioner execution, and state management across Pulumi, OpenTofu, and Terraform. Installation For other platforms, download the binary from GitHub Releases. You also need at least one IaC engine installed: How the CLI Works Every deployment follows the same sequence regardless of which engine you choose: Load a manifest from a file, clipboard, kustomize build,...
Read next article