Overcast is alpha — behaviour and APIs may change between releases. Pin your version and read the changelog before upgrading.

overcast

Getting Started

Using AWS SDKs and CLI with Overcast

Pointing the AWS CLI, Node.js, Python, Go, Java, .NET, Rust and Terraform at Overcast, plus overcast env and overcast aws, S3 addressing styles, and the credential and region defaults.

Overcast is a drop-in local AWS endpoint. Point any AWS SDK or the AWS CLI at http://localhost:4566 and use it exactly as you would against real AWS.


AWS CLI

--endpoint-url flag

AWS CLI v2 accepts --endpoint-url as a global flag on any command:

aws --endpoint-url http://localhost:4566 s3 ls
aws --endpoint-url http://localhost:4566 sqs list-queues
aws --endpoint-url http://localhost:4566 dynamodb list-tables

This is the quickest way to try Overcast without changing any configuration.

overcast aws and overcast env

The overcast binary can drive the AWS CLI for you. overcast aws runs the aws CLI from your PATH with the endpoint, test credentials, and region already set — and with every ambient AWS_* variable scrubbed first, so a stray profile or region in your shell can never send a command to real AWS:

overcast aws s3 mb s3://my-bucket
overcast aws sqs list-queues

Migrating from LocalStack? alias awslocal='overcast aws' gives you the familiar name.

(For the rest of the overcast CLI — starting background instances, logs, reset, services, and more — see the CLI reference.)

For every other tool, overcast env prints the same variables as exports for your shell (sh, PowerShell, and fish output are supported — auto-detected, override with --shell). The output also unsets every other AWS_* variable your shell currently exports — AWS_PROFILE, AWS_SESSION_TOKEN, a per-service AWS_ENDPOINT_URL_<SERVICE> — so after the eval, nothing left over can redirect a call to real AWS:

eval "$(overcast env)"
aws s3 ls        # any AWS tool in this shell now talks to Overcast

One thing overcast env deliberately leaves alone is ~/.aws itself: your config and credentials files stay readable, and the exported variables outrank them everywhere it matters (credentials, region, endpoint). For a single call with total isolation from local AWS configuration, use overcast aws, which also points the config-file variables at an empty file.

Setting AWS_ENDPOINT_URL avoids repeating the flag on every command:

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

In CI, overcast wait --timeout 60s blocks until the daemon reports healthy, so the job’s first real AWS call never races the startup.

Then use the CLI normally:

aws s3 mb s3://my-bucket
aws s3 cp file.txt s3://my-bucket/
aws sqs create-queue --queue-name my-queue
aws dynamodb create-table \
  --table-name users \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

AWS CLI profile

Add to ~/.aws/config:

[profile overcast]
aws_access_key_id = test
aws_secret_access_key = test
region = us-east-1
endpoint_url = http://localhost:4566

Then:

aws --profile overcast s3 ls

S3 addressing styles

Both styles work, with no configuration:

  • Path-style — http://localhost:4566/bucket/key. Always works, needs no DNS at all. The CLI uses it automatically with --endpoint-url or AWS_ENDPOINT_URL.
  • Virtual-hosted style — http://mybucket.s3.localhost:4566/key and the bare http://mybucket.localhost:4566/key. The bare form is what an AWS SDK emits against a custom endpoint when path-style is disabled, and the only form CDK’s asset publisher uses.

Virtual-hosted style needs the bucket subdomain to resolve, so set OVERCAST_HOSTNAME=localhost.overcast.sh — see Hostnames that resolve for every caller for why, and for the offline fallbacks. Host-routed addressing has the full rule and the reserved service labels a bucket name cannot carry in the bare form; the CDK S3 asset upload troubleshooting covers the CDK-specific case.


SDK examples

The pattern is the same in every language: point the client at http://localhost:4566, pass any static credentials, and (for S3) enable path-style addressing.

Node.js (AWS SDK v3)

import { S3Client, CreateBucketCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: "http://localhost:4566",
  region: "us-east-1",
  credentials: { accessKeyId: "test", secretAccessKey: "test" },
  forcePathStyle: true,
});

await s3.send(new CreateBucketCommand({ Bucket: "my-bucket" }));

The same pattern works for any service client — set endpoint:

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { SQSClient } from "@aws-sdk/client-sqs";
import { LambdaClient } from "@aws-sdk/client-lambda";

const dynamodb = new DynamoDBClient({
  endpoint: "http://localhost:4566",
  region: "us-east-1",
  credentials: { accessKeyId: "test", secretAccessKey: "test" },
});

const sqs = new SQSClient({
  endpoint: "http://localhost:4566",
  region: "us-east-1",
  credentials: { accessKeyId: "test", secretAccessKey: "test" },
});

Using AWS_ENDPOINT_URL

AWS SDK v3 (v3.451.0+) respects the AWS_ENDPOINT_URL environment variable. Set it once and skip per-client endpoint configuration:

export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
// No endpoint needed — the SDK reads AWS_ENDPOINT_URL
const s3 = new S3Client({ region: "us-east-1", forcePathStyle: true });

Python (boto3)

import boto3

s3 = boto3.client(
    's3',
    endpoint_url='http://localhost:4566',
    region_name='us-east-1',
    aws_access_key_id='test',
    aws_secret_access_key='test',
)

s3.create_bucket(Bucket='my-bucket')
s3.put_object(Bucket='my-bucket', Key='hello.txt', Body=b'hello')

Or set AWS_ENDPOINT_URL and use the default session:

export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
import boto3
s3 = boto3.client('s3', region_name='us-east-1')

Go (AWS SDK v2)

package main

import (
    "context"
    "fmt"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
    cfg, _ := config.LoadDefaultConfig(context.TODO(),
        config.WithRegion("us-east-1"),
        config.WithCredentialsProvider(
            credentials.NewStaticCredentialsProvider("test", "test", ""),
        ),
    )

    client := s3.NewFromConfig(cfg, func(o *s3.Options) {
        o.BaseEndpoint = aws.String("http://localhost:4566")
        o.UsePathStyle = true
    })

    _, err := client.CreateBucket(context.TODO(), &s3.CreateBucketInput{
        Bucket: aws.String("my-bucket"),
    })
    fmt.Println("Created bucket:", err)
}

Java (AWS SDK v2)

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Configuration;
import java.net.URI;

S3Client s3 = S3Client.builder()
    .endpointOverride(URI.create("http://localhost:4566"))
    .region(Region.US_EAST_1)
    .credentialsProvider(StaticCredentialsProvider.create(
        AwsBasicCredentials.create("test", "test")))
    .serviceConfiguration(S3Configuration.builder()
        .pathStyleAccessEnabled(true)
        .build())
    .build();

s3.createBucket(b -> b.bucket("my-bucket"));

.NET (AWS SDK)

using Amazon.S3;
using Amazon.Runtime;

var config = new AmazonS3Config
{
    ServiceURL = "http://localhost:4566",
    ForcePathStyle = true,
};

var credentials = new BasicAWSCredentials("test", "test");
var client = new AmazonS3Client(credentials, config);

await client.PutBucketAsync("my-bucket");

Rust (AWS SDK)

use aws_config::BehaviorVersion;
use aws_sdk_s3::config::{Credentials, Region};

let creds = Credentials::new("test", "test", None, None, "overcast");
let config = aws_config::defaults(BehaviorVersion::latest())
    .region(Region::new("us-east-1"))
    .credentials_provider(creds)
    .endpoint_url("http://localhost:4566")
    .load()
    .await;

let s3 = aws_sdk_s3::Client::new(&config);
s3.create_bucket().bucket("my-bucket").send().await.unwrap();

Terraform / OpenTofu

Overcast works with the AWS provider’s custom endpoints:

provider "aws" {
  access_key = "test"
  secret_key = "test"
  region     = "us-east-1"

  s3_use_path_style           = true
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    s3             = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    iam            = "http://localhost:4566"
    sts            = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    # ... add endpoints for all services you use
  }
}

General notes

Credentials

Overcast accepts any credentials. Use test/test or any non-empty strings. SigV4 signatures are accepted but not verified unless you opt in with OVERCAST_SIGV4_VALIDATE=true, which rejects an invalid or expired signature with 403 InvalidSignatureException.

Single endpoint

All AWS services are served from one endpoint (http://localhost:4566). You do not need per-service ports or URLs.

Account ID and region

Overcast returns 000000000000 as the account ID and us-east-1 as the region by default. These appear in ARNs and STS responses. Override with OVERCAST_ACCOUNT_ID and OVERCAST_DEFAULT_REGION.

HTTPS

For browser-trusted HTTPS (and HTTP/2) run overcast https enable once and start the daemon with OVERCAST_TLS=auto; to bring your own certificate, configure OVERCAST_TLS_CERT and OVERCAST_TLS_KEY. Point SDK clients at the CA with AWS_CA_BUNDLE=~/.overcast/data/ca/rootCA.pem. See HTTPS and HTTP/2 for details.