# How to manage OpenTofu state with Quake AI S3

Source: https://docs.quake.ai/docs/automation/how-to/state-management
Markdown: https://docs.quake.ai/docs/automation/how-to/state-management.md

---

# How to manage OpenTofu state with Quake AI S3

By default, OpenTofu stores state in a local `terraform.tfstate` file. This works for solo development but breaks down when multiple people or CI pipelines need to apply changes to the same infrastructure. Remote state with locking solves this.

Quake AI provides S3-compatible object storage (Swift + Ceph), which works as an OpenTofu S3 backend. This guide shows you how to set it up.

## Prerequisites

- OpenTofu or Terraform installed
- A Quake AI account with S3 credentials (access key and secret key from the dashboard)
- An existing S3 bucket on Quake AI (create one through the dashboard or using the [S3 Storage with ACLs template](/resources/iac-templates/s3-storage-acl))

## Create a state bucket

If you do not already have a bucket for state, create one through the Quake AI dashboard or the AWS CLI configured for Quake AI:

```bash
aws s3 mb s3://my-project-tfstate \
  --endpoint-url https://object.YOUR_REGION.rumble.cloud
```

Enable versioning on the bucket to protect against accidental state corruption:

```bash
aws s3api put-bucket-versioning \
  --bucket my-project-tfstate \
  --versioning-configuration Status=Enabled \
  --endpoint-url https://object.YOUR_REGION.rumble.cloud
```

## Configure the S3 backend

Add a `backend` block to your OpenTofu configuration. This typically goes in a `backend.tf` file:

```hcl
terraform {
  backend "s3" {
    bucket = "my-project-tfstate"
    key    = "infrastructure/terraform.tfstate"
    region = "us-east-1"

    endpoints = {
      s3 = "https://object.YOUR_REGION.rumble.cloud"
    }

    skip_credentials_validation = true
    skip_metadata_api_check     = true
    skip_region_validation      = true
    skip_requesting_account_id  = true
    use_path_style              = true
  }
}
```

The `skip_*` flags are required because Quake AI's S3-compatible endpoint does not implement every AWS-specific metadata API. The `use_path_style` flag ensures bucket names appear in the URL path rather than as subdomains.


The S3 backend protocol requires a `region` value. Set it to `us-east-1` to match the example above. Quake AI does not use this value for routing.


## Set S3 credentials

The S3 backend reads credentials from environment variables. Add these alongside your OpenStack credentials:

```bash
export AWS_ACCESS_KEY_ID="YOUR_S3_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="YOUR_S3_SECRET_KEY"
```


These are your Quake AI S3 credentials, not AWS credentials. Generate them from the Quake AI dashboard under **Object Storage** > **S3 Credentials**.


## Migrate existing local state

If you already have a local `terraform.tfstate` file, reinitialize to migrate it to the remote backend:

```bash
tofu init -migrate-state
```

OpenTofu prompts you to confirm the migration. After confirmation, the local state file is copied to the S3 bucket, and future operations read and write state remotely.

Verify the migration by checking the bucket contents:

```bash
aws s3 ls s3://my-project-tfstate/infrastructure/ \
  --endpoint-url https://object.YOUR_REGION.rumble.cloud
```

You should see `terraform.tfstate` listed.

## State locking

The S3 backend supports state locking through DynamoDB, but Quake AI does not provide a DynamoDB-compatible service. This means concurrent `tofu apply` operations from different machines could corrupt state.

To mitigate this:

- **CI/CD serialization.** Configure your CI pipeline to run only one apply at a time (see [CI/CD integration](/docs/automation/how-to/cicd-integration))
- **Team discipline.** Agree that only the CI pipeline applies changes, never individual developers
- **State snapshots.** Bucket versioning (configured above) lets you recover from corruption by restoring a previous state version


If your team requires strict locking, consider running a lightweight DynamoDB-compatible service (such as ScyllaDB Alternator) on Quake AI and configuring the `dynamodb_table` backend option.


## Multiple state files per project

For larger projects, use separate state files per environment or component. Change the `key` value in the backend configuration:

```hcl
# Production
key = "production/terraform.tfstate"

# Staging
key = "staging/terraform.tfstate"

# Networking (shared across environments)
key = "shared/networking.tfstate"
```

This isolates blast radius: a bad apply in staging cannot corrupt production state.

## See also

- [How to get started with Infrastructure as Code on Quake AI](/docs/automation/how-to/getting-started-iac)
- [S3 Storage with ACLs template](/resources/iac-templates/s3-storage-acl)
- [How to manage multiple environments with OpenTofu](/docs/automation/how-to/multi-environment)
- [How to integrate OpenTofu with CI/CD](/docs/automation/how-to/cicd-integration)
