State Backends
State Backends
Every infrastructure deployment managed by Planton uses an Infrastructure as Code (IaC) engine β either Pulumi or Terraform β under the hood. These engines track the state of your infrastructure in state files: a record of what resources exist, their current configuration, and the relationships between them. Without state, the IaC engine cannot determine what to create, update, or delete on the next deployment.
State backend connections tell Planton where to store these state files. Every new organization starts with platform-managed backends for both Pulumi and Terraform, so you can deploy infrastructure immediately. If you need state to live in your own cloud account, you configure a self-managed backend and Planton handles reading, writing, and locking state during deployments.
Why State Backend Choice Matters
State files contain sensitive information β resource IDs, connection strings, and sometimes credentials. Where you store them is a security and operational decision:
- Pulumi Cloud or Terraform Cloud β Managed services that handle storage, encryption, locking, and access control. Lowest setup overhead, but state leaves your infrastructure.
- S3, GCS, or Azure Blob β You control the storage location, encryption keys, and access policies. State stays in your cloud account. Requires more setup but gives you full control.
- S3-compatible storage (R2, MinIO) β Self-hosted state that avoids vendor lock-in to either cloud provider or IaC vendor. Planton itself uses Cloudflare R2 as its default state backend.
There is no universally right answer. The choice depends on your security requirements, compliance constraints, and operational preferences.
Platform-Managed Defaults
Every new organization comes with two pre-configured state backends: one for Pulumi and one for Terraform. Planton provisions and manages the underlying storage, so you can start deploying infrastructure without any state backend configuration.
Platform-managed backends are the default for new organizations. When a stack job runs, it uses the organization's default backend automatically. You do not need to select a backend, provide credentials, or manage storage buckets.
Limitations:
- Platform-managed backends are only available when using Planton-hosted runners. If you deploy a runner in your own infrastructure, you need a self-managed backend (S3, GCS, Azure Blob, or Pulumi Cloud) because customer-deployed runners cannot access platform-managed storage.
- You do not control the storage location or encryption keys. If compliance requirements mandate that state files remain in your cloud account, configure a self-managed backend instead.
To switch away from platform-managed defaults, create a new state backend connection, configure it with your preferred storage, and mark it as the default for your organization. See Switching from Platform-Managed to Self-Managed for details.
Authentication Mode
When you configure a self-managed state backend (S3, GCS, Azure Blob, or Pulumi Cloud), you choose how the runner authenticates with the storage provider. This choice appears as the first step in the connection wizard.
Provide Credentials
Planton resolves credentials from the platform's secrets system at deploy time and injects them into the IaC engine. You store secrets (access keys, service account keys, access tokens) in Planton, and the runner receives them just before executing a stack job.
This is the default mode. Use it when:
- Credentials are managed centrally through Planton's Secrets Manager
- You want the platform to handle credential injection without any runner-side configuration
- The runner does not have direct access to the storage provider
Runner Environment
The runner authenticates using credentials already available in its own environment. Planton skips credential resolution entirely β only the storage location (bucket name, endpoint, region) is configured in the connection. The runner relies on mechanisms like:
- AWS S3: IRSA (EKS), EC2 instance profile, ECS task role, or
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYenvironment variables - GCS: Workload Identity (GKE), Application Default Credentials, or
GOOGLE_CREDENTIALSenvironment variable - Azure Blob: Managed Identity or
AZURE_STORAGE_KEYenvironment variable - Pulumi Cloud:
PULUMI_ACCESS_TOKENenvironment variable
Use this mode when:
- You deploy runners in your own infrastructure where IAM roles or environment variables provide storage access
- You prefer not to store state backend credentials in the platform
- You use cloud-native identity federation (IRSA, Workload Identity) for least-privilege access
Compatibility
Platform-managed backends always handle their own authentication. The authentication mode choice applies only to self-managed backends. If you create a self-managed backend and select Runner Environment, the platform validates that the combination is valid β for example, you cannot pair a platform-managed backend with runner-provided credentials.
Pulumi Backends
Pulumi state can be stored in four locations:
Pulumi Cloud (HTTP Backend)
The managed option. Pulumi Cloud stores state, provides a web UI for inspecting resources, and handles concurrent access control.
| Field | Description |
|---|---|
| API URL | Pulumi Cloud API endpoint (defaults to https://api.pulumi.com) |
| Pulumi Organization | Your Pulumi Cloud organization name |
| Access Token | A Pulumi access token with write permissions |
When to use: Getting started quickly, small teams, or when you want a managed experience and don't need state to remain in your infrastructure.
S3 Backend
Store Pulumi state in an Amazon S3 bucket (or any S3-compatible storage).
| Field | Description |
|---|---|
| S3 Bucket | The bucket name for state storage |
| Access Key ID | AWS IAM credentials for bucket access |
| Secret Access Key | AWS IAM credentials for bucket access |
| Endpoint | Custom endpoint for S3-compatible storage (R2, MinIO, etc.) |
| Region | AWS region, or "auto" for R2 |
When to use: State must stay in your AWS account, or you want to use S3-compatible storage like Cloudflare R2 or MinIO for a vendor-neutral backend.
GCS Backend
Store Pulumi state in a Google Cloud Storage bucket.
| Field | Description |
|---|---|
| GCS Bucket | The bucket name for state storage |
| Service Account Key | JSON key for a service account with Storage Object Admin role |
When to use: State must stay in your GCP project, or your organization standardizes on GCS for all object storage.
Azure Blob Backend
Store Pulumi state in an Azure Blob Storage container.
| Field | Description |
|---|---|
| Blob Storage Container | The container name for state storage |
| Storage Account Name | Your Azure Storage account name |
| Storage Account Key | Access key for the storage account |
When to use: State must stay in your Azure subscription.
Secrets Passphrase
For S3, GCS, and Azure Blob backends (the "DIY" backends), Pulumi encrypts sensitive values in state using a passphrase. You provide this passphrase when configuring the backend, and Planton uses it during deployments.
This is not needed for the Pulumi Cloud backend, which handles encryption internally.
Terraform Backends
Terraform state can be stored in three locations:
S3 Backend
The most common choice for AWS-centric organizations. Optionally, use a DynamoDB table for state locking to prevent concurrent modifications.
| Field | Description |
|---|---|
| S3 Bucket | The bucket name for state storage |
| Access Key ID | AWS IAM credentials for bucket access |
| Secret Access Key | AWS IAM credentials for bucket access |
| Region | AWS region for the bucket |
| DynamoDB Table | Table name for state locking (optional but recommended) |
| Endpoint | Custom endpoint for S3-compatible storage (optional) |
The S3 backend supports S3-compatible storage providers (Cloudflare R2, MinIO) by specifying a custom endpoint. When using non-AWS S3-compatible storage, you may need to enable compatibility flags for credential and region validation.
When to use: State must stay in your AWS account, you want DynamoDB-based locking, or you want to use S3-compatible storage.
GCS Backend
| Field | Description |
|---|---|
| GCS Bucket | The bucket name for state storage |
| Service Account Key | JSON key for a service account with Storage Object Admin role |
When to use: State must stay in your GCP project.
Azure RM Backend
| Field | Description |
|---|---|
| Resource Group Name | Azure resource group containing the storage account |
| Storage Account Name | Your Azure Storage account name |
| Container Name | The blob container for state files |
| Tenant ID | Azure AD tenant identifier |
| Subscription ID | Azure subscription identifier |
| Client ID | Service principal application ID |
| Client Secret | Service principal secret |
When to use: State must stay in your Azure subscription.
Connecting via the Web Console
- Navigate to Connections and click the Pulumi or Terraform card under Infrastructure as Code.
- Name your connection.
- Select the backend type. Choose Platform Managed to use Planton's built-in storage with no further configuration, or select a self-managed option (Pulumi Cloud, S3, GCS, or Azure for Pulumi; S3, GCS, or Azure RM for Terraform).
- Choose the authentication mode (self-managed backends only). Select Provide Credentials to store secrets in Planton, or Runner Environment if your runner already has access to the storage provider.
- Provide the backend-specific credentials if you selected Provide Credentials. This step is skipped when using Runner Environment or Platform Managed.
- Create the connection.
The Cloudflare R2 Story
Planton itself moved from Pulumi Cloud to Cloudflare R2 as its default state backend. The motivation was straightforward: self-hosted state eliminates dependency on a third-party service for a critical path operation (state reads happen on every deployment), reduces costs at scale, and keeps state geographically close to where deployments run.
R2 is S3-compatible, so it works with both the Pulumi S3 backend and the Terraform S3 backend. You configure it by specifying the R2 endpoint and using "auto" as the region.
This isn't a recommendation β Pulumi Cloud and Terraform Cloud are excellent choices for many teams. But if you want self-hosted state without managing your own object storage infrastructure, R2 is worth considering.
Practical Guidance
One Backend Per IaC Engine
You can configure separate state backends for Pulumi and Terraform if your organization uses both. Each has its own connection type and configuration.
State File Organization
Planton automatically organizes state files within the backend bucket. Each deployment gets a unique state path based on the organization, environment, resource type, and resource name. You don't need to manage state file paths manually.
Concurrent Access
Both Pulumi and Terraform support state locking to prevent concurrent modifications. For the S3 backend with Terraform, configure a DynamoDB table for locking. Pulumi handles locking differently depending on the backend (Pulumi Cloud handles it natively; DIY backends use the storage provider's conditional writes).
Switching from Platform-Managed to Self-Managed
To move from the platform-managed default to your own storage:
- Create a new state backend connection (Pulumi or Terraform) through the web console or CLI.
- Select your preferred backend type (S3, GCS, Azure Blob, or Pulumi Cloud for Pulumi; S3, GCS, or Azure RM for Terraform).
- Choose the authentication mode and provide credentials if using Provide Credentials.
- Mark the new connection as the default for your organization.
New deployments use the new default backend. The platform-managed connection remains in your organization but is no longer used for new stack jobs. Existing state files in the platform-managed backend are not migrated automatically β state migration is a manual operation if you need to consolidate.
Related Documentation
- Connections Overview β Understanding the Connect system
- Infrastructure β How infrastructure deployments use state backends
- Infrastructure: Stack Jobs β The atomic execution units that read and write state
- Runner: Deployment β Deploying runners in your own infrastructure (relevant for Runner Environment auth mode)
Next article