DocsAWS 101BlogServices

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.

SurfaceWhat's missing
CloudWatch Alarm → Lambda / other targetsSNS alarm actions fire on state transition; Lambda and other action targets are stored but not invoked.
CloudWatch Metrics for LambdaInvocations, Errors, Duration and Throttles are emitted; ConcurrentExecutions is not.
CloudWatch Metrics for SQSApproximateNumberOfMessagesVisible, ApproximateAgeOfOldestMessage not tracked.
EventBridge → API Destination retriesAPI 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 PipesDynamoDB 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 logsAccessLogSettings stored; no log events written.
Step Functions loggingloggingConfiguration stored; not written to CloudWatch Logs. No ExecutionsStarted/Failed/Duration metrics.
CodeBuild logsMetadata-only builds (the default) write nothing; with MINISTACK_CODEBUILD_EXECUTE=1 build output does stream to CloudWatch Logs.
WAFv2 rule evaluationWebACLs, rules, IP sets all stored. Rules are not enforced against incoming requests.
AutoScaling policy triggersScaling policies + lifecycle hooks stored; never fired by CloudWatch alarms.
CloudFormation Stack PolicySetStackPolicy / GetStackPolicy store and return the policy; updates are never policy-gated.
Cognito Lambda triggersPreTokenGeneration, 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 verificationVerifyEmailIdentity / VerifyDomainIdentity jump straight to Success — no pending state, no confirmation email.
Route53 health checksChecks stored; status never updated; no CloudWatch bridge.
S3 SSE-KMSSSE headers validated, stored and echoed (SSE-C key checks enforced); no real cryptography — object bytes stored as sent.
CloudFormation AWS::DynamoDB::GlobalTable ReplicasAccepted 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 → EventBridgeSubmitTaskStateChange 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, RegisterImage can back an instance with a real container so it is reachable and ssm:SendCommand runs 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 (a mock_value row, or echoed SELECT literals). auto/duckdb gives 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_HOST points 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 use 123456789012 interchangeably).
  • 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} as iss, exactly as on AWS. To let an unmodified client (Spring, Vault, aws-jwt-verify) follow it to MiniStack, run with USE_SSL=1 and 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 trust ministack-ca.pem in your client's runtime; the OS keychain is not enough for these: Java keytool -importcert -file ministack-ca.pem -alias ministack -cacerts -storepass changeit, Node NODE_EXTRA_CA_CERTS=./ministack-ca.pem, Python REQUESTS_CA_BUNDLE / AWS_CA_BUNDLE pointing at a bundle of your system roots plus ministack-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/hosts line 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.