DocsAWS 101BlogServices

Migrating from LocalStack

LocalStack's pricing change in 2025 pushed a lot of teams to evaluate alternatives. MiniStack was built to be the low-friction drop-in: same single-port model, same SigV4-optional credential handling, same endpoint-override pattern. This page is the practical checklist for moving a LocalStack-based test suite across.

MIT licensed ~50 services single Docker image

TL;DR

For the majority of suites the migration is three diffs:

  1. Swap the Docker image: localstack/localstackministackorg/ministack.
  2. Keep pointing your SDKs at http://localhost:4566. Same port, same credential-free mode.
  3. Rename (or dual-set) the LocalStack env vars you depend on — see the table below.

A LocalStack Compose file typically reduces to:

services:
  ministack:
    image: ministackorg/ministack:latest
    ports:
      - "4566:4566"
    environment:
      - PERSIST_STATE=1            # was LOCALSTACK_PERSISTENCE (still honored)
      - LAMBDA_EXECUTOR=docker
      - DOCKER_NETWORK=myapp_default
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data/state:/tmp/ministack-state
      - ./data/s3:/tmp/ministack-data/s3

What works unchanged

  • Port 4566 serves every service. Same as LocalStack.
  • Any access key + secret is accepted. No signature validation. 12-digit keys become the account ID, just like LocalStack's multi-account mode.
  • boto3 / AWS SDK v1 / AWS SDK v2 / AWS CLI with an endpoint_url=http://localhost:4566 override. No code change.
  • Path-style S3 addressing. Same requirement as LocalStack.
  • Health check at /_localstack/health still responds (alongside /_ministack/health). Monitoring shims keep working.
  • Init scripts in /etc/localstack/init/boot.d and /etc/localstack/init/ready.d are picked up unchanged.
  • API Gateway test URLs — both LocalStack path styles are honored: /restapis/{id}/{stage}/_user_request_/{path} (v1-legacy) and /_aws/execute-api/{id}/{stage}/{path} (newer LS style). Plus MiniStack's own execute-api host-routing.

Environment variables

MiniStack honors a subset of LocalStack env-var names for drop-in compatibility, but prefers its own names for new setups. Both sides of the table are valid; pick whichever your team finds clearer.

LocalStack nameMiniStack nameNotes
LOCALSTACK_PERSISTENCE=1PERSIST_STATE=1MiniStack still recognises LOCALSTACK_PERSISTENCE and auto-enables S3_PERSIST when it's set.
PERSISTENCE=1(same)Legacy LS alias — not automatically honored. Use the names above.
LOCALSTACK_HOSTNAMEAWS_ENDPOINT_URLMiniStack reads LOCALSTACK_HOSTNAME inside Lambda environments for URL routing, so user code that already reads it keeps working. New code should use AWS_ENDPOINT_URL.
LAMBDA_DOCKER_NETWORKDOCKER_NETWORKLAMBDA_DOCKER_NETWORK still works as a Lambda-only scoped alias; DOCKER_NETWORK covers every container-backed service (RDS, EKS, ElastiCache, Lambda).
LAMBDA_DOCKER_FLAGS(same)Exact match — whitelisted flag set also matches LS.
LAMBDA_EXECUTOR(same)local / docker / docker-reuse. LAMBDA_STRICT=1 is MiniStack-specific (forces RIE-in-Docker).
LOCALSTACK_SFN_MOCK_CONFIGSFN_MOCK_CONFIGBoth honored. Same AWS SFN Local mock-config format as LS.
GATEWAY_PORT / EDGE_PORT(same)Same fallback order.
SERVICES(same)Comma-separated filter. Works identically.
DEBUG=1LOG_LEVEL=DEBUGMiniStack uses the Python logging level name directly.

See Configuration for the full env-var reference.

Admin endpoints

LocalStack pathMiniStack pathStatus
/_localstack/health/_ministack/healthBoth served. Same JSON shape.
/health/healthAlso served as a convenience.
/_localstack/* (other)/_ministack/*Unknown /_localstack/* paths return a clear 404 JSON naming the MiniStack equivalent.
(no equivalent)/_ministack/resetWipes state. ?init=1 re-runs init scripts. LocalStack Pro has /_localstack/state/reset; MiniStack's is free.
(no equivalent)/_ministack/ses/messagesInspect sent emails by account. LocalStack has its own SES inspector under /_aws/ses; MiniStack's path is different — grep your tests.

Init scripts

LocalStack's init hooks are supported exactly as-is. MiniStack scans both paths:

  • /etc/localstack/init/boot.d/* and /docker-entrypoint-initaws.d/* — run synchronously before the server accepts traffic.
  • /etc/localstack/init/ready.d/* and /docker-entrypoint-initaws.d/ready.d/* — run asynchronously after startup; /_ministack/ready reflects completion.

The pre-populated env (AWS_ACCESS_KEY_ID=test, AWS_DEFAULT_REGION=us-east-1, AWS_ENDPOINT_URL=http://localhost:4566) matches LocalStack's convention, so scripts that start with aws s3 mb s3://... just work.

Testcontainers

If you were using org.testcontainers:localstack, swap to org.ministack:testcontainers-ministack. The API is narrower (and clearer):

// LocalStack:
LocalStackContainer ls = new LocalStackContainer(DockerImageName.parse("localstack/localstack:3"))
    .withServices(S3, SQS, DYNAMODB);
String endpoint = ls.getEndpointOverride(S3).toString();

// MiniStack:
MiniStackContainer ms = new MiniStackContainer("1.3.14");
String endpoint = ms.getEndpoint();  // one endpoint for every service

Python suites that used the testcontainers-python LocalStackContainer helper should switch to a generic DockerContainer("ministackorg/ministack:...") — see Testcontainers (Python) for the recommended fixture.

Terraform / CDK / Pulumi

Your provider config is unchanged. LocalStack's endpoints { ... } block pointed at http://localhost:4566; MiniStack's is identical. See IaC for the exact provider blocks. If you were using tflocal / awslocal, you can keep them (they're just endpoint-override wrappers) or replace them with explicit --endpoint-url on the AWS CLI.

Known incompatibilities

  • API Gateway custom ID tag. LocalStack's ls-custom-id tag is not accepted — use ms-custom-id. MiniStack returns an explicit BadRequestException naming the replacement so migrations fail loud, not silent.
  • LocalStack Pro-only services. If your suite depended on Pro services (Kafka/MSK, Neptune, DocumentDB, Cloud Control, IoT, etc.), they don't exist in MiniStack. Check the services catalog before migrating.
  • LocalStack extensions / hooks. The LS extensions API (localstack-extension-*) has no equivalent — MiniStack is a single process without a plugin system today.
  • CloudFormation resource coverage differs. MiniStack supports 85 resource types with a list on the CFN engine page. LocalStack's free tier covers a smaller set; Pro is broader. Run create-stack against MiniStack and look for UnsupportedResource in the events.
  • Account ID default. Both default to 000000000000, but some MiniStack code paths use 123456789012 interchangeably. If you hard-coded the account in ARNs, normalize to one value.
  • Timestamp format. Some MiniStack responses return epoch floats where LocalStack returns ISO strings. Intentional — the Go/Terraform SDK works better with floats. If you assert on timestamp shapes, relax the assertion to accept both.

What MiniStack doesn't do

Before you migrate, confirm MiniStack doesn't drop a feature you rely on:

  • No real VPC networking, no real IAM policy evaluation (same as LocalStack free).
  • No scheduled triggers — EventBridge rules with cron/rate expressions store but don't fire. If your tests depend on scheduled execution, call PutEvents manually.
  • CloudWatch alarms don't dispatch their actions. SES identities auto-verify. Cognito Lambda triggers don't invoke. See Known limitations for the full "stored but not dispatched" list.
  • Some LocalStack integrations that MiniStack hasn't filled yet: API destination HTTP calls from EventBridge, ECS task logs → CloudWatch, Step Functions logging. File issues if any of these block you — several are on the roadmap.
Hit something that worked in LocalStack but not here? File it at github.com/ministackorg/ministack/issues with the failing request — drop-in compatibility is an explicit goal.