We just shipped MiniStack v1.1.2. This is the biggest release since the initial launch — full Amazon Cognito support, partial EC2, EMR, a complete test suite overhaul, and a pile of infrastructure fixes that make running MiniStack day-to-day significantly cleaner.
If you're not familiar: MiniStack is a free, open-source local AWS emulator. One port, no account, no license key. A drop-in replacement for LocalStack — which moved its core services behind a paid plan.
What's new in v1.1.0
Amazon Cognito — full emulation
This was the most requested feature since launch. v1.1.0 ships complete Cognito support across both planes:
User Pools (cognito-idp)
- Full user lifecycle:
SignUp,ConfirmSignUp,AdminCreateUser,AdminDeleteUser,AdminGetUser,ListUsers - Auth flows:
USER_PASSWORD_AUTH,ADMIN_USER_PASSWORD_AUTH,REFRESH_TOKEN_AUTH,USER_SRP_AUTH(returnsPASSWORD_VERIFIERchallenge) -
FORCE_CHANGE_PASSWORDchallenge on first login - Self-service:
ForgotPassword,ConfirmForgotPassword,ChangePassword,GetUser,DeleteUser - Groups, domains, MFA config, tags — all covered
Identity Pools (cognito-identity)
-
CreateIdentityPool,GetId,GetCredentialsForIdentity,GetOpenIdToken -
SetIdentityPoolRoles,GetIdentityPoolRoles - Federated identity:
MergeDeveloperIdentities,UnlinkIdentity
OAuth2
-
POST /oauth2/token— client_credentials flow, returns stub Bearer token
Stub JWTs are structurally valid base64url tokens — they pass format checks in most SDKs without needing real crypto.
import boto3
idp = boto3.client("cognito-idp", endpoint_url="http://localhost:4566",
aws_access_key_id="test", aws_secret_access_key="test",
region_name="us-east-1")
# Create a pool
pool = idp.create_user_pool(PoolName="my-app")
pool_id = pool["UserPool"]["Id"]
# Sign up a user
idp.sign_up(
ClientId="local",
Username="alice@example.com",
Password="Password123!",
UserAttributes=[{"Name": "email", "Value": "alice@example.com"}],
)
# Confirm and authenticate
idp.admin_confirm_sign_up(UserPoolId=pool_id, Username="alice@example.com")
resp = idp.initiate_auth(
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={"USERNAME": "alice@example.com", "PASSWORD": "Password123!"},
ClientId="local",
)
print(resp["AuthenticationResult"]["AccessToken"])
644 tests — one file, all passing
We merged a separate QA test file into the main suite and fixed every test bug we found along the way:
-
test_s3_list_v1_marker_pagination—NextMarkeronly returned whenDelimiteris set (AWS spec) -
test_iam_inline_user_policy— boto3 deserialisesPolicyDocumentas a dict, not a string -
test_kinesis_at_timestamp_iterator— boto3 already returnsDataasbytes, no need to base64-decode -
test_rds_snapshot_crud/test_rds_deletion_protection— addedfinallycleanup so containers are deleted after each test
644 tests across all 25 services. Single file. All passing.
Docker volume leak — fixed
This one was subtle. Every RDS (CreateDBInstance) and ElastiCache (CreateCacheCluster) call spins up a real Docker container. The postgres and mysql images declare VOLUME /var/lib/postgresql/data — so Docker was creating an anonymous volume for every container, even after the container was removed.
After 20 test runs: 30+ dangling volumes, 600MB+ of wasted space.
Two fixes:
-
tmpfsoncontainers.run()— postgres/mysql data lives in container RAM. No volume created. -
container.remove(v=True)inreset()— volumes are removed with the container.
Before: 32 dangling volumes after 3 test runs
After: 2 volumes total (ministack + redis, always)
Clean up what you already have:
make purge
make purge — safe cleanup
purge: stop-compose
docker rm -f $(docker ps -aq --filter "label=ministack") 2>/dev/null || true
docker volume prune -f
rm -rf ./data/s3/*
Every container MiniStack spins up is labelled ministack=true. The purge target uses that label — it won't touch your other Redis, Postgres, or MySQL containers.
Bug fixes
-
Lambda
GetFunctionConcurrency— was returning 404 afterDeleteFunctionConcurrency. Now returns{}matching AWS behaviour -
ElastiCache
ModifyCacheParameterGroup— parameter key format was wrong (membervsParameterNameValue). Modified params were silently ignored -
RDS
ModifyDBInstance—DeletionProtection=FalsewithApplyImmediately=Truenow correctly applies immediately -
Cognito
GetCredentialsForIdentity— response field isSecretKey(correct boto3 wire name) -
Port conflict on
pip install—ministacknow prints a clear error if port 4566 is already in use instead of a raw uvicorn traceback
AWS CLI docs fix
A user reported credentials failing. The README was showing aws configure --profile local but then omitting --profile local from the example commands. Fixed — two working options now documented:
Option A — environment variables
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
aws --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket
Option B — named profile
aws configure --profile local
# Access Key: test / Secret: test / Region: us-east-1
aws --profile local --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket
Get it
# PyPI
pip install --upgrade ministack
# Docker
docker pull nahuelnucera/ministack:latest
docker compose up -d
GitHub: github.com/Nahuel990/ministack
25 AWS services. Free. MIT licensed. No account required.
What's next
-
SFN Activities (
CreateActivity,GetActivityTask) — already requested -
State persistence for Secrets Manager, SSM, DynamoDB —
PERSIST_STATE=1currently only covers API Gateway - ACM — last item on the original roadmap
Issues and PRs welcome.
Top comments (0)