Tools: Report: MiniStack v1.1.2 — Cognito, EC2, EMR, 656 Tests, and Zero Docker Leaks

Tools: Report: MiniStack v1.1.2 — Cognito, EC2, EMR, 656 Tests, and Zero Docker Leaks

What's new in v1.1.0

Amazon Cognito — full emulation

644 tests — one file, all passing

Docker volume leak — fixed

make purge — safe cleanup

Bug fixes

AWS CLI docs fix

Get it

What's next 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. This was the most requested feature since launch. v1.1.0 ships complete Cognito support across both planes: User Pools (cognito-idp) Identity Pools (cognito-identity) Stub JWTs are structurally valid base64url tokens — they pass format checks in most SDKs without needing real crypto. We merged a separate QA test file into the main suite and fixed every test bug we found along the way: 644 tests across all 25 services. Single file. All passing. 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. Clean up what you already have: 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. 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 Option B — named profile GitHub: github.com/Nahuel990/ministack 25 AWS services. Free. MIT licensed. No account required. Issues and PRs welcome. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ 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="[email protected]", Password="Password123!", UserAttributes=[{"Name": "email", "Value": "[email protected]"}], ) # Confirm and authenticate idp.admin_confirm_sign_up(UserPoolId=pool_id, Username="[email protected]") resp = idp.initiate_auth( AuthFlow="USER_PASSWORD_AUTH", AuthParameters={"USERNAME": "[email protected]", "PASSWORD": "Password123!"}, ClientId="local", ) print(resp["AuthenticationResult"]["AccessToken"]) 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="[email protected]", Password="Password123!", UserAttributes=[{"Name": "email", "Value": "[email protected]"}], ) # Confirm and authenticate idp.admin_confirm_sign_up(UserPoolId=pool_id, Username="[email protected]") resp = idp.initiate_auth( AuthFlow="USER_PASSWORD_AUTH", AuthParameters={"USERNAME": "[email protected]", "PASSWORD": "Password123!"}, ClientId="local", ) print(resp["AuthenticationResult"]["AccessToken"]) 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="[email protected]", Password="Password123!", UserAttributes=[{"Name": "email", "Value": "[email protected]"}], ) # Confirm and authenticate idp.admin_confirm_sign_up(UserPoolId=pool_id, Username="[email protected]") resp = idp.initiate_auth( AuthFlow="USER_PASSWORD_AUTH", AuthParameters={"USERNAME": "[email protected]", "PASSWORD": "Password123!"}, ClientId="local", ) print(resp["AuthenticationResult"]["AccessToken"]) Before: 32 dangling volumes after 3 test runs After: 2 volumes total (ministack + redis, always) Before: 32 dangling volumes after 3 test runs After: 2 volumes total (ministack + redis, always) Before: 32 dangling volumes after 3 test runs After: 2 volumes total (ministack + redis, always) purge: -weight: 500;">stop-compose -weight: 500;">docker rm -f $(-weight: 500;">docker ps -aq --filter "label=ministack") 2>/dev/null || true -weight: 500;">docker volume prune -f rm -rf ./data/s3/* purge: -weight: 500;">stop-compose -weight: 500;">docker rm -f $(-weight: 500;">docker ps -aq --filter "label=ministack") 2>/dev/null || true -weight: 500;">docker volume prune -f rm -rf ./data/s3/* purge: -weight: 500;">stop-compose -weight: 500;">docker rm -f $(-weight: 500;">docker ps -aq --filter "label=ministack") 2>/dev/null || true -weight: 500;">docker volume prune -f rm -rf ./data/s3/* 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 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 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 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 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 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 # PyPI -weight: 500;">pip -weight: 500;">install ---weight: 500;">upgrade ministack # Docker -weight: 500;">docker pull nahuelnucera/ministack:latest -weight: 500;">docker compose up -d # PyPI -weight: 500;">pip -weight: 500;">install ---weight: 500;">upgrade ministack # Docker -weight: 500;">docker pull nahuelnucera/ministack:latest -weight: 500;">docker compose up -d # PyPI -weight: 500;">pip -weight: 500;">install ---weight: 500;">upgrade ministack # Docker -weight: 500;">docker pull nahuelnucera/ministack:latest -weight: 500;">docker compose up -d - Full user lifecycle: SignUp, ConfirmSignUp, AdminCreateUser, AdminDeleteUser, AdminGetUser, ListUsers - Auth flows: USER_PASSWORD_AUTH, ADMIN_USER_PASSWORD_AUTH, REFRESH_TOKEN_AUTH, USER_SRP_AUTH (returns PASSWORD_VERIFIER challenge) - FORCE_CHANGE_PASSWORD challenge on first login - Self--weight: 500;">service: ForgotPassword, ConfirmForgotPassword, ChangePassword, GetUser, DeleteUser - Groups, domains, MFA config, tags — all covered - CreateIdentityPool, GetId, GetCredentialsForIdentity, GetOpenIdToken - SetIdentityPoolRoles, GetIdentityPoolRoles - Federated identity: MergeDeveloperIdentities, UnlinkIdentity - POST /oauth2/token — client_credentials flow, returns stub Bearer token - test_s3_list_v1_marker_pagination — NextMarker only returned when Delimiter is set (AWS spec) - test_iam_inline_user_policy — boto3 deserialises PolicyDocument as a dict, not a string - test_kinesis_at_timestamp_iterator — boto3 already returns Data as bytes, no need to base64-decode - test_rds_snapshot_crud / test_rds_deletion_protection — added finally cleanup so containers are deleted after each test - tmpfs on containers.run() — postgres/mysql data lives in container RAM. No volume created. - container.-weight: 500;">remove(v=True) in reset() — volumes are removed with the container. - Lambda GetFunctionConcurrency — was returning 404 after DeleteFunctionConcurrency. Now returns {} matching AWS behaviour - ElastiCache ModifyCacheParameterGroup — parameter key format was wrong (member vs ParameterNameValue). Modified params were silently ignored - RDS ModifyDBInstance — DeletionProtection=False with ApplyImmediately=True now correctly applies immediately - Cognito GetCredentialsForIdentity — response field is SecretKey (correct boto3 wire name) - Port conflict on -weight: 500;">pip -weight: 500;">install — ministack now prints a clear error if port 4566 is already in use instead of a raw uvicorn traceback - SFN Activities (CreateActivity, GetActivityTask) — already requested - State persistence for Secrets Manager, SSM, DynamoDB — PERSIST_STATE=1 currently only covers API Gateway - ACM — last item on the original roadmap