Skip to content
GitHubXDiscord

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.

Configure Alchemy to use S3 for state storage:

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"
})
});

Specify the S3 bucket and AWS region for state storage:

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"
})
});

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

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"
})
});

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

Terminal window
# 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

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:

{
"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/*"
]
}
]
}

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

Use a development-specific bucket or prefix:

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"
})
});

Share state across team members with appropriate bucket permissions:

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"
})
});

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
  • 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)