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.
TL;DR
For the majority of suites the migration is three diffs:
- Swap the Docker image:
localstack/localstack→ministackorg/ministack. - Keep pointing your SDKs at
http://localhost:4566. Same port, same credential-free mode. - 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:4566override. No code change. - Path-style S3 addressing. Same requirement as LocalStack.
- Health check at
/_localstack/healthstill responds (alongside/_ministack/health). Monitoring shims keep working. - Init scripts in
/etc/localstack/init/boot.dand/etc/localstack/init/ready.dare 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 ownexecute-apihost-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 name | MiniStack name | Notes |
|---|---|---|
LOCALSTACK_PERSISTENCE=1 | PERSIST_STATE=1 | MiniStack 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_HOSTNAME | AWS_ENDPOINT_URL | MiniStack 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_NETWORK | DOCKER_NETWORK | LAMBDA_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_CONFIG | SFN_MOCK_CONFIG | Both 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=1 | LOG_LEVEL=DEBUG | MiniStack uses the Python logging level name directly. |
See Configuration for the full env-var reference.
Admin endpoints
| LocalStack path | MiniStack path | Status |
|---|---|---|
/_localstack/health | /_ministack/health | Both served. Same JSON shape. |
/health | /health | Also served as a convenience. |
/_localstack/* (other) | /_ministack/* | Unknown /_localstack/* paths return a clear 404 JSON naming the MiniStack equivalent. |
| (no equivalent) | /_ministack/reset | Wipes state. ?init=1 re-runs init scripts. LocalStack Pro has /_localstack/state/reset; MiniStack's is free. |
| (no equivalent) | /_ministack/ses/messages | Inspect 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/readyreflects 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-idtag is not accepted — usems-custom-id. MiniStack returns an explicitBadRequestExceptionnaming 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-stackagainst MiniStack and look forUnsupportedResourcein the events. - Account ID default. Both default to
000000000000, but some MiniStack code paths use123456789012interchangeably. 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
PutEventsmanually. - 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.