Configuration
All MiniStack configuration is driven by environment variables and a small admin HTTP surface. This page is the consolidated reference.
ministack/app.py, ministack/core/*, and per-service modules). Search os.environ / os.getenv in the repo to find the authoritative site where each is read.
Docker images (slim vs full)
MiniStack publishes two image variants. The default (slim) image is what ministackorg/ministack and :latest resolve to; pull the full image only when you need one of the extra engines it bundles.
| Slim (default) | Full | |
|---|---|---|
| Tags | ministackorg/ministack, :latest, :1.4.21 | ministackorg/ministack:full, :1.4.21-full |
| Base | python:3.13-alpine (musl libc) | python:3.13-slim (Debian, glibc) |
| Size | ~110 MB | ~360 MB |
edition on /_ministack/health | light | full |
The full image bundles the optional [full] dependencies (duckdb, cryptography, psycopg2-binary, pymysql, asyncssh, aws-sam-translator), which enable:
- Athena — real SQL execution via DuckDB.
- S3 Tables & Firehose Iceberg delivery — the DuckDB-backed Iceberg REST catalog engine.
- CloudFormation SAM transform —
Transform: AWS::Serverless-2016-10-31expansion viaaws-sam-translator. - IoT — certificate issuance and the in-process Local CA (
cryptography). - Databases — native PostgreSQL / MySQL driver access, e.g. the RDS Data API (
psycopg2/pymysql). - Transfer Family — the SFTP data plane (
asyncssh).
On the slim image, a call that needs one of these returns a clear error pointing you at the full image rather than failing silently. Everything else — S3, DynamoDB, SQS, SNS, Lambda, EC2, CloudFormation, and the rest — runs identically on both.
# slim (default) docker run -p 4566:4566 ministackorg/ministack # full — Athena, Iceberg, SAM, IoT certs, DB drivers, SFTP docker run -p 4566:4566 ministackorg/ministack:full
General
| Variable | Default | Purpose |
|---|---|---|
GATEWAY_PORT | 4566 | Port the single ASGI gateway listens on. Fallback order: GATEWAY_PORT → EDGE_PORT → 4566. |
EDGE_PORT | (fallback) | Legacy alias honored when GATEWAY_PORT is unset. |
MINISTACK_HOST | localhost | Hostname the server advertises (used in returned endpoints). |
MINISTACK_REGION | us-east-1 | Default AWS region for callers who don't sign requests with SigV4. |
LOG_LEVEL | INFO | DEBUG, INFO, WARNING, ERROR. DEBUG logs every request's routing decision. |
MINISTACK_WORKER_THREADS | 64 | Size of the thread pool executor used for sync-in-async offload and Lambda local runs. |
SERVICES | (all enabled) | Comma-separated filter, e.g. s3,sqs,lambda. Only those services answer — good for shrinking startup in tiny test matrices. |
TLS
The gateway can speak HTTPS natively — useful for AWS SDKs that hardcode https:// against Cognito Hosted UI / Amplify v6 endpoints, without a separate TLS-terminating proxy.
| Variable | Default | Purpose |
|---|---|---|
USE_SSL | 0 | When 1 (also accepts true / yes), the listener serves TLS instead of plaintext HTTP on the same port. Flag name aligns with LocalStack's USE_SSL for drop-in compose.yml swaps. |
MINISTACK_SSL_CERT | (auto-generated) | Path to a PEM-encoded certificate. If unset, MiniStack auto-generates a self-signed RSA cert (CN: ministack-local, SAN: localhost, ministack, 127.0.0.1, ::1) cached under ${TMPDIR}/ministack-tls/ so the cert survives restarts. Pin to an mkcert-issued cert for browser trust. |
MINISTACK_SSL_KEY | (auto-generated) | Path to the PEM-encoded private key matching MINISTACK_SSL_CERT. Auto-generation shells out to the openssl CLI (already present in both Docker images) — no Python crypto dependency added. |
Persistence
Three independent layers. Enable whatever subset fits your workflow.
| Variable | Default | Purpose |
|---|---|---|
PERSIST_STATE / LOCALSTACK_PERSISTENCE | 0 | Master switch. When 1, every multi-tenant service snapshots its in-memory maps to STATE_DIR on shutdown and restores on startup. |
STATE_DIR | /tmp/ministack-state | Directory for JSON snapshots. Mount to a host volume in Docker to survive container recreation. |
S3_PERSIST | 0 (auto-enabled if LOCALSTACK_PERSISTENCE=1) | Writes S3 object bytes (the body, not just metadata) to disk. |
S3_DATA_DIR | /tmp/ministack-data/s3 | Where S3 object files are stored. Mount this for persistent object storage. |
RDS_PERSIST | 0 | Switches RDS Docker containers from tmpfs to Docker named volumes. Real databases, real persistence. |
RDS_TMPFS_SIZE | 256m | Size of the tmpfs mount when RDS_PERSIST=0. Bump to 1g or 2g if tests hit "no space left on device". |
DSQL_PERSIST | 0 | Switches Aurora DSQL's backing Postgres containers from tmpfs to Docker named volumes (requires DSQL_STRICT=1). Real data, real persistence. |
Nested containers (RDS / EKS / ElastiCache / Lambda)
Services that spawn real sidecar containers share a common networking + registry-prefix surface.
| Variable | Default | Purpose |
|---|---|---|
DOCKER_NETWORK | (unset) | Attaches every container-backed service to the named Docker network. RDS/ElastiCache endpoints return the routable in-network IP instead of localhost. |
LAMBDA_DOCKER_NETWORK | (falls back to DOCKER_NETWORK) | Lambda-scoped override. Legacy — prefer DOCKER_NETWORK. |
MINISTACK_IMAGE_PREFIX | (unset) | Private-registry prefix prepended to every nested image (postgres, mysql, mariadb, redis, memcached, k3s, Lambda runtimes under public.ecr.aws/lambda/*). Idempotent on already-prefixed images. The Testcontainers Java module auto-forwards hub.image.name.prefix into this variable. |
RDS_BASE_PORT | 15432 | First host port allocated to an RDS container. Each subsequent DB gets the next free port. |
ELASTICACHE_BASE_PORT | 16379 | First host port for ElastiCache containers. |
ELASTICACHE_CLUSTER_MODE_REAL | 0 | When 1 (requires DOCKER_NETWORK), CreateReplicationGroup with NumNodeGroups=N / ReplicasPerNodeGroup=R provisions N × (1 + R) cluster-enabled Redis nodes wired with redis-cli --cluster create. Cluster-aware clients see real CLUSTER SLOTS / MOVED redirects. |
OPENSEARCH_DATAPLANE | 0 | When 1, CreateDomain spawns a real opensearchproject/opensearch container per domain (same pattern as ElastiCache and RDS). DescribeDomain.Endpoint then points at the container, and _cluster/health / _search work end to end. Default 0 returns a stub endpoint (<domain>.ministack.local:9200) for fast offline management-plane tests. |
OPENSEARCH_BASE_PORT | 14571 | First host port allocated when OPENSEARCH_DATAPLANE=1. Each subsequent CreateDomain gets the next free port. |
OPENSEARCH_IMAGE | opensearchproject/opensearch:2.15.0 | Image used when spawning per-domain OpenSearch containers. Override to pin a specific engine version. |
OPENSEARCH_DASHBOARDS | 0 | Set 1 together with OPENSEARCH_DATAPLANE=1 to also spawn a per-domain opensearchproject/opensearch-dashboards sidecar wired to the cluster. DescribeDomain.DomainStatus.DashboardEndpoint is then populated. |
OPENSEARCH_DASHBOARDS_BASE_PORT | 15601 | First host port allocated for per-domain Dashboards containers. |
OPENSEARCH_DASHBOARDS_IMAGE | opensearchproject/opensearch-dashboards:2.15.0 | Image used when spawning per-domain Dashboards containers. |
MINISTACK_OPENSEARCH_ENDPOINT | (unset) | If set (e.g. localhost:9200), every domain's DescribeDomain.Endpoint resolves to this value and ministack does not spawn per-domain containers. Useful when you bring your own cluster. |
DSQL_STRICT | 0 | When 1 (with a Docker daemon), each Aurora DSQL cluster is backed by a real postgres container fronted by an in-process wire-protocol proxy that enforces DSQL's SQL subset. Default 0 keeps clusters ACTIVE metadata-only. |
DSQL_BASE_PORT | 25432 | First host port allocated to a DSQL backend container when DSQL_STRICT=1. A fixed 30-port window off this base caps concurrent backends (one per cluster). |
DSQL_PG_IMAGE | postgres:16-alpine | Image used for the per-cluster Postgres backend. Override to pin a specific Postgres version. |
EKS_BASE_PORT | 16443 | First host port for EKS k3s clusters. |
EKS_K3S_IMAGE | rancher/k3s:v1.31.4-k3s1 | k3s image tag for EKS cluster sidecars. Pin this if you need a specific Kubernetes version. |
ECS_REAP_INTERVAL_SECONDS | 60 | Interval for the ECS task reaper. Cleans up stopped task containers. |
Lambda
| Variable | Default | Purpose |
|---|---|---|
LAMBDA_EXECUTOR | local | local runs Python as a subprocess (fast, no Docker). docker runs each invocation in a container, reusing a warm-container pool. Image-based and provided.* runtimes always use Docker regardless. |
LAMBDA_STRICT | 0 | 1 forces AWS-fidelity mode — in-process fallback is disabled and missing Docker surfaces as Runtime.DockerUnavailable. |
LAMBDA_DOCKER_FLAGS | (unset) | Extra docker run flags. Whitelisted: -e, -v, --dns, --network, --cap-add, -m, --shm-size, --tmpfs, --add-host, --privileged, --read-only. |
LAMBDA_REMOTE_DOCKER_VOLUME_MOUNT | (unset) | Path remapping for remote Docker daemons (when the daemon can't see the host's /tmp). |
LAMBDA_WARM_TTL_SECONDS | 300 (5 min) | Idle container TTL in the warm pool. Raise for long local-dev sessions, lower for CI. |
LAMBDA_KEEPALIVE_MS | (unset) | LocalStack-compat lever (not an AWS behavior). 0 forces a per-invocation cold start for Docker RIE runtimes (Ruby / Java / .NET): the warm container is torn down after each invocation so the next invoke re-runs INIT. Unset or any non-zero value keeps the warm-pool behavior. |
LAMBDA_ACCOUNT_CONCURRENCY | 0 | Account-level concurrent-invocation cap. 0 = unbounded; set 1000 to simulate AWS's default. |
LAMBDA_STATE_TRANSITION_SECONDS | 0.5 | Artificial delay between Pending→Active and Active→Inactive. Matches AWS's eventual-consistency window for tests that assert on state. |
LAMBDA_ASYNC_RETRY_BASE_SECONDS | 1 | Base backoff for async invoke retries. |
LAMBDA_ASYNC_RETRY_MAX_SECONDS | 30 | Cap on async retry backoff. |
_LAMBDA_LAYERS_DIRS | (unset) | Colon-separated directories scanned for layers. Useful for local-dev layer prototyping. |
Databases & analytics
| Variable | Default | Purpose |
|---|---|---|
ATHENA_ENGINE | auto | auto uses DuckDB when installed and falls back to mock. duckdb requires DuckDB (errors if missing). mock always returns empty result sets. |
ATHENA_DATA_DIR | (derived from S3_DATA_DIR) | DuckDB working directory for Athena query staging. |
SFN_MOCK_CONFIG | (unset) | Path to a JSON mock file (AWS SFN Local format) for Step Functions Task states. |
Other services
| Variable | Default | Purpose |
|---|---|---|
SMTP_HOST | (unset) | If set (e.g. mailhog:1025), SES SendEmail/SendRawEmail forwards to a real SMTP server. Otherwise mails are stored in memory and inspectable at /_ministack/ses/messages. |
REDIS_HOST / REDIS_PORT | redis / 6379 | External Redis (if you don't want ElastiCache to spin up a container per cluster). |
MINISTACK_APIGW_PROXY_TIMEOUT_SECONDS | 30 | Timeout for API Gateway HTTP / HTTP_PROXY integration calls to the upstream backend. Bumped per-deployment when an upstream is intentionally slow. |
MINISTACK_APIGW_JWKS_TIMEOUT_SECONDS | 5 | Timeout for JWT-authorizer JWKS fetches (HTTP API + REST API). Both proxy and JWKS fetches run off the event loop so a slow upstream cannot stall unrelated requests. |
MINISTACK_IMDS_V2_REQUIRED | 0 | When 1, the IMDS service rejects token-less GET requests on /latest/meta-data/.... Callers must PUT /latest/api/token first and pass the value as X-aws-ec2-metadata-token, matching real-AWS hop-limit-1 IMDSv2-only instances. Point SDK callers via AWS_EC2_METADATA_SERVICE_ENDPOINT=http://localhost:4566. |
MINISTACK_COGNITO_PRETOKEN_STRICT | 0 | When 1, a failing PreTokenGeneration Lambda fails the auth call the way real AWS does. Default is fail-open: a Lambda error is logged and the unmodified token is still issued, which keeps local-dev auth flows working when an unrelated Lambda is broken. |
MINISTACK_CODEBUILD_EXECUTE | 0 | When 1, StartBuild runs the project's inline buildspec through the official AWS CodeBuild local agent in Docker (phases run, output streams to CloudWatch Logs) instead of returning a metadata-only SUCCEEDED record. Requires the Docker socket mounted into the container. |
IOT_MTLS_ENABLED / IOT_MTLS_PORT | (on) / 8883 | The IoT broker also accepts native MQTT over TLS on IOT_MTLS_PORT (default 8883), on by default when the cryptography package is installed. Set IOT_MTLS_ENABLED=0 to disable. The broker certificate is served at GET /_ministack/iot/ca.pem for devices to trust. |
Admin endpoints
All admin endpoints live under /_ministack/*. Most are also exposed under /_localstack/* for LocalStack compatibility.
| Endpoint | Method | Purpose |
|---|---|---|
/_ministack/health | GET | Liveness — returns 200 with a JSON map of service availability and init/ready script status. |
/_ministack/ready | GET | Readiness — 200 when ready.d scripts have finished, 503 while still running. |
/_ministack/reset | POST | Wipes all in-memory service state. Add ?init=1 to re-run init.d scripts. Acquires a global reset lock so concurrent requests serialize. |
/_ministack/config | POST | Runtime config for an exact-match whitelist of keys (e.g. athena.ATHENA_ENGINE, stepfunctions._SFN_WAIT_SCALE, lambda_svc.LAMBDA_EXECUTOR, cloudtrail._recording_enabled). Flat JSON body mapping each key to its value. |
/_ministack/ses/messages | GET | Inspect sent emails. ?account=123456789012 filters by account. |
/_ministack/lambda-code/{function_name} | GET | Downloads the function's deployment ZIP. |
/_ministack/lambda-layers/{layer_name}/{version}/content | GET | Downloads a layer version ZIP. |
Init & ready scripts
On startup MiniStack runs executable files from (in order):
/docker-entrypoint-initaws.d/(LocalStack-compat) or/etc/localstack/init/boot.d/— ran synchronously before the server accepts traffic./docker-entrypoint-initaws.d/ready.d/or/etc/localstack/init/ready.d/— ran asynchronously after startup;/_ministack/readyflips to 200 once these finish.
Scripts see the usual AWS env (AWS_ACCESS_KEY_ID=test, AWS_SECRET_ACCESS_KEY=test, AWS_DEFAULT_REGION=us-east-1, AWS_ENDPOINT_URL=http://localhost:4566) pre-populated.
Runtime config
POST /_ministack/config mutates a limited set of keys without restarting. Example:
curl -X POST http://localhost:4566/_ministack/config \
-H 'Content-Type: application/json' \
-d '{"athena.ATHENA_ENGINE":"duckdb"}'
The whitelist is exact-match (not prefixes). Current keys: athena.ATHENA_ENGINE, athena.ATHENA_DATA_DIR, stepfunctions._sfn_mock_config, stepfunctions._SFN_WAIT_SCALE, lambda_svc.LAMBDA_EXECUTOR, cloudtrail._recording_enabled. Tests use this to flip a single knob without tearing down the container.
grep -r "os.environ" ministack/ gives the definitive list.