MiniStack is a free, MIT-licensed, drop-in local AWS emulator (a LocalStack alternative). It emulates 60+ AWS services (S3, SQS, DynamoDB, Lambda, IAM, STS, RDS, etc.) behind a single local endpoint, so you can develop and test AWS-based applications entirely offline, with no AWS account, API key, or telemetry.
This tutorial assumes the AWS CLI is already installed and covers:
- Installing MiniStack
- Starting and verifying MiniStack
- Configuring the AWS CLI so you don't need
--endpoint-urlon every command - Environment management — reset, restart, persistence, full cleanup
Choose one of the two methods below.
pip install ministackStart it:
ministackBy default, MiniStack listens on http://localhost:4566. To use a different port:
GATEWAY_PORT=5000 ministackdocker run -d --name ministack -p 4566:4566 ministackorg/ministackWith disk persistence enabled (state survives container restarts — see section 4):
docker run -d --name ministack \
-p 4566:4566 \
-e PERSIST_STATE=1 \
-e STATE_DIR=/data/ministack-state \
-v /tmp/ministack-data:/data \
ministackorg/ministackIf you want real backing services (actual Postgres/MySQL containers for RDS, real Redis for ElastiCache, real Docker containers for ECS, etc.), mount the Docker socket:
docker run -d --name ministack \
-p 4566:4566 \
-v /var/run/docker.sock:/var/run/docker.sock \
ministackorg/ministackOr via Docker Compose, if you cloned the repo:
git clone https://github.com/ministackorg/ministack
cd ministack
docker compose up -dcurl http://localhost:4566/_ministack/healthHealthy response:
{"services": {"...": "available"}, "status": "available"}If you're waiting for init.d/ready.d startup scripts to finish before running anything against it, poll readiness instead:
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:4566/_ministack/readyReturns 503 while init scripts are still running, 200 once ready.
MiniStack doesn't validate credentials, but the AWS CLI still requires something to be configured.
Create a dedicated profile:
aws configure --profile ministackAWS Access Key ID: test
AWS Secret Access Key: test
Default region name: us-east-1
Default output format: json
Now, to avoid passing --endpoint-url=http://localhost:4566 on every command, edit ~/.aws/config and add a services block tied to this profile:
[profile ministack]
region = us-east-1
output = json
endpoint_url = http://localhost:4566export AWS_PROFILE=ministack
aws s3 ls
aws dynamodb list-tablesexport AWS_PROFILE=ministack
aws s3 ls
aws dynamodb list-tablesNo --endpoint-url needed, and other profiles pointing at real AWS stay untouched.
Quicker alternative for a throwaway shell session:
export AWS_ENDPOINT_URL=http://localhost:4566applies to every service globally, but it's less safe long-term since it isn't scoped to a specific profile.
This is the part people often miss: MiniStack keeps state in memory (and optionally on disk), and it exposes an admin HTTP surface under /_ministack/* for managing that state without tearing the whole thing down.
Wipes all in-memory state across every service (buckets, queues, tables, functions, everything) but keeps the MiniStack process/container alive:
curl -X POST http://localhost:4566/_ministack/resetUseful between test runs, or whenever your local environment has drifted and you just want a clean slate without restarting the container.
To also re-run your init.d bootstrap scripts right after the reset (e.g. to recreate baseline buckets/queues/tables automatically):
curl -X POST "http://localhost:4566/_ministack/reset?init=1"Notes:
- A global reset lock serializes concurrent reset calls, so no service is left in a half-reset state.
- If
PERSIST_STATE=1is set, reset also deletes the JSON snapshots inSTATE_DIRand the object bytes inS3_DATA_DIR, so a subsequent restart won't reload the old state. - Lambda's warm container pool is drained as part of a reset too.
A small whitelist of settings can be flipped live via POST /_ministack/config, useful for switching behavior mid-session (e.g. Athena engine, Step Functions wait scaling, Lambda executor):
curl -X POST http://localhost:4566/_ministack/config \
-H 'Content-Type: application/json' \
-d '{"lambda_svc.LAMBDA_EXECUTOR":"docker"}'- PyPI install: stop with
Ctrl+Cand runministackagain. - Docker:
docker restart ministack
If PERSIST_STATE=1 (and S3_PERSIST for object bytes), your data survives the restart. Otherwise, a restart behaves like a full reset — everything is wiped.
When you want to guarantee a completely clean environment, including any mounted persisted state:
docker stop ministack
docker rm -v ministackIf you're using Docker Compose:
docker compose down -vThe -v flag also removes named volumes, so any persisted state directories tied to the compose stack are wiped too.
If you mounted a host directory for persistence manually (e.g. -v /tmp/ministack-data:/data), also clear it if you want a truly clean slate:
rm -rf /tmp/ministack-data/*Not a reset command, but handy for environment debugging — MiniStack captures SES emails in memory instead of sending them:
curl http://localhost:4566/_ministack/ses/messages| Goal | Command |
|---|---|
| Wipe all service state, keep container running | curl -X POST http://localhost:4566/_ministack/reset |
| Wipe state + re-run init scripts | curl -X POST "http://localhost:4566/_ministack/reset?init=1" |
| Change a runtime setting live | curl -X POST http://localhost:4566/_ministack/config -d '{...}' |
Restart process/container (persists if PERSIST_STATE=1) |
docker restart ministack |
| Full teardown, delete everything | docker rm -v ministack (or docker compose down -v) |
| Check liveness | curl http://localhost:4566/_ministack/health |
| Check readiness (init scripts done) | curl http://localhost:4566/_ministack/ready |
aws s3 mb s3://demo-bucket
aws s3 cp ./somefile.txt s3://demo-bucket/
aws s3 ls s3://demo-bucket
curl -X POST http://localhost:4566/_ministack/reset
aws s3 ls s3://demo-bucket # should now fail — the bucket is goneIf the last command returns a "bucket does not exist" error, the reset worked as expected.