Known limitations
MiniStack aims for AWS compatibility, but some integrations and infrastructure concepts don't translate to a single local process. This page is the honest, cross-service gap list.
If a gap blocks you, open an issue. We actively closes parity gaps — several items on this list are first-class work items, not permanent compromises. Dated against MiniStack 1.5.24.
Stored but not dispatched
These integrations accept configuration and return correct shapes, but the side-effect is not performed. Tests that assert on the stored config pass; tests that assert on downstream side-effects will not.
| Surface | What's missing |
|---|---|
| CloudWatch Alarm → Lambda / other targets | SNS alarm actions fire on state transition; Lambda and other action targets are stored but not invoked. |
| CloudWatch Metrics for Lambda | Invocations, Errors, Duration and Throttles are emitted; ConcurrentExecutions is not. |
| CloudWatch Metrics for SQS | ApproximateNumberOfMessagesVisible, ApproximateAgeOfOldestMessage not tracked. |
| EventBridge → API Destination retries | API destinations make real outbound HTTP calls (with OAuth refresh); AWS's 24-hour/185-attempt retry pipeline and DLQ are not modeled — a failed delivery is logged and dropped. |
| EventBridge Pipes | DynamoDB Streams → SNS and Step Functions pipes deliver (background poller); other source/target combinations are stored but not piped. |
| ECS → CloudWatch Logs (awslogs driver) | Log driver config parsed; stdout/stderr not written to log groups. |
| API Gateway access logs | AccessLogSettings stored; no log events written. |
| Step Functions logging | loggingConfiguration stored; not written to CloudWatch Logs. No ExecutionsStarted/Failed/Duration metrics. |
| CodeBuild logs | Metadata-only builds (the default) write nothing; with MINISTACK_CODEBUILD_EXECUTE=1 build output does stream to CloudWatch Logs. |
| WAFv2 rule evaluation | WebACLs, rules, IP sets all stored. Rules are not enforced against incoming requests. |
| AutoScaling policy triggers | Scaling policies + lifecycle hooks stored; never fired by CloudWatch alarms. |
| CloudFormation Stack Policy | SetStackPolicy / GetStackPolicy store and return the policy; updates are never policy-gated. |
| Cognito Lambda triggers | PreTokenGeneration, the federated PreSignUp, the CUSTOM_AUTH triggers and CustomMessage_AdminCreateUser are invoked; PostConfirmation, Pre/PostAuthentication and the other CustomMessage sources are stored but not invoked. |
| SES identity verification | VerifyEmailIdentity / VerifyDomainIdentity jump straight to Success — no pending state, no confirmation email. |
| Route53 health checks | Checks stored; status never updated; no CloudWatch bridge. |
| S3 SSE-KMS | SSE headers validated, stored and echoed (SSE-C key checks enforced); no real cryptography — object bytes stored as sent. |
CloudFormation AWS::DynamoDB::GlobalTable Replicas | Accepted and ignored by the stack. Replicas do work through UpdateTable ReplicaUpdates (Terraform / OpenTofu replica blocks) since 1.5.20, alongside S3 replication (1.5.3) and KMS multi-Region keys (1.5.5). |
| ECS task state → EventBridge | SubmitTaskStateChange exists; event is not put on the default bus. |
Metadata-only services
These services accept and return realistic shapes so IaC tools plan and apply, but no real infrastructure is created:
- EC2 — instances, VPCs, subnets, and security groups exist as data by default (no ENI networking). Since 1.5.0,
RegisterImagecan back an instance with a real container so it is reachable andssm:SendCommandruns on it (opt-in). - CloudFront — a distribution serves viewer requests from its S3 or custom origins at
<label>.cloudfront.net; no caching, WAF, logging, geo restrictions, custom error pages, signed URLs or cookies, or Lambda@Edge. - Transfer Family — a real SFTP listener is bound (default port 2222, requires asyncssh); FTPS/FTP are not implemented.
- EFS — file systems, mount targets, access points stored; no POSIX filesystem or NFS mount.
- AppSync — GraphQL queries and mutations execute against DynamoDB and Lambda data sources (best-effort parser; VTL mapping templates are stored, not interpreted).
- EMR — cluster and step metadata tracked; no Spark/Hadoop execution. Glue is different: python-shell jobs run as a subprocess and Spark (glueetl) jobs run in Docker when available.
- ACM — certificates auto-ISSUED; no DNS/HTTP validation occurs.
- Athena without DuckDB — with
ATHENA_ENGINE=mock, results are synthetic (amock_valuerow, or echoed SELECT literals).auto/duckdbgives real SQL on S3 data. - Firehose non-S3 destinations — HTTP, Redshift, OpenSearch, Splunk, Snowflake all stored; no delivery performed. (S3/ExtendedS3 and Iceberg do deliver.)
Impossible locally
These are structural — a single-process emulator cannot simulate them.
- Real VPC networking. Subnets, route tables, and NAT all exist as metadata; packet routing is not simulated.
- Real DNS propagation. Route53 changes are visible inside MiniStack only; they don't affect your host's resolver.
- SMTP delivery without a real MTA. Unless
SMTP_HOSTpoints at one (e.g. MailHog), SES emails stay in memory. - Real Kubernetes control plane. EKS runs k3s as a sidecar — powerful, but not byte-identical to EKS.
By-design differences
- No SigV4 signature validation. Any access key/secret works.
- Default account
000000000000(some paths use123456789012interchangeably). - State shared across regions — only for S3 and Aurora DSQL; every other regional service is region-isolated as of 1.5.4 (use unique names for S3/DSQL when exercising two regions). IAM, STS, CloudFront, Route 53, and Organizations are global services in AWS, so account-scoped shared state is correct for them, not a gap.
- Lenient validation. Required fields are checked; optional fields are accepted more permissively than AWS.
- Cognito tokens carry the AWS issuer. A pool's tokens use
https://cognito-idp.{region}.amazonaws.com/{poolId}asiss, exactly as on AWS. To let an unmodified client (Spring, Vault,aws-jwt-verify) follow it to MiniStack, run withUSE_SSL=1and point the issuer host at MiniStack:docker run -d --name ministack -p 443:4566 -p 4566:4566 -e USE_SSL=1 ministackorg/ministack docker cp ministack:/tmp/ministack-tls/server.crt ./ministack-ca.pem # /etc/hosts 127.0.0.1 cognito-idp.us-east-1.amazonaws.com
Then trustministack-ca.pemin your client's runtime; the OS keychain is not enough for these: Javakeytool -importcert -file ministack-ca.pem -alias ministack -cacerts -storepass changeit, NodeNODE_EXTRA_CA_CERTS=./ministack-ca.pem, PythonREQUESTS_CA_BUNDLE/AWS_CA_BUNDLEpointing at a bundle of your system roots plusministack-ca.pem. The certificate covers every region's issuer host, and Lambda containers MiniStack starts get the hosts entries and trust automatically. Remove the/etc/hostsline before calling the real Cognito.
Per-service detail: each entry on the Services index page links to a service page with its own "Known limitations" section, calling out the gaps that matter for that specific API surface.