Skip to content

S3StateStore

The S3StateStore provides reliable, scalable state storage for Alchemy applications using Amazon S3. It's designed for cloud-based deployments where you need durable, shared state storage across multiple environments or team members.

Basic Usage

Configure Alchemy to use S3 for state storage:

ts
import { S3StateStore } from "alchemy/aws";

const app = await alchemy("my-app", {
  stage: "prod",
  phase: process.argv.includes("--destroy") ? "destroy" : "up",
  stateStore: (scope) => new S3StateStore(scope, {
    bucketName: "my-app-alchemy-state",
    region: "us-east-1"
  })
});

Configuration Options

Bucket Name and Region

Specify the S3 bucket and AWS region for state storage:

ts
import { S3StateStore } from "alchemy/aws";

const app = await alchemy("my-app", {
  stateStore: (scope) => new S3StateStore(scope, {
    bucketName: "my-company-alchemy-state",
    region: "us-west-2"
  })
});

Custom Prefix

Use a prefix to organize state files when sharing buckets across projects:

ts
import { S3StateStore } from "alchemy/aws";

const app = await alchemy("my-app", {
  stateStore: (scope) => new S3StateStore(scope, {
    bucketName: "shared-alchemy-state",
    prefix: "my-team/my-app/",
    region: "us-east-1"
  })
});

Prerequisites

Create S3 Bucket

The S3 bucket must exist before using S3StateStore. Use the Alchemy bootstrap command (recommended) or create it manually:

bash
# Recommended: Use Alchemy bootstrap
alchemy bootstrap s3

# Or create manually with AWS CLI
aws s3 mb s3://my-app-alchemy-state --region us-east-1

Configure AWS Credentials

Ensure AWS credentials are configured via:

  • AWS credentials file (~/.aws/credentials)
  • Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  • IAM roles (for EC2/Lambda)
  • AWS CLI profile

The credentials need S3 permissions for the target bucket:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-alchemy-state",
        "arn:aws:s3:::my-app-alchemy-state/*"
      ]
    }
  ]
}

State Organization

S3StateStore organizes state files using scope-based prefixes:

my-app-alchemy-state/
  alchemy/my-app/dev/
    my-resource
    my-other-resource
  alchemy/my-app/prod/
    my-resource
    my-other-resource

Keys containing forward slashes are converted to colons for S3 compatibility:

  • Resource key: api/database/connection
  • S3 object key: alchemy/my-app/dev/api:database:connection

Environment-Specific Configuration

Development

Use a development-specific bucket or prefix:

ts
const isDev = process.env.NODE_ENV === "development";

const app = await alchemy("my-app", {
  stage: isDev ? "dev" : "prod",
  stateStore: (scope) => new S3StateStore(scope, {
    bucketName: isDev ? "my-app-dev-state" : "my-app-prod-state",
    region: "us-east-1"
  })
});

Team Environments

Share state across team members with appropriate bucket permissions:

ts
const app = await alchemy("my-app", {
  stage: "shared",
  stateStore: (scope) => new S3StateStore(scope, {
    bucketName: "team-shared-alchemy-state",
    prefix: `${process.env.USER || "unknown"}/`,
    region: "us-east-1"
  })
});

Error Handling

S3StateStore includes built-in retry logic and proper error handling:

  • NoSuchBucket: Clear error message if bucket doesn't exist
  • NoSuchKey: Gracefully handles missing state files
  • Network errors: Automatic retry with exponential backoff
  • Permissions: Clear AWS permission error messages

Performance Considerations

  • Eventual consistency: S3 provides eventual consistency for state operations
  • Batch operations: Uses concurrent requests for multi-key operations
  • Network latency: Consider bucket region proximity to your deployment location
  • Costs: S3 charges for requests and storage (typically very low for state files)