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

overcast

LocalStack endpoints and init hooks

Which /_localstack and /_aws paths Overcast serves as-is, what the rest map to, and how LocalStack health checks, init-hook directories and Testcontainers modules carry over.

The paths a migrated test suite already calls, behind Migrating from LocalStack. Six are served as-is; the rest answer 404 naming the Overcast endpoint that replaces them.

Endpoint mapping

LocalStackOvercastAvailability
/_localstack/healthserved as-is, or /_overcast/healthAlways
/_localstack/initserved as-is, or /_overcast/initAlways
/_localstack/init/{stage}served as-is, or /_overcast/init/{stage}Always
POST /_localstack/state/resetserved as-is, or /_overcast/resetAlways
GET/DELETE /_aws/sesserved as-is, or /_overcast/ses/inbox/messagesAlways
/_aws/sqs/messagesserved as-is, or GET /{account}/{queue}Always
/_localstack/health (detailed)/_overcast/debug/healthRequires OVERCAST_DEBUG=true
/_localstack/info/_overcast/debug/configRequires OVERCAST_DEBUG=true
/_localstack/state/_overcast/debug/stateRequires OVERCAST_DEBUG=true
/_localstack/diagnose/_overcast/debug/state and /_overcast/debug/configRequires OVERCAST_DEBUG=true
/_localstack/config/_overcast/debug/configRead-only: configuration is fixed for the process
/_localstack/usage/_overcast/metricsAlways
/_localstack/state/save, /loadPersistence is incremental, not snapshot-based
/_aws/sns/sms-messages/_overcast/ses/inbox/messages, entries with "kind": "sms"Always
/_aws/sns/platform-endpoint-messages/_overcast/ses/inbox/messages, entries with "kind": "push"Always
/_aws/lambda/runtimes/_overcast/lambda/runtimesAlways; Overcast’s shape carries more per runtime
/_aws/execute-api/{id}/{stage}/…/restapis/{id}/{stage}/_user_request_/…, or the host formAlways
/_aws/sns/subscription-tokens/{arn}, DELETE /_aws/dynamodb/expiredThe token is not exposed; TTL expiry has no manual trigger

The init pair needs no translation. Overcast’s own status endpoint already answers in LocalStack’s shape — the same BOOT/START/READY/SHUTDOWN stages, the same UNKNOWN/RUNNING/SUCCESSFUL/ERROR script states — so a localstack wait or an init-script poll reads what it expects. Reset answers {"status":"reset"} where LocalStack returns an empty body; nothing checking the status code notices.

The /_aws/ pair is the one a test suite hits: curl localhost:4566/_aws/ses after a send, /_aws/sqs/messages?QueueUrl=… to peek a queue without consuming it. Both answer in LocalStack’s shape, over the same store Overcast’s own endpoint reads — see the debug endpoints reference for the parameters and the two SES fields (Region, RawData) that are omitted because the capture does not hold them.

Every other path under /_localstack/ or /_aws/ answers 404 with the Overcast endpoint that replaces it, so a missed one says so instead of returning an S3 error. The remaining /_aws/* aliases are tracked in #1545.

Health checks

/_localstack/health is the one endpoint in the table you do not have to change. Overcast serves it, in LocalStack’s response shape — a services map plus edition and version — so a compose healthcheck, a localstack wait, or a Testcontainers HTTP wait strategy carried over unedited keeps working:

services:
  overcast:
    image: ghcr.io/overcast-sh/overcast:latest
    ports: ["4566:4566"]
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://localhost:4566/_localstack/health"]
      interval: 5s
      timeout: 3s
      retries: 5
// Testcontainers, any language: the LocalStack path works unchanged.
new GenericContainer("ghcr.io/overcast-sh/overcast-slim:latest")
  .withExposedPorts(4566)
  .withWaitStrategy(Wait.forHttp("/_localstack/health", 4566));

Prefer /_overcast/health for anything you are writing fresh — it reports per-service emulation tiers, the resolved storage backend and Docker connectivity, none of which LocalStack’s shape has a field for.

Warning

Do fix a healthcheck that points at neither. A 404 there is indistinguishable from a dead container: the orchestrator restarts Overcast, and on the default in-memory state backend a restart is a wipe — so a deploy running at the time loses the resources it had already created, and the client polling them is told they no longer exist. Set OVERCAST_STATE=persistent with a mounted volume if you need state to survive a restart at all.

Init hooks

Shell scripts in /etc/localstack/init/<stage>.d/ run at the matching lifecycle stage, with no configuration. An Overcast-native /etc/overcast/init/<stage>.d/ works the same way; both trees are scanned, LocalStack’s first.

services:
  overcast:
    image: ghcr.io/overcast-sh/overcast:latest
    ports: ["4566:4566"]
    volumes:
      - "./init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh"
StageDirectoryWhen it runs
BOOTboot.d/Before the emulator starts (as root)
STARTstart.d/After config is loaded, before HTTP
READYready.d/After the server is listening
SHUTDOWNshutdown.d/On graceful shutdown

Scripts need the .sh extension and the executable bit. They run in alphabetical order, subdirectories depth-first, and a failing script does not block the ones after it. Check what ran at /_overcast/init (or /_overcast/init/ready for one stage) — always available, no debug flag needed.

The image ships awslocal, the same thin aws wrapper, so init scripts carry over unchanged:

#!/bin/bash
awslocal s3 mb s3://my-bucket
awslocal sqs create-queue --queue-name my-queue

Note

awslocal needs the aws CLI present in the container. Install it in a boot.d hook or a custom image layer.

On a native Windows daemon, hooks run through cmd.exe /c, so a .sh script needs WSL or Git Bash on the PATH.

Tune with OVERCAST_INIT_ENABLED (default true), OVERCAST_INIT_DIRS (default /etc/localstack/init,/etc/overcast/init) and OVERCAST_INIT_TIMEOUT (default 30s).

Testcontainers

Point an existing LocalStack Testcontainers module at the Overcast image (Java’s asCompatibleSubstituteFor, for example) and it fails in non-obvious ways: those modules parse the image tag as a LocalStack version to pick legacy behaviours, and wait for a log line Overcast does not emit. Use Overcast’s own module — a Go module ships today, and the generic-container recipe on that page works from every other language.