Endpoint Configuration Guide
This guide shows how to point each major AWS SDK and tool at a running Substrate server instead of the real AWS APIs.
Substrate listens on :4566 by default — the same default port as LocalStack — so most LocalStack configurations work without changes.
Quick start: substratelocal wrapper
The substratelocal binary injects the necessary environment variables into any child process automatically:
substratelocal aws s3 ls
substratelocal terraform apply
substratelocal python deploy.pyBy default it targets http://localhost:4566. Override with --endpoint or SUBSTRATE_ENDPOINT:
substratelocal --endpoint http://my-ci-host:4566 aws s3 ls
SUBSTRATE_ENDPOINT=http://my-ci-host:4566 substratelocal aws s3 lsAWS CLI v2
Environment variable (recommended)
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
aws s3 ls
aws dynamodb list-tablesPer-command flag
aws --endpoint-url http://localhost:4566 s3 lsAWS profile (~/.aws/config)
[profile substrate]
endpoint_url = http://localhost:4566
region = us-east-1
[profile substrate]
aws_access_key_id = test
aws_secret_access_key = testThen use --profile substrate or export AWS_PROFILE=substrate.
AWS SDK for Go v2
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
cfg, err := config.LoadDefaultConfig(context.Background(),
config.WithRegion("us-east-1"),
config.WithCredentialsProvider(
aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) {
return aws.Credentials{
AccessKeyID: "test",
SecretAccessKey: "test",
}, nil
}),
),
config.WithEndpointResolverWithOptions(
aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{URL: "http://localhost:4566"}, nil
}),
),
)
client := s3.NewFromConfig(cfg)Or set AWS_ENDPOINT_URL=http://localhost:4566 and let the SDK pick it up automatically (SDK v2 ≥ 1.27.0 honours AWS_ENDPOINT_URL).
AWS SDK for Python (boto3)
import boto3
s3 = boto3.client(
"s3",
endpoint_url="http://localhost:4566",
aws_access_key_id="test",
aws_secret_access_key="test",
region_name="us-east-1",
)
s3.list_buckets()Or set the environment variables and omit the explicit kwargs:
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1Terraform (AWS provider)
provider "aws" {
region = "us-east-1"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
s3 = "http://localhost:4566"
dynamodb = "http://localhost:4566"
iam = "http://localhost:4566"
lambda = "http://localhost:4566"
sqs = "http://localhost:4566"
sns = "http://localhost:4566"
# add other services as needed
}
}CDK (AWS Cloud Development Kit)
Set the environment variables before running cdk deploy:
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
cdk deployOr use substratelocal:
substratelocal cdk deployPrism integration
Prism polls /_localstack/health to verify services are available. Substrate exposes this endpoint and returns all registered plugins as "available":
{
"services": {
"s3": "available",
"dynamodb": "available",
"lambda": "available",
...
},
"version": "v0.68.0"
}Set the following environment variables before starting Prism:
export LOCALSTACK_ENDPOINT=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=testDocker / Docker Compose
Run Substrate as a sidecar and configure the AWS SDK to use the container's hostname:
services:
substrate:
image: ghcr.io/scttfrdmn/substrate:latest
ports:
- "4566:4566"
app:
image: my-app
environment:
AWS_ENDPOINT_URL: http://substrate:4566
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
AWS_DEFAULT_REGION: us-east-1
depends_on:
- substrateState reset between tests
To wipe all emulated state between test suites without restarting the server, send a POST /v1/state/reset:
curl -X POST http://localhost:4566/v1/state/reset
# {"status":"ok"}In Go integration tests, use the [StartTestServer] helper:
func TestMyWorkflow(t *testing.T) {
ts := substrate.StartTestServer(t)
t.Run("first case", func(t *testing.T) {
defer ts.ResetState(t)
// ... test using ts.URL ...
})
t.Run("second case", func(t *testing.T) {
defer ts.ResetState(t)
// ... clean state ...
})
}