<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nahuel Nucera</title>
    <description>The latest articles on Forem by Nahuel Nucera (@nahuel990).</description>
    <link>https://forem.com/nahuel990</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3842305%2Ffafe7064-02bd-47d1-8838-51df2da5ddd3.png</url>
      <title>Forem: Nahuel Nucera</title>
      <link>https://forem.com/nahuel990</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nahuel990"/>
    <language>en</language>
    <item>
      <title>What landed in MiniStack between 1.2.0 and 1.3.30</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Thu, 07 May 2026 16:10:04 +0000</pubDate>
      <link>https://forem.com/nahuel990/what-landed-in-ministack-between-120-and-1330-3c74</link>
      <guid>https://forem.com/nahuel990/what-landed-in-ministack-between-120-and-1330-3c74</guid>
      <description>&lt;h1&gt;
  
  
  MiniStack 1.3.30 is out — here's what shipped recently
&lt;/h1&gt;

&lt;p&gt;MiniStack is a free, MIT-licensed local AWS emulator. Single binary, 55+ services, runs on your laptop or in CI in under a second. No login, no token, no per-seat fee.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;https://github.com/ministackorg/ministack&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://ministack.org/docs/" rel="noopener noreferrer"&gt;https://ministack.org/docs/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Highlights since 1.3.20
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step Functions &lt;code&gt;aws-sdk:s3&lt;/code&gt; integrations.&lt;/strong&gt; &lt;code&gt;arn:aws:states:::aws-sdk:s3:listObjectsV2&lt;/code&gt; and 11 other S3 operations now work as native Step Functions SDK integrations: &lt;code&gt;ListBuckets&lt;/code&gt;, &lt;code&gt;CreateBucket&lt;/code&gt;, &lt;code&gt;DeleteBucket&lt;/code&gt;, &lt;code&gt;HeadBucket&lt;/code&gt;, &lt;code&gt;GetBucketVersioning&lt;/code&gt;, &lt;code&gt;ListObjectsV2&lt;/code&gt;, &lt;code&gt;ListObjects&lt;/code&gt;, &lt;code&gt;HeadObject&lt;/code&gt;, &lt;code&gt;CopyObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt;, &lt;code&gt;GetObjectTagging&lt;/code&gt;, &lt;code&gt;PutObjectTagging&lt;/code&gt;. REST-JSON output also moved to PascalCase to match the rest of the dispatcher, so &lt;code&gt;ResultSelector: $.Records&lt;/code&gt; resolves cleanly on &lt;code&gt;aws-sdk:rdsdata:executeStatement&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EC2 &lt;code&gt;DescribeVpcEndpointServices&lt;/code&gt;.&lt;/strong&gt; Returns the standard catalog of 2 Gateway services (&lt;code&gt;s3&lt;/code&gt;, &lt;code&gt;dynamodb&lt;/code&gt;) and 17 Interface PrivateLink services with region-templated DNS names. &lt;code&gt;ServiceNames&lt;/code&gt;, &lt;code&gt;service-name&lt;/code&gt;, and &lt;code&gt;service-type&lt;/code&gt; filters supported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB legacy parameters.&lt;/strong&gt; &lt;code&gt;Expected&lt;/code&gt;, &lt;code&gt;KeyConditions&lt;/code&gt;, &lt;code&gt;ScanFilter&lt;/code&gt;, &lt;code&gt;QueryFilter&lt;/code&gt;, and &lt;code&gt;AttributeUpdates&lt;/code&gt; all work now. The .NET AWS SDK was silently dropping non-key fields on every &lt;code&gt;UpdateItem&lt;/code&gt; because it speaks the legacy attribute API under the hood. Fixed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECS Task Metadata V4.&lt;/strong&gt; Every container started by &lt;code&gt;RunTask&lt;/code&gt; now gets &lt;code&gt;ECS_CONTAINER_METADATA_URI_V4&lt;/code&gt; injected. The gateway serves &lt;code&gt;/v4/&amp;lt;token&amp;gt;&lt;/code&gt;, &lt;code&gt;/v4/&amp;lt;token&amp;gt;/task&lt;/code&gt;, and the &lt;code&gt;/stats&lt;/code&gt; endpoints. &lt;code&gt;RunTask&lt;/code&gt; also translates &lt;code&gt;privileged&lt;/code&gt;, &lt;code&gt;linuxParameters.capabilities.add&lt;/code&gt;, &lt;code&gt;pidMode: host&lt;/code&gt;, and &lt;code&gt;volumes&lt;/code&gt; + &lt;code&gt;mountPoints&lt;/code&gt; into Docker bind mounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CloudTrail.&lt;/strong&gt; New service. In-memory audit log, opt in via &lt;code&gt;CLOUDTRAIL_RECORDING=1&lt;/code&gt;. Per-account ring buffer with &lt;code&gt;LookupEvents&lt;/code&gt; covering all eight &lt;code&gt;LookupAttributes&lt;/code&gt;. Full control plane: &lt;code&gt;CreateTrail&lt;/code&gt;, &lt;code&gt;DeleteTrail&lt;/code&gt;, &lt;code&gt;Get/DescribeTrails&lt;/code&gt;, &lt;code&gt;StartLogging&lt;/code&gt;/&lt;code&gt;StopLogging&lt;/code&gt; with real &lt;code&gt;IsLogging&lt;/code&gt; state, &lt;code&gt;Put/GetEventSelectors&lt;/code&gt;, tag ops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Resource Groups.&lt;/strong&gt; New service, 19 of 23 spec operations. Group CRUD, resource queries, configuration, membership, tagging, account settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CloudFormation &lt;code&gt;AWS::CloudFront::KeyValueStore&lt;/code&gt;.&lt;/strong&gt; Create / Update / Delete with &lt;code&gt;Arn&lt;/code&gt;, &lt;code&gt;Id&lt;/code&gt;, &lt;code&gt;Status&lt;/code&gt; exposed via &lt;code&gt;Fn::GetAtt&lt;/code&gt;. The CFN engine now routes previously-provisioned resources through a per-type &lt;code&gt;update&lt;/code&gt; handler when one is defined.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenSearch non-VPC domains.&lt;/strong&gt; &lt;code&gt;VPCOptions&lt;/code&gt; is now omitted on non-VPC domains instead of returning an empty object, which was causing the Terraform AWS provider to misclassify them as VPC-backed and fail with &lt;code&gt;OpenSearch Domain in VPC expected to have null Endpoint value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Gateway v1 fixes.&lt;/strong&gt; &lt;code&gt;GetUsagePlanKey&lt;/code&gt; handler added (Terraform's &lt;code&gt;aws_api_gateway_usage_plan_key&lt;/code&gt; apply was aborting without it). &lt;code&gt;HTTP_PROXY&lt;/code&gt; integrations now do path-parameter substitution from &lt;code&gt;integration.request.path.X = method.request.path.X&lt;/code&gt; mappings and forward the query string. &lt;code&gt;UpdateModel&lt;/code&gt; handler added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step Functions REST-JSON casing.&lt;/strong&gt; Successful REST-JSON integrations like &lt;code&gt;aws-sdk:rdsdata:executeStatement&lt;/code&gt; now expose output keys in PascalCase (&lt;code&gt;Records&lt;/code&gt;, &lt;code&gt;NumberOfRecordsUpdated&lt;/code&gt;) instead of raw camelCase, matching query and REST-XML dispatchers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQS &lt;code&gt;ReceiveMessage&lt;/code&gt; honours &lt;code&gt;MessageSystemAttributeNames&lt;/code&gt;.&lt;/strong&gt; Only the deprecated &lt;code&gt;AttributeNames&lt;/code&gt; was being read, so AWS SDK v2 (Java, Kotlin) consumers were getting empty &lt;code&gt;Attributes&lt;/code&gt; and broken &lt;code&gt;ApproximateReceiveCount&lt;/code&gt;-based redelivery detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SFN &lt;code&gt;aws-sdk:ec2&lt;/code&gt; security group compatibility.&lt;/strong&gt; &lt;code&gt;CreateSecurityGroup&lt;/code&gt; now maps SDK &lt;code&gt;Description&lt;/code&gt; to wire &lt;code&gt;GroupDescription&lt;/code&gt;, &lt;code&gt;DescribeSecurityGroups&lt;/code&gt; sends EC2-shaped filters (&lt;code&gt;Filter.1.Value.1&lt;/code&gt; instead of &lt;code&gt;member.N&lt;/code&gt;), and the XML adapter returns &lt;code&gt;SecurityGroups&lt;/code&gt; rather than raw &lt;code&gt;SecurityGroupInfo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CFN &lt;code&gt;AWS::SNS::Subscription&lt;/code&gt; honours &lt;code&gt;RawMessageDelivery&lt;/code&gt;.&lt;/strong&gt; The provisioner was silently defaulting to &lt;code&gt;false&lt;/code&gt; even when templates set &lt;code&gt;true&lt;/code&gt;, so consumers got SNS-wrapped envelopes instead of raw payloads.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wire-format parity (what makes the project worth using)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All JSON timestamps are int epoch seconds. Java SDK v2 rejects floats. STS &lt;code&gt;Credentials.Expiration&lt;/code&gt;, EventBridge &lt;code&gt;Time&lt;/code&gt;, EKS &lt;code&gt;createdAt&lt;/code&gt;, CloudTrail &lt;code&gt;EventTime&lt;/code&gt;, Backup, Firehose — all converted.&lt;/li&gt;
&lt;li&gt;KMS &lt;code&gt;Verify&lt;/code&gt; raises &lt;code&gt;KMSInvalidSignatureException&lt;/code&gt; on bad signatures instead of returning &lt;code&gt;SignatureValid: false&lt;/code&gt; with HTTP 200.&lt;/li&gt;
&lt;li&gt;KMS crypto operations return the full key ARN as &lt;code&gt;KeyId&lt;/code&gt;, not the bare UUID.&lt;/li&gt;
&lt;li&gt;DynamoDB &lt;code&gt;ConditionalCheckFailedException&lt;/code&gt; populates &lt;code&gt;Item&lt;/code&gt; when &lt;code&gt;ReturnValuesOnConditionCheckFailure="ALL_OLD"&lt;/code&gt; is set, on &lt;code&gt;Put&lt;/code&gt;/&lt;code&gt;Update&lt;/code&gt;/&lt;code&gt;Delete&lt;/code&gt;/&lt;code&gt;TransactWriteItems&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;EC2 VPCs include &lt;code&gt;cidrBlockAssociationSet&lt;/code&gt; (Terraform AWS provider v6 crashes without it).&lt;/li&gt;
&lt;li&gt;SQS FIFO &lt;code&gt;DeduplicationScope: messageGroup&lt;/code&gt; actually scopes per message group.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 ministackorg/ministack:1.3.30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;ministack
ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point AWS CLI / boto3 / Terraform at &lt;code&gt;http://localhost:4566&lt;/code&gt; with any non-empty access key.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;https://github.com/ministackorg/ministack&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://ministack.org/docs/" rel="noopener noreferrer"&gt;https://ministack.org/docs/&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/company/ministackorg/" rel="noopener noreferrer"&gt;https://www.linkedin.com/company/ministackorg/&lt;/a&gt;&lt;br&gt;
Reddit: &lt;a href="https://www.reddit.com/r/ministack/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/ministack/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If something is missing or wrong, open an issue.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>aws</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>What landed in MiniStack between 1.2.0 and 1.3.30</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Thu, 07 May 2026 07:27:11 +0000</pubDate>
      <link>https://forem.com/nahuel990/what-landed-in-ministack-between-120-and-1330-3k20</link>
      <guid>https://forem.com/nahuel990/what-landed-in-ministack-between-120-and-1330-3k20</guid>
      <description>&lt;p&gt;If you read the 1.2.0 post, that is where this picks up. About four weeks have gone by and over fifty patch releases. This is a tour of what actually shipped, focused on the parts you can use today.&lt;/p&gt;

&lt;p&gt;If you have not seen MiniStack before: it is a single-binary local AWS emulator. One process, 46 services, MIT licensed, runs on your laptop or in CI in under a second. Think LocalStack without the paywall and without the JVM.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;https://github.com/ministackorg/ministack&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://ministack.org/docs/" rel="noopener noreferrer"&gt;https://ministack.org/docs/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  New services
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;EKS with real k3s&lt;/strong&gt; (1.2.20). &lt;code&gt;CreateCluster&lt;/code&gt; spawns a 75 MB k3s container that gives you a real Kubernetes API. &lt;code&gt;kubectl&lt;/code&gt; and Helm work against it. CloudFormation provisioners for &lt;code&gt;AWS::EKS::Cluster&lt;/code&gt; and &lt;code&gt;AWS::EKS::Nodegroup&lt;/code&gt; are wired in. If Docker is unavailable the cluster still goes ACTIVE with a stub endpoint so the API contract holds in CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transfer Family&lt;/strong&gt; (1.2.17). 10 operations covering server CRUD, user CRUD, SSH key rotation, and LOGICAL home directory mappings to S3. Contributed by @mjdavidson.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EventBridge Scheduler&lt;/strong&gt; (1.2.19). The full &lt;code&gt;scheduler&lt;/code&gt; API: schedule groups, cascading deletes, name and state filters, plus &lt;code&gt;at()&lt;/code&gt;, &lt;code&gt;cron()&lt;/code&gt;, and &lt;code&gt;rate()&lt;/code&gt; expressions. CFN &lt;code&gt;AWS::Scheduler::Schedule&lt;/code&gt; and &lt;code&gt;AWS::Scheduler::ScheduleGroup&lt;/code&gt; provisioners included. EventBridge &lt;code&gt;cron()&lt;/code&gt; rules also fire automatically now (1.3.25), with a zero-dependency parser that covers the full six-field syntax including &lt;code&gt;L&lt;/code&gt;, &lt;code&gt;LW&lt;/code&gt;, &lt;code&gt;&amp;lt;n&amp;gt;W&lt;/code&gt;, &lt;code&gt;&amp;lt;n&amp;gt;#&amp;lt;k&amp;gt;&lt;/code&gt;. Contributed by &lt;a class="mentioned-user" href="https://dev.to/hiddengearz"&gt;@hiddengearz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Groups Tagging API&lt;/strong&gt; (1.3.2). &lt;code&gt;GetResources&lt;/code&gt;, &lt;code&gt;GetTagKeys&lt;/code&gt;, &lt;code&gt;GetTagValues&lt;/code&gt; across 15 services (S3, Lambda, SQS, SNS, DynamoDB, EventBridge, KMS, ECR, ECS, Glue, Cognito, AppSync, Scheduler, CloudFront, EFS). Contributed by @AdigaAkhil.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Groups&lt;/strong&gt; (1.3.27). 19 of 23 spec operations: group CRUD, resource queries, configuration, membership, tagging, account settings. Requested by @staranto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CloudTrail&lt;/strong&gt; (1.3.27). In-memory audit log with control plane and &lt;code&gt;LookupEvents&lt;/code&gt;. Per-account ring buffer, opt-in via &lt;code&gt;CLOUDTRAIL_RECORDING=1&lt;/code&gt;. Contributed by @AdigaAkhil.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AppSync Events API&lt;/strong&gt; (1.3.25). Event API management, channel namespaces, API keys, HTTP publish, and realtime WebSocket on the &lt;code&gt;aws-appsync-event-ws&lt;/code&gt; subprotocol. Contributed by @marcin-nowak-scl.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CloudFront KeyValueStore&lt;/strong&gt; (1.3.25, 1.3.26). Both planes: management plane for &lt;code&gt;Create&lt;/code&gt;/&lt;code&gt;Describe&lt;/code&gt;/&lt;code&gt;List&lt;/code&gt;/&lt;code&gt;Update&lt;/code&gt;/&lt;code&gt;Delete&lt;/code&gt; with ETag concurrency, and a separate &lt;code&gt;cloudfront-keyvaluestore&lt;/code&gt; data plane for &lt;code&gt;GetKey&lt;/code&gt;/&lt;code&gt;PutKey&lt;/code&gt;/&lt;code&gt;DeleteKey&lt;/code&gt;/&lt;code&gt;UpdateKeys&lt;/code&gt;. CFN &lt;code&gt;AWS::CloudFront::KeyValueStore&lt;/code&gt; provisioner included. Contributed by &lt;a class="mentioned-user" href="https://dev.to/davireisvieira"&gt;@davireisvieira&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lambda
&lt;/h2&gt;

&lt;p&gt;A big stretch of work. Highlights:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RIE-based Docker executor&lt;/strong&gt; (1.2.20). &lt;code&gt;LAMBDA_EXECUTOR=docker&lt;/code&gt; now uses official AWS Lambda Runtime Interface Emulator images for every runtime. Containers stay warm between invocations. Originally contributed by @fzonneveld.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime coverage&lt;/strong&gt; (1.2.21 and after). The full image map today: &lt;code&gt;python3.8&lt;/code&gt; through &lt;code&gt;python3.14&lt;/code&gt;, &lt;code&gt;nodejs14.x&lt;/code&gt; through &lt;code&gt;nodejs24.x&lt;/code&gt;, &lt;code&gt;java8.al2&lt;/code&gt;, &lt;code&gt;java11&lt;/code&gt;, &lt;code&gt;java17&lt;/code&gt;, &lt;code&gt;java21&lt;/code&gt;, &lt;code&gt;java25&lt;/code&gt;, &lt;code&gt;dotnet6&lt;/code&gt;, &lt;code&gt;dotnet8&lt;/code&gt;, &lt;code&gt;dotnet10&lt;/code&gt;, &lt;code&gt;ruby3.2&lt;/code&gt;, &lt;code&gt;ruby3.3&lt;/code&gt;, &lt;code&gt;ruby3.4&lt;/code&gt;, plus &lt;code&gt;provided&lt;/code&gt;, &lt;code&gt;provided.al2&lt;/code&gt;, &lt;code&gt;provided.al2023&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lambda to CloudWatch Logs&lt;/strong&gt; (1.3.3). Every invocation now writes &lt;code&gt;START&lt;/code&gt;, handler output, &lt;code&gt;END&lt;/code&gt;, and &lt;code&gt;REPORT&lt;/code&gt; lines to &lt;code&gt;/aws/lambda/{FunctionName}&lt;/code&gt; on a per-invocation stream. Metric filters, subscription filters, and alarm chains that needed real Lambda logs work locally now. Applies to every executor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Async retry and DLQ routing&lt;/strong&gt; (1.3.3). &lt;code&gt;Invoke(InvocationType=Event)&lt;/code&gt; and S3-notification fan-out retry up to &lt;code&gt;MaximumRetryAttempts&lt;/code&gt; and route the final failure to the configured DLQ or &lt;code&gt;OnFailure&lt;/code&gt; destination (SQS, SNS, or another Lambda) with the AWS-shaped envelope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker-in-Docker&lt;/strong&gt; (1.2.21). Running MiniStack inside Docker with &lt;code&gt;LAMBDA_EXECUTOR=docker&lt;/code&gt; works. Code is copied via &lt;code&gt;docker cp&lt;/code&gt; instead of bind-mounted, and Lambda containers are reached via container IP. Reported by @HackJack-101.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter criteria during polling&lt;/strong&gt; (1.3.3). SQS, Kinesis, and DynamoDB Streams pollers evaluate each record against &lt;code&gt;FilterCriteria.Filters&lt;/code&gt; and drop non-matching records before invoking. Equality lists, &lt;code&gt;prefix&lt;/code&gt;, &lt;code&gt;suffix&lt;/code&gt;, &lt;code&gt;anything-but&lt;/code&gt;, &lt;code&gt;exists&lt;/code&gt;, and &lt;code&gt;numeric&lt;/code&gt; content filters are all supported.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step Functions aws-sdk:* integrations
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Resource: arn:aws:states:::aws-sdk:&amp;lt;service&amp;gt;:&amp;lt;op&amp;gt;&lt;/code&gt; works against the following services in 1.3.30. The list is the actual contents of the dispatch table in &lt;code&gt;ministack/services/stepfunctions.py&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query protocol&lt;/strong&gt;: &lt;code&gt;ec2&lt;/code&gt;, &lt;code&gt;sqs&lt;/code&gt;, &lt;code&gt;sns&lt;/code&gt;, &lt;code&gt;rds&lt;/code&gt;, &lt;code&gt;elasticache&lt;/code&gt;, &lt;code&gt;iam&lt;/code&gt;, &lt;code&gt;sts&lt;/code&gt;, &lt;code&gt;cloudwatch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON protocol&lt;/strong&gt;: &lt;code&gt;dynamodb&lt;/code&gt;, &lt;code&gt;secretsmanager&lt;/code&gt;, &lt;code&gt;states&lt;/code&gt; (Step Functions calling itself), &lt;code&gt;logs&lt;/code&gt; (CloudWatch Logs), &lt;code&gt;ssm&lt;/code&gt;, &lt;code&gt;eventbridge&lt;/code&gt;, &lt;code&gt;kinesis&lt;/code&gt;, &lt;code&gt;glue&lt;/code&gt;, &lt;code&gt;athena&lt;/code&gt;, &lt;code&gt;ecs&lt;/code&gt;, &lt;code&gt;ecr&lt;/code&gt;, &lt;code&gt;kms&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST-JSON protocol&lt;/strong&gt;: &lt;code&gt;rdsdata&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST-XML protocol&lt;/strong&gt;: &lt;code&gt;s3&lt;/code&gt; (12 ops added in 1.3.29: &lt;code&gt;ListBuckets&lt;/code&gt;, &lt;code&gt;CreateBucket&lt;/code&gt;, &lt;code&gt;DeleteBucket&lt;/code&gt;, &lt;code&gt;HeadBucket&lt;/code&gt;, &lt;code&gt;GetBucketVersioning&lt;/code&gt;, &lt;code&gt;ListObjectsV2&lt;/code&gt;, &lt;code&gt;ListObjects&lt;/code&gt;, &lt;code&gt;HeadObject&lt;/code&gt;, &lt;code&gt;CopyObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt;, &lt;code&gt;GetObjectTagging&lt;/code&gt;, &lt;code&gt;PutObjectTagging&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt;: dispatched directly to the local Lambda invoker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;REST-JSON output now uses PascalCase to match the query and REST-XML dispatchers (1.3.30, contributed by @jayjanssen), so &lt;code&gt;ResultSelector: $.Records&lt;/code&gt; works on &lt;code&gt;aws-sdk:rdsdata:executeStatement&lt;/code&gt; results.&lt;/p&gt;
&lt;h2&gt;
  
  
  CloudFormation
&lt;/h2&gt;

&lt;p&gt;New provisioners since 1.2.0:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS::Events::EventBus&lt;/code&gt; (1.2.21, contributed by @AdigaAkhil)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS::Pipes::Pipe&lt;/code&gt; (1.3.2, contributed by @davidtme), with &lt;code&gt;FilterPolicy&lt;/code&gt; and &lt;code&gt;FilterPolicyScope&lt;/code&gt; on &lt;code&gt;AWS::SNS::Subscription&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS::Scheduler::Schedule&lt;/code&gt;, &lt;code&gt;AWS::Scheduler::ScheduleGroup&lt;/code&gt; (1.2.19)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS::CodeBuild::Project&lt;/code&gt; (1.2.19, contributed by @AdigaAkhil)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS::EKS::Cluster&lt;/code&gt;, &lt;code&gt;AWS::EKS::Nodegroup&lt;/code&gt; (1.2.20)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS::CloudFront::KeyValueStore&lt;/code&gt; (1.3.26), with the engine now routing previously-provisioned resources through a per-type &lt;code&gt;update&lt;/code&gt; handler when one is defined&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Infrastructure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hypercorn ASGI with HTTP/2 h2c&lt;/strong&gt; (1.3.1). Replaces uvicorn. AWS Java SDK v2 and the Kinesis Client Library work out of the box. Idle RAM dropped from about 21 MB to about 7 MB. Contributed by @AdigaAkhil.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lazy service imports&lt;/strong&gt; (1.2.19). Service modules load on first request. Idle RAM dropped from 59 MB to 21 MB before the hypercorn cut, startup time from 1.2 s to 0.5 s. Services you never call cost zero memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-PR preview Docker images&lt;/strong&gt; (1.3.2). Every PR (including forks) publishes &lt;code&gt;ministackorg/ministack-preview-build:pr-N-&amp;lt;shortsha&amp;gt;&lt;/code&gt;. Reviewers can &lt;code&gt;docker pull&lt;/code&gt; the exact build before merge. Contributed by &lt;a class="mentioned-user" href="https://dev.to/jgrumboe"&gt;@jgrumboe&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multi-tenancy correctness
&lt;/h2&gt;

&lt;p&gt;1.3.3 closed eight cross-account leaks where services stored per-tenant data in plain &lt;code&gt;dict&lt;/code&gt; or &lt;code&gt;list&lt;/code&gt; (CloudWatch metrics and alarm history, ElastiCache events, EventBridge buses, Athena workgroups and data catalogs, SES sent emails, API Gateway v1 stages, deployments, authorizers, and tags). All of them now route through &lt;code&gt;AccountScopedDict&lt;/code&gt;, with isolation tests in &lt;code&gt;tests/test_multitenancy.py&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wire-format fixes worth knowing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All timestamps in JSON responses are int epoch seconds. The Java SDK v2 rejects floats. STS &lt;code&gt;Credentials.Expiration&lt;/code&gt;, EventBridge &lt;code&gt;Time&lt;/code&gt;, EKS &lt;code&gt;createdAt&lt;/code&gt;, Backup, Firehose, CloudTrail timestamps were converted across 1.3.x.&lt;/li&gt;
&lt;li&gt;KMS &lt;code&gt;Verify&lt;/code&gt; raises &lt;code&gt;KMSInvalidSignatureException&lt;/code&gt; on bad signatures instead of returning &lt;code&gt;SignatureValid: false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;KMS crypto operations return the full key ARN as &lt;code&gt;KeyId&lt;/code&gt;, not the bare UUID.&lt;/li&gt;
&lt;li&gt;DynamoDB &lt;code&gt;ConditionalCheckFailedException&lt;/code&gt; populates &lt;code&gt;Item&lt;/code&gt; when &lt;code&gt;ReturnValuesOnConditionCheckFailure="ALL_OLD"&lt;/code&gt; is set, on &lt;code&gt;Put&lt;/code&gt;/&lt;code&gt;Update&lt;/code&gt;/&lt;code&gt;Delete&lt;/code&gt;/&lt;code&gt;TransactWriteItems&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;EC2 VPCs include &lt;code&gt;cidrBlockAssociationSet&lt;/code&gt; (Terraform AWS provider v6 crashes without it).&lt;/li&gt;
&lt;li&gt;SQS FIFO &lt;code&gt;DeduplicationScope: messageGroup&lt;/code&gt; actually scopes per message group.&lt;/li&gt;
&lt;li&gt;SQS &lt;code&gt;ReceiveMessage&lt;/code&gt; honours &lt;code&gt;MessageSystemAttributeNames&lt;/code&gt;. AWS SDK v2 was the only thing reading it (1.3.29, contributed by @joaomena).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 ministackorg/ministack:1.3.30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;ministack
ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;https://github.com/ministackorg/ministack&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://ministack.org/docs/" rel="noopener noreferrer"&gt;https://ministack.org/docs/&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/company/ministackorg/" rel="noopener noreferrer"&gt;https://www.linkedin.com/company/ministackorg/&lt;/a&gt;&lt;br&gt;
Reddit: &lt;a href="https://www.reddit.com/r/ministack/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/ministack/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If something you need is missing or wrong, open an issue. Most of what is listed above started as one.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>aws</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>MiniStack Now Emulates Amazon EKS — Free, Open-Source AWS Emulator v1.2.20</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Fri, 17 Apr 2026 19:02:33 +0000</pubDate>
      <link>https://forem.com/nahuel990/ministack-now-emulates-amazon-eks-free-open-source-aws-emulator-v1220-2900</link>
      <guid>https://forem.com/nahuel990/ministack-now-emulates-amazon-eks-free-open-source-aws-emulator-v1220-2900</guid>
      <description>&lt;h2&gt;
  
  
  EKS Joins the Free AWS Emulator
&lt;/h2&gt;

&lt;p&gt;MiniStack v1.2.20 adds &lt;strong&gt;Amazon EKS&lt;/strong&gt; to its list of emulated AWS services. Create clusters, manage node groups, and hit the EKS API surface — all locally, all free.&lt;/p&gt;

&lt;p&gt;If you haven't seen MiniStack before: it's a free, MIT-licensed AWS emulator. 40+ services on a single Docker container, single port (4566). No account, no API key, no telemetry. Drop-in compatible with the AWS CLI, Terraform, CDK, and every AWS SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  EKS Emulation in Action
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 eks create-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-arn&lt;/span&gt; arn:aws:iam::123456789012:role/eks-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resources-vpc-config&lt;/span&gt; &lt;span class="nv"&gt;subnetIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;subnet-1,securityGroupIds&lt;span class="o"&gt;=&lt;/span&gt;sg-1

aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 eks describe-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-cluster

aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 eks create-nodegroup &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster-name&lt;/span&gt; my-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--nodegroup-name&lt;/span&gt; my-nodes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--node-role&lt;/span&gt; arn:aws:iam::123456789012:role/node-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnets&lt;/span&gt; subnet-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform and CDK work without changes — just point your provider endpoint to &lt;code&gt;localhost:4566&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;EKS is one of those services that's painful to test against real AWS. Clusters take 10-15 minutes to provision, cost money even when idle, and are overkill when all you need is to validate your IaC or integration logic.&lt;/p&gt;

&lt;p&gt;With MiniStack, your Terraform plans, CDK deploys, and SDK integration tests hit a local EKS API instantly. No cluster spin-up time, no AWS bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MiniStack Emulates (40+ Services)
&lt;/h2&gt;

&lt;p&gt;Not just EKS. The full list keeps growing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core (in-memory):&lt;/strong&gt;&lt;br&gt;
S3, SQS, SNS, DynamoDB, Lambda, IAM, STS, Secrets Manager, CloudWatch Logs, SSM Parameter Store, EventBridge, Kinesis, CloudWatch Metrics, SES, Step Functions, Cognito, API Gateway, CloudFormation, ACM, Route 53, Cloud Map&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure (real Docker containers):&lt;/strong&gt;&lt;br&gt;
RDS (real Postgres/MySQL), ElastiCache (real Redis), ECS (real Docker containers), Athena (real SQL via DuckDB), Glue&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compute &amp;amp; Container:&lt;/strong&gt;&lt;br&gt;
EC2, EKS, ECR, Auto Scaling, ELBv2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data &amp;amp; Analytics:&lt;/strong&gt;&lt;br&gt;
Firehose, Redshift, S3 Files&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other:&lt;/strong&gt;&lt;br&gt;
AppConfig, CodeBuild, Scheduler, Resource Groups Tagging API&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;LocalStack Free&lt;/th&gt;
&lt;th&gt;LocalStack Pro&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EKS&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Paid&lt;/td&gt;
&lt;td&gt;✅ Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core services&lt;/td&gt;
&lt;td&gt;Now paid&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS/ElastiCache/ECS&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ Real containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup&lt;/td&gt;
&lt;td&gt;~30s&lt;/td&gt;
&lt;td&gt;~30s&lt;/td&gt;
&lt;td&gt;~2s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM at idle&lt;/td&gt;
&lt;td&gt;~500MB&lt;/td&gt;
&lt;td&gt;~500MB&lt;/td&gt;
&lt;td&gt;~21MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image size&lt;/td&gt;
&lt;td&gt;~1GB&lt;/td&gt;
&lt;td&gt;~1GB&lt;/td&gt;
&lt;td&gt;~270MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;BSL&lt;/td&gt;
&lt;td&gt;Proprietary&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;$35+/mo&lt;/td&gt;
&lt;td&gt;Free forever&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No account. No API key. No telemetry.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;github.com/ministackorg/ministack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker Hub: &lt;a href="https://hub.docker.com/r/nahuelnucera/ministack" rel="noopener noreferrer"&gt;nahuelnucera/ministack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Website: &lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;ministack.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIT licensed. PRs welcome.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>java</category>
      <category>aws</category>
    </item>
    <item>
      <title>MiniStack: Free Local AWS Emulator + Testcontainers Module + AWS CLI Built In</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Tue, 14 Apr 2026 10:44:56 +0000</pubDate>
      <link>https://forem.com/nahuel990/ministack-free-local-aws-emulator-testcontainers-module-aws-cli-built-in-ne8</link>
      <guid>https://forem.com/nahuel990/ministack-free-local-aws-emulator-testcontainers-module-aws-cli-built-in-ne8</guid>
      <description>&lt;p&gt;4 releases in a weekend: Testcontainers Java module, AWS CLI bundled, Step Functions intrinsics, RDS Data stubs. Free, open-source, MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;We shipped 4 releases this weekend (v1.2.6 through v1.2.9):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;org.ministack:testcontainers-ministack:0.1.0&lt;/code&gt;&lt;/strong&gt; on Maven Central&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;62 bug fixes&lt;/strong&gt; found by running 2,490 tests across all 41 services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI bundled&lt;/strong&gt; in the Docker image — init scripts just work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step Functions intrinsics&lt;/strong&gt; — 7 new functions (ArrayContains, MathAdd, UUID, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS Data API stubs&lt;/strong&gt; — test database provisioning without Docker-in-Docker&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What is MiniStack?
&lt;/h2&gt;

&lt;p&gt;MiniStack is a free, MIT-licensed local AWS emulator. One Docker image, one port (4566), 41 services. It started as a response to LocalStack moving core services behind a paid plan.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image size:&lt;/strong&gt; 269MB (was 242MB before CLI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup time:&lt;/strong&gt; &amp;lt;2 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services:&lt;/strong&gt; S3, DynamoDB, SQS, SNS, Lambda, Step Functions, CloudFormation, EC2, ECS, and 32 more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker:&lt;/strong&gt; &lt;code&gt;ministackorg/ministack&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;ministackorg/ministack&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No license keys. No pro tiers. Just run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 ministackorg/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testcontainers Module
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.ministack&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;testcontainers-ministack&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Testcontainers&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;S3IntegrationTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Container&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;MiniStackContainer&lt;/span&gt; &lt;span class="n"&gt;ministack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MiniStackContainer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ministackorg/ministack:latest"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldCreateBucketAndPutObject&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;S3Client&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;endpointOverride&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ministack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEndpoint&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credentialsProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ministack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCredentialsProvider&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;US_EAST_1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createBucket&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-bucket"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-bucket"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello.txt"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;RequestBody&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello MiniStack!"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getObjectAsBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-bucket"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello.txt"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asUtf8String&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello MiniStack!"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works with JUnit 5 and Spring Boot's &lt;code&gt;@DynamicPropertySource&lt;/code&gt;. The container starts in under 2 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  AWS CLI Bundled (v1.2.9)
&lt;/h2&gt;

&lt;p&gt;The Docker image now ships with AWS CLI v1. Init scripts just work without any credential configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ready.d/01-create-resources.sh&lt;/span&gt;
aws s3 mb s3://my-bucket
aws sqs create-queue &lt;span class="nt"&gt;--queue-name&lt;/span&gt; my-queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ready.d/02-seed-data.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS_ENDPOINT_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both &lt;code&gt;.sh&lt;/code&gt; and &lt;code&gt;.py&lt;/code&gt; init scripts are supported. &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;, &lt;code&gt;AWS_DEFAULT_REGION&lt;/code&gt;, and &lt;code&gt;AWS_ENDPOINT_URL&lt;/code&gt; are automatically set for init scripts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Flea Hunt: 62 Bugs Found and Fixed
&lt;/h2&gt;

&lt;p&gt;We ran what we call a "Flea Hunt" — 2,490 tests across all 41 services using 10 parallel MiniStack instances. Some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 versioning&lt;/strong&gt; — &lt;code&gt;GetObject&lt;/code&gt; by &lt;code&gt;VersionId&lt;/code&gt; was returning the wrong version. Delete markers were missing entirely from &lt;code&gt;ListObjectVersions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; — internal &lt;code&gt;_request_id&lt;/code&gt; was leaking into handler events. &lt;code&gt;PublishVersion&lt;/code&gt; ARN was missing the &lt;code&gt;:version&lt;/code&gt; qualifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt; — Go SDK v2 requires a CRC32 header on every response. Without it, the SDK throws an integrity error before your code sees anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java SDK v2&lt;/strong&gt; — &lt;code&gt;time.time()&lt;/code&gt; floats in scientific notation broke the timestamp parser across 14 services. Fixed with &lt;code&gt;int(time.time())&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2&lt;/strong&gt; — TagSpecifications were silently ignored on 11 create operations. DeleteVpc succeeded even with subnets attached.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the fixes: &lt;strong&gt;1,327 regression tests (1,196 Python + 131 Java), zero failures.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Docker:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 ministackorg/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terraform:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;s3&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;dynamodb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;sqs&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
  &lt;span class="nx"&gt;secret_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;ministack.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;ministackorg/ministack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker Hub: &lt;a href="https://hub.docker.com/r/ministackorg/ministack" rel="noopener noreferrer"&gt;ministackorg/ministack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Maven Central: &lt;code&gt;org.ministack:testcontainers-ministack:0.1.0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you give it a try, open an issue or tell us which service you want improved next.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MiniStack is MIT licensed. Star us on GitHub if this saves you from mocking &lt;code&gt;AmazonS3Client&lt;/code&gt; one more time.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>docker</category>
      <category>aws</category>
      <category>python</category>
    </item>
    <item>
      <title>How we made Step Functions call any AWS service locally</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Tue, 07 Apr 2026 18:43:25 +0000</pubDate>
      <link>https://forem.com/nahuel990/how-we-made-step-functions-call-any-aws-service-locally-2m44</link>
      <guid>https://forem.com/nahuel990/how-we-made-step-functions-call-any-aws-service-locally-2m44</guid>
      <description>&lt;p&gt;If you've used AWS Step Functions, you know the &lt;code&gt;aws-sdk:*&lt;/code&gt; integration pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:states:::aws-sdk:dynamodb:PutItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"TableName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-table"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Item"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"S.$"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$.id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line in your state machine, and Step Functions calls DynamoDB directly. No Lambda wrapper needed. AWS recommends this for all new workflows.&lt;/p&gt;

&lt;p&gt;The problem? Every local AWS emulator hardcodes service integrations. Want SQS? There's a handler. Want SNS? Another handler. Want SecretsManager? Sorry, not implemented yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The generic dispatcher
&lt;/h2&gt;

&lt;p&gt;We added a generic &lt;code&gt;aws-sdk:*&lt;/code&gt; task dispatcher to MiniStack. Instead of writing a handler for each service, it routes to whatever service is already emulated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:states:::aws-sdk:secretsmanager:CreateSecret  → SecretsManager handler
arn:aws:states:::aws-sdk:dynamodb:PutItem              → DynamoDB handler  
arn:aws:states:::aws-sdk:ecs:RunTask                   → ECS handler
arn:aws:states:::aws-sdk:kms:Encrypt                   → KMS handler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;38 services. All callable from Step Functions. No new code needed per service.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The dispatcher:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parses the ARN to extract service name and action&lt;/li&gt;
&lt;li&gt;Looks up the service in MiniStack's handler map&lt;/li&gt;
&lt;li&gt;Constructs the right headers (X-Amz-Target for JSON services)&lt;/li&gt;
&lt;li&gt;Converts PascalCase keys to the format each service expects&lt;/li&gt;
&lt;li&gt;Calls the handler and returns the result to the workflow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For JSON-protocol services (DynamoDB, SecretsManager, ECS, KMS, CloudWatch Logs, SSM, EventBridge, Kinesis, Glue, Athena, ECR), it works today. Query and REST protocol services are coming.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else ships with MiniStack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;38 AWS services&lt;/strong&gt; on a single port&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1,047 integration tests&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;48 CloudFormation resource types&lt;/strong&gt; — CDK bootstrap + deploy works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform VPC module&lt;/strong&gt; v6.6.0 fully compatible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; — Python, Node.js, Go/Rust/C++ (Docker), Docker images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real infrastructure&lt;/strong&gt; — RDS spins up Postgres, ElastiCache runs Redis, ECS starts Docker containers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run your Step Functions workflow locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;sfn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stepfunctions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Your workflow can now call any of the 38 services
&lt;/span&gt;&lt;span class="n"&gt;sfn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_state_machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-workflow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;definition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;...your ASL with aws-sdk integrations...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;roleArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::000000000000:role/role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MIT licensed. Free forever. &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;github.com/Nahuel990/ministack&lt;/a&gt;&lt;/p&gt;

</description>
      <category>stepfunctions</category>
      <category>devops</category>
      <category>cicd</category>
      <category>aws</category>
    </item>
    <item>
      <title>Getting started testing AWS with MiniStack in under 10 minutes</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Sun, 05 Apr 2026 11:10:05 +0000</pubDate>
      <link>https://forem.com/nahuel990/getting-started-testing-aws-with-ministack-in-under-10-minutes-5787</link>
      <guid>https://forem.com/nahuel990/getting-started-testing-aws-with-ministack-in-under-10-minutes-5787</guid>
      <description>&lt;p&gt;You don't need an AWS account to test your AWS code.&lt;/p&gt;

&lt;p&gt;We just published an &lt;a href="https://ministack.org/getting-started.html" rel="noopener noreferrer"&gt;AWS Testing 101 guide&lt;/a&gt; — a step-by-step tutorial that takes you from zero to testing S3, DynamoDB, SQS, and Lambda on your laptop. No credentials, no cloud costs, no cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Every developer who writes AWS code hits this wall:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I don't want to create resources in a real AWS account just to test"&lt;/li&gt;
&lt;li&gt;"My CI pipeline needs AWS but I don't want to pay for it"&lt;/li&gt;
&lt;li&gt;"I accidentally left a DynamoDB table running and got billed"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer is a local AWS emulator. Run it on your machine, point your SDK at &lt;code&gt;localhost:4566&lt;/code&gt;, and your code thinks it's talking to AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. 35+ AWS services running locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the guide covers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. S3 — Create buckets and upload files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, local AWS!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. DynamoDB — Create tables and query data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ddb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ddb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;KeySchema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;userId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KeyType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HASH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;userId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;BillingMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PAY_PER_REQUEST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ddb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;userId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. SQS — Send and receive messages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-queue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;MessageBody&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from SQS!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;msgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# Hello from SQS!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Lambda — Deploy and invoke functions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;zipfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;

&lt;span class="c1"&gt;# Package a function
&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;zipfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ZipFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writestr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;def handler(event, ctx): return {&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from Lambda!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;lam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;lam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-function&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python3.12&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::000000000000:role/fake-role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ZipFile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-function&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# {"message": "Hello from Lambda!"}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No AWS account needed&lt;/strong&gt; — test on day one&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cost&lt;/strong&gt; — run thousands of operations for free&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cleanup&lt;/strong&gt; — stop the container, everything's gone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD ready&lt;/strong&gt; — same Docker image in GitHub Actions, GitLab CI, Jenkins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform compatible&lt;/strong&gt; — point your provider at localhost:4566&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The full guide
&lt;/h2&gt;

&lt;p&gt;The complete tutorial with more examples and explanations is at:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://ministack.org/getting-started.html" rel="noopener noreferrer"&gt;ministack.org/getting-started.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MiniStack is the best alternative to LocalStack, it's open-source, MIT licensed, and free forever: &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;github.com/Nahuel990/ministack&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command. 35+ services. Zero cost.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>docker</category>
      <category>cloud</category>
    </item>
    <item>
      <title>MiniStack vs Floci vs LocalStack: Honest Performance Benchmark (April 3rd 2026)</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Fri, 03 Apr 2026 19:57:59 +0000</pubDate>
      <link>https://forem.com/nahuel990/ministack-vs-floci-vs-localstack-honest-performance-benchmark-april-3rd-2026-479p</link>
      <guid>https://forem.com/nahuel990/ministack-vs-floci-vs-localstack-honest-performance-benchmark-april-3rd-2026-479p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;We ran 30 API operations, throughput tests, and service coverage checks against real Docker containers. No cherry-picking. No marketing. Just numbers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;LocalStack Free&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Services supported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;~15 (rest paywalled)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image size&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;211 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;276 MB&lt;/td&gt;
&lt;td&gt;~1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory after load&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;39 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;56 MB&lt;/td&gt;
&lt;td&gt;~500 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup time&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt;2s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~3s&lt;/td&gt;
&lt;td&gt;~15-30s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median API latency&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.6 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.2 ms&lt;/td&gt;
&lt;td&gt;varies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MIT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;BSL (restricted)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fresh &lt;code&gt;docker system prune -af --volumes&lt;/code&gt; before each run&lt;/li&gt;
&lt;li&gt;Both images pulled from Docker Hub (&lt;code&gt;nahuelnucera/ministack:latest&lt;/code&gt;, &lt;code&gt;hectorvent/floci:latest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Each operation run &lt;strong&gt;5 times&lt;/strong&gt;, median taken&lt;/li&gt;
&lt;li&gt;All tests use boto3 with &lt;code&gt;endpoint_url=http://localhost:{port}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Machine: Apple Silicon M4, 24 GB RAM, Docker Desktop&lt;/li&gt;
&lt;li&gt;No warm-up runs — cold container, first request measured&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Image Size
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nahuelnucera/ministack:latest    211 MB
hectorvent/floci:latest          276 MB
localstack/localstack:latest    ~1.0 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MiniStack is 24% smaller than Floci and 5x smaller than LocalStack. MiniStack uses Alpine + Python + Node.js. Floci uses a JVM-based stack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Startup Time
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;First Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MiniStack&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Floci&lt;/td&gt;
&lt;td&gt;15 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LocalStack&lt;/td&gt;
&lt;td&gt;15-30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack starts instantly. No JVM warm-up, no class loading. The ASGI server is ready before the health check even fires.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Latency (median of 5 runs, single operation)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  S3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateBucket&lt;/td&gt;
&lt;td&gt;5.9 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.6 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PutObject (1 KB)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.4 ms&lt;/td&gt;
&lt;td&gt;+2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PutObject (100 KB)&lt;/td&gt;
&lt;td&gt;10.6 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetObject&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.4 ms&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ListObjectsV2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.2 ms&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;S3 is competitive. Floci edges ahead on small reads. MiniStack is significantly faster on larger writes (100 KB: 31% faster).&lt;/p&gt;

&lt;h3&gt;
  
  
  SQS
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateQueue&lt;/td&gt;
&lt;td&gt;4.5 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SendMessage&lt;/td&gt;
&lt;td&gt;9.8 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReceiveMessage&lt;/td&gt;
&lt;td&gt;7.8 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-17%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack is consistently faster on SQS operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  DynamoDB
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateTable&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.4 ms&lt;/td&gt;
&lt;td&gt;-8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PutItem&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.2 ms&lt;/td&gt;
&lt;td&gt;+14%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetItem&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.4 ms&lt;/td&gt;
&lt;td&gt;+16%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.0 ms&lt;/td&gt;
&lt;td&gt;+16%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scan&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.2 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.6 ms&lt;/td&gt;
&lt;td&gt;+10%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Floci wins on DynamoDB read/write operations. This is likely due to Java's optimized JSON parsing for the DynamoDB wire format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Services
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SNS CreateTopic&lt;/td&gt;
&lt;td&gt;3.9 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNS Publish&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8.8 ms&lt;/td&gt;
&lt;td&gt;+4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM CreateRole&lt;/td&gt;
&lt;td&gt;5.0 ms&lt;/td&gt;
&lt;td&gt;5.9 ms&lt;/td&gt;
&lt;td&gt;+18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STS GetCallerIdentity&lt;/td&gt;
&lt;td&gt;5.1 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-12%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM PutParameter&lt;/td&gt;
&lt;td&gt;6.6 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-29%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM GetParameter&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.2 ms&lt;/td&gt;
&lt;td&gt;+11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SecretsManager Create&lt;/td&gt;
&lt;td&gt;4.8 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SecretsManager Get&lt;/td&gt;
&lt;td&gt;4.7 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge PutRule&lt;/td&gt;
&lt;td&gt;5.3 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge PutEvents&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.5 ms&lt;/td&gt;
&lt;td&gt;+15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kinesis CreateStream&lt;/td&gt;
&lt;td&gt;5.6 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.1 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CW PutMetricData&lt;/td&gt;
&lt;td&gt;4.9 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs CreateLogGroup&lt;/td&gt;
&lt;td&gt;6.5 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.6 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-29%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route53 CreateHostedZone&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ERR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Floci doesn't support Route53&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack is faster on SSM, SecretsManager, CloudWatch, and Logs. Floci is faster on IAM and EventBridge PutEvents. Route53 only works on MiniStack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Throughput
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQS SendMessage x500&lt;/td&gt;
&lt;td&gt;221 ops/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;233 ops/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On sustained SQS throughput, MiniStack is 5% faster. Earlier cold-start benchmarks showed Floci ahead, but with warm containers the gap disappears.&lt;/p&gt;




&lt;h2&gt;
  
  
  Memory Usage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;At idle&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;38 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After 500+ operations&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;56 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;39 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Interesting: Floci uses less memory at idle (JVM lazy class loading) but grows to 56 MB after load. MiniStack starts at 38 MB and barely grows. Over time, MiniStack's memory profile is more predictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Service Coverage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SecretsManager&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Logs&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kinesis&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Metrics&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Functions&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudFormation&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACM&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KMS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElastiCache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Glue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Athena&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Firehose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Route53&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EC2/VPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ELBv2/ALB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WAF v2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack supports 55% more services. The gap is particularly significant for infrastructure-heavy workloads (ECS, RDS with real Docker, EC2/VPC, Route53, ALB).&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;LocalStack Free&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Python execution&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Node.js execution&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda warm workers&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS real Postgres/MySQL&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECS real Docker containers&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElastiCache real Redis&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena real SQL (DuckDB)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudFormation&lt;/td&gt;
&lt;td&gt;YES (12 types)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Functions TestState API&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SFN Mock Config (SFN Local compat)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State persistence&lt;/td&gt;
&lt;td&gt;YES (20 services)&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 disk persistence&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detached mode (&lt;code&gt;-d&lt;/code&gt; / &lt;code&gt;--stop&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform v6 compatible&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS SDK v2 chunked encoding&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testcontainers examples&lt;/td&gt;
&lt;td&gt;Java, Go, Python&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;docker run&lt;/code&gt; one-liner&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PyPI installable&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What Floci Does Better
&lt;/h2&gt;

&lt;p&gt;Let's be honest about where Floci wins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB read latency&lt;/strong&gt; — 15-16% faster on GetItem/Query/Scan. Java's JSON processing is well-optimized for DynamoDB's wire format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idle memory&lt;/strong&gt; — 26 MB vs 38 MB at cold start. JVM defers class loading.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What MiniStack Does Better
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;11 more services&lt;/strong&gt; — ECS, ElastiCache, Glue, Athena, Route53, EC2, EMR, ALB, WAF, Firehose, ECR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real infrastructure&lt;/strong&gt; — RDS spins up actual Postgres/MySQL. ECS runs real containers. Athena runs real SQL via DuckDB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Node.js&lt;/strong&gt; — warm worker pool for both Python and Node.js.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State persistence&lt;/strong&gt; — 20 services survive restarts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster on most operations&lt;/strong&gt; — SSM, SecretsManager, SQS, CloudWatch, Logs are 15-30% faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform v6 ready&lt;/strong&gt; — EC2 stubs, S3 Control routing, DynamoDB WarmThroughput.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller image&lt;/strong&gt; — 211 MB vs 276 MB (24% smaller).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  When to Use What
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use MiniStack if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need ECS, Route53, EC2, Glue, Athena, ALB, or any of the 11 extra services&lt;/li&gt;
&lt;li&gt;You're migrating from LocalStack and need maximum service coverage&lt;/li&gt;
&lt;li&gt;You want state persistence across container restarts&lt;/li&gt;
&lt;li&gt;You use Terraform v6&lt;/li&gt;
&lt;li&gt;You want Lambda Node.js support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Floci if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You only need the core 20 services&lt;/li&gt;
&lt;li&gt;DynamoDB read performance is critical for your test suite&lt;/li&gt;
&lt;li&gt;You want the smallest possible idle memory footprint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use LocalStack Pro if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need IAM policy enforcement&lt;/li&gt;
&lt;li&gt;You need Lambda container image support&lt;/li&gt;
&lt;li&gt;Budget isn't a constraint&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Benchmark Reproducibility
&lt;/h2&gt;

&lt;p&gt;All benchmarks can be reproduced with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;
docker pull nahuelnucera/ministack:latest
docker pull hectorvent/floci:latest

docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ms &lt;span class="nt"&gt;-p&lt;/span&gt; 4568:4566 nahuelnucera/ministack:latest
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; fl &lt;span class="nt"&gt;-p&lt;/span&gt; 4567:4566 hectorvent/floci:latest

&lt;span class="c"&gt;# Run your own boto3 tests against both ports&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Versions Tested
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MiniStack: v1.1.27 (April 2026)&lt;/li&gt;
&lt;li&gt;Floci: latest (April 2026)&lt;/li&gt;
&lt;li&gt;LocalStack: comparison based on published documentation (not benchmarked directly)&lt;/li&gt;
&lt;li&gt;boto3: 1.34+&lt;/li&gt;
&lt;li&gt;Docker Desktop: latest&lt;/li&gt;
&lt;li&gt;Hardware: Apple M4, 24 GB RAM&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This benchmark was created by the MiniStack team. We tried to be as fair as possible — if you find any methodology issues, please open an issue on &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>aws</category>
      <category>testing</category>
    </item>
    <item>
      <title>Ministack, the best alternative to Localstack</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Mon, 30 Mar 2026 19:18:13 +0000</pubDate>
      <link>https://forem.com/nahuel990/ministack-the-best-alternative-to-localstack-4553</link>
      <guid>https://forem.com/nahuel990/ministack-the-best-alternative-to-localstack-4553</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F979z720qvr8qm4u3zvbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F979z720qvr8qm4u3zvbh.png" alt="Ministack, the best alternative to Localstack" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  We built a free LocalStack alternative — after last week’s changes
&lt;/h2&gt;

&lt;p&gt;Last week, LocalStack moved more core services behind a paywall.&lt;/p&gt;

&lt;p&gt;For a lot of teams, that quietly broke something important:&lt;/p&gt;

&lt;p&gt;👉 local AWS development that &lt;em&gt;just worked&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So we decided to build something about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing MiniStack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MiniStack&lt;/strong&gt; is a free, open-source AWS emulator designed for local development and CI/CD.&lt;/p&gt;

&lt;p&gt;It just got featured as &lt;em&gt;Project of the Day&lt;/em&gt; on aidigitalcrew.com 🙌&lt;/p&gt;




&lt;h2&gt;
  
  
  What started small grew fast
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;30 AWS services
&lt;/li&gt;
&lt;li&gt;~2s startup time
&lt;/li&gt;
&lt;li&gt;~30MB RAM at idle
&lt;/li&gt;
&lt;li&gt;Single port (&lt;code&gt;:4566&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;600+ integration tests
&lt;/li&gt;
&lt;li&gt;MIT licensed (fully free)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The key difference: real infrastructure
&lt;/h2&gt;

&lt;p&gt;MiniStack doesn’t just mock APIs where it matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RDS&lt;/strong&gt; → spins up real Postgres/MySQL containers
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache&lt;/strong&gt; → runs real Redis
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS&lt;/strong&gt; → starts real Docker containers
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena&lt;/strong&gt; → executes real SQL via DuckDB
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 No fake endpoints. No stubbed responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Services that are now paid elsewhere — free here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;EC2 (instances, VPCs, networking)
&lt;/li&gt;
&lt;li&gt;EMR (clusters, steps)
&lt;/li&gt;
&lt;li&gt;Cognito (user + identity pools)
&lt;/li&gt;
&lt;li&gt;EBS / EFS
&lt;/li&gt;
&lt;li&gt;ALB / ELBv2
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Same developer experience
&lt;/h2&gt;

&lt;p&gt;If you’ve used LocalStack before, nothing changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack

aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 s3 mb s3://my-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;No account
&lt;/li&gt;
&lt;li&gt;No API key
&lt;/li&gt;
&lt;li&gt;No telemetry
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why we built this
&lt;/h2&gt;

&lt;p&gt;This isn’t about competing.&lt;/p&gt;

&lt;p&gt;LocalStack is a great product.&lt;/p&gt;

&lt;p&gt;But local development tooling should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast
&lt;/li&gt;
&lt;li&gt;predictable
&lt;/li&gt;
&lt;li&gt;accessible
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;⭐ &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;https://github.com/Nahuel990/ministack&lt;/a&gt;&lt;br&gt;
⭐ &lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;https://ministack.org&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Would love feedback, ideas, or contributions from the community.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>terraform</category>
      <category>python</category>
      <category>aws</category>
    </item>
    <item>
      <title>MiniStack v1.1.2 — Cognito, EC2, EMR, 656 Tests, and Zero Docker Leaks</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Mon, 30 Mar 2026 04:57:48 +0000</pubDate>
      <link>https://forem.com/nahuel990/ministack-v110-cognito-ec2-emr-656-tests-and-zero-docker-leaks-2k3b</link>
      <guid>https://forem.com/nahuel990/ministack-v110-cognito-ec2-emr-656-tests-and-zero-docker-leaks-2k3b</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's new in v1.1.0
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Amazon Cognito — full emulation
&lt;/h3&gt;

&lt;p&gt;This was the most requested feature since launch. v1.1.0 ships complete Cognito support across both planes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Pools (cognito-idp)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full user lifecycle: &lt;code&gt;SignUp&lt;/code&gt;, &lt;code&gt;ConfirmSignUp&lt;/code&gt;, &lt;code&gt;AdminCreateUser&lt;/code&gt;, &lt;code&gt;AdminDeleteUser&lt;/code&gt;, &lt;code&gt;AdminGetUser&lt;/code&gt;, &lt;code&gt;ListUsers&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Auth flows: &lt;code&gt;USER_PASSWORD_AUTH&lt;/code&gt;, &lt;code&gt;ADMIN_USER_PASSWORD_AUTH&lt;/code&gt;, &lt;code&gt;REFRESH_TOKEN_AUTH&lt;/code&gt;, &lt;code&gt;USER_SRP_AUTH&lt;/code&gt; (returns &lt;code&gt;PASSWORD_VERIFIER&lt;/code&gt; challenge)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FORCE_CHANGE_PASSWORD&lt;/code&gt; challenge on first login&lt;/li&gt;
&lt;li&gt;Self-service: &lt;code&gt;ForgotPassword&lt;/code&gt;, &lt;code&gt;ConfirmForgotPassword&lt;/code&gt;, &lt;code&gt;ChangePassword&lt;/code&gt;, &lt;code&gt;GetUser&lt;/code&gt;, &lt;code&gt;DeleteUser&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Groups, domains, MFA config, tags — all covered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Identity Pools (cognito-identity)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CreateIdentityPool&lt;/code&gt;, &lt;code&gt;GetId&lt;/code&gt;, &lt;code&gt;GetCredentialsForIdentity&lt;/code&gt;, &lt;code&gt;GetOpenIdToken&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SetIdentityPoolRoles&lt;/code&gt;, &lt;code&gt;GetIdentityPoolRoles&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Federated identity: &lt;code&gt;MergeDeveloperIdentities&lt;/code&gt;, &lt;code&gt;UnlinkIdentity&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;OAuth2&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /oauth2/token&lt;/code&gt; — client_credentials flow, returns stub Bearer token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stub JWTs are structurally valid base64url tokens — they pass format checks in most SDKs without needing real crypto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;idp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cognito-idp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a pool
&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_user_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PoolName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pool_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UserPool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Sign up a user
&lt;/span&gt;&lt;span class="n"&gt;idp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ClientId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alice@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password123!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UserAttributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alice@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Confirm and authenticate
&lt;/span&gt;&lt;span class="n"&gt;idp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;admin_confirm_sign_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserPoolId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pool_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alice@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initiate_auth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AuthFlow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USER_PASSWORD_AUTH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;AuthParameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USERNAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alice@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password123!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;ClientId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AuthenticationResult&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AccessToken&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  644 tests — one file, all passing
&lt;/h3&gt;

&lt;p&gt;We merged a separate QA test file into the main suite and fixed every test bug we found along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;test_s3_list_v1_marker_pagination&lt;/code&gt; — &lt;code&gt;NextMarker&lt;/code&gt; only returned when &lt;code&gt;Delimiter&lt;/code&gt; is set (AWS spec)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_iam_inline_user_policy&lt;/code&gt; — boto3 deserialises &lt;code&gt;PolicyDocument&lt;/code&gt; as a dict, not a string&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_kinesis_at_timestamp_iterator&lt;/code&gt; — boto3 already returns &lt;code&gt;Data&lt;/code&gt; as &lt;code&gt;bytes&lt;/code&gt;, no need to base64-decode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_rds_snapshot_crud&lt;/code&gt; / &lt;code&gt;test_rds_deletion_protection&lt;/code&gt; — added &lt;code&gt;finally&lt;/code&gt; cleanup so containers are deleted after each test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;644 tests across all 25 services. Single file. All passing.&lt;/p&gt;




&lt;h3&gt;
  
  
  Docker volume leak — fixed
&lt;/h3&gt;

&lt;p&gt;This one was subtle. Every RDS (&lt;code&gt;CreateDBInstance&lt;/code&gt;) and ElastiCache (&lt;code&gt;CreateCacheCluster&lt;/code&gt;) call spins up a real Docker container. The postgres and mysql images declare &lt;code&gt;VOLUME /var/lib/postgresql/data&lt;/code&gt; — so Docker was creating an anonymous volume for every container, even after the container was removed.&lt;/p&gt;

&lt;p&gt;After 20 test runs: 30+ dangling volumes, 600MB+ of wasted space.&lt;/p&gt;

&lt;p&gt;Two fixes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tmpfs&lt;/code&gt;&lt;/strong&gt; on &lt;code&gt;containers.run()&lt;/code&gt; — postgres/mysql data lives in container RAM. No volume created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;container.remove(v=True)&lt;/code&gt;&lt;/strong&gt; in &lt;code&gt;reset()&lt;/code&gt; — volumes are removed with the container.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: 32 dangling volumes after 3 test runs
After:  2 volumes total (ministack + redis, always)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean up what you already have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make purge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;make purge&lt;/code&gt; — safe cleanup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;purge&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;stop-compose&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"label=ministack"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
    docker volume prune &lt;span class="nt"&gt;-f&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ./data/s3/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every container MiniStack spins up is labelled &lt;code&gt;ministack=true&lt;/code&gt;. The purge target uses that label — it won't touch your other Redis, Postgres, or MySQL containers.&lt;/p&gt;




&lt;h3&gt;
  
  
  Bug fixes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lambda &lt;code&gt;GetFunctionConcurrency&lt;/code&gt;&lt;/strong&gt; — was returning 404 after &lt;code&gt;DeleteFunctionConcurrency&lt;/code&gt;. Now returns &lt;code&gt;{}&lt;/code&gt; matching AWS behaviour&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache &lt;code&gt;ModifyCacheParameterGroup&lt;/code&gt;&lt;/strong&gt; — parameter key format was wrong (&lt;code&gt;member&lt;/code&gt; vs &lt;code&gt;ParameterNameValue&lt;/code&gt;). Modified params were silently ignored&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS &lt;code&gt;ModifyDBInstance&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;DeletionProtection=False&lt;/code&gt; with &lt;code&gt;ApplyImmediately=True&lt;/code&gt; now correctly applies immediately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito &lt;code&gt;GetCredentialsForIdentity&lt;/code&gt;&lt;/strong&gt; — response field is &lt;code&gt;SecretKey&lt;/code&gt; (correct boto3 wire name)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port conflict on &lt;code&gt;pip install&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;ministack&lt;/code&gt; now prints a clear error if port 4566 is already in use instead of a raw uvicorn traceback&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  AWS CLI docs fix
&lt;/h3&gt;

&lt;p&gt;A user reported credentials failing. The README was showing &lt;code&gt;aws configure --profile local&lt;/code&gt; but then omitting &lt;code&gt;--profile local&lt;/code&gt; from the example commands. Fixed — two working options now documented:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A — environment variables&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test
export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test
export &lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1

aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 s3 mb s3://my-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B — named profile&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt;
&lt;span class="c"&gt;# Access Key: test / Secret: test / Region: us-east-1&lt;/span&gt;

aws &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 s3 mb s3://my-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# PyPI&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; ministack

&lt;span class="c"&gt;# Docker&lt;/span&gt;
docker pull nahuelnucera/ministack:latest
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;github.com/Nahuel990/ministack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;25 AWS services. Free. MIT licensed. No account required.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SFN Activities&lt;/strong&gt; (&lt;code&gt;CreateActivity&lt;/code&gt;, &lt;code&gt;GetActivityTask&lt;/code&gt;) — already requested&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State persistence&lt;/strong&gt; for Secrets Manager, SSM, DynamoDB — &lt;code&gt;PERSIST_STATE=1&lt;/code&gt; currently only covers API Gateway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACM&lt;/strong&gt; — last item on the original roadmap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issues and PRs welcome.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>python</category>
      <category>development</category>
      <category>ec2</category>
    </item>
    <item>
      <title>LocalStack Is Dead. MiniStack Runs Real Databases for Free.</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Sat, 28 Mar 2026 15:14:30 +0000</pubDate>
      <link>https://forem.com/nahuel990/localstack-is-dead-ministack-runs-real-databases-for-free-1lim</link>
      <guid>https://forem.com/nahuel990/localstack-is-dead-ministack-runs-real-databases-for-free-1lim</guid>
      <description>&lt;p&gt;&lt;em&gt;LocalStack archived its repo on March 23. Pipelines are breaking everywhere. Here's a drop-in replacement that actually spins up real Postgres, Redis, and Docker containers — no account, no API key, MIT licensed.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you woke up this week to broken pipelines, you're not alone.&lt;/p&gt;

&lt;p&gt;LocalStack archived its public GitHub repository and moved every image behind mandatory authentication. No warning in your &lt;code&gt;docker-compose.yml&lt;/code&gt;. No deprecation period for CI. Just — gone.&lt;/p&gt;

&lt;p&gt;The usual suspects have appeared: Moto, Floci, individual service emulators. But one project stands out for a reason nobody else is doing — it runs &lt;strong&gt;real infrastructure&lt;/strong&gt; instead of faking it.&lt;/p&gt;

&lt;h2&gt;
  
  
  MiniStack: The One That Doesn't Fake It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No account. No API key. No telemetry. No BSL license. MIT, forever.&lt;/p&gt;

&lt;p&gt;Existing &lt;code&gt;--endpoint-url&lt;/code&gt; configs, boto3 clients, Terraform providers, and CDK stacks work without code changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 s3 mb s3://my-bucket
aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 sqs create-queue &lt;span class="nt"&gt;--queue-name&lt;/span&gt; my-queue
aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 dynamodb list-tables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Killer Feature: Real Infrastructure
&lt;/h2&gt;

&lt;p&gt;Most AWS emulators fake everything in memory. MiniStack doesn't — not where it matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RDS creates actual database containers:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;

&lt;span class="n"&gt;rds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_db_instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;DBInstanceIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mydb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DBInstanceClass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db.t3.micro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Engine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MasterUsername&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MasterUserPassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DBName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;appdb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;AllocatedStorage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DBInstance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# This is a REAL Postgres instance
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dbname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;appdb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;conn&lt;/code&gt; is talking to a real Postgres process. Not a mock. Not an in-memory fake. A real database you can inspect with &lt;code&gt;psql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Same story for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache&lt;/strong&gt; → spins up a real Redis container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS&lt;/strong&gt; → runs real Docker containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena&lt;/strong&gt; → executes real SQL via DuckDB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → real Python execution with warm starts between invocations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;If a test says "insert a row into RDS and read it back," the question is: is it testing application code or testing someone's mock of Postgres?&lt;/p&gt;

&lt;p&gt;Moto is excellent for unit tests. But when integration confidence is what matters — when SQL migrations need to actually run, connection pooling needs to actually work, Redis TTLs need to actually expire — real infrastructure beats mocks. Locally.&lt;/p&gt;

&lt;p&gt;That's the gap MiniStack fills.&lt;/p&gt;

&lt;h2&gt;
  
  
  23 Services, One Port
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Real Infra?&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;In-memory + persistence&lt;/td&gt;
&lt;td&gt;Full API including presigned URLs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;FIFO, dead-letter, visibility timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Subscriptions, filtering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Streams, GSI, LSI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;✅ Real Python execution&lt;/td&gt;
&lt;td&gt;Warm starts between invocations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS&lt;/td&gt;
&lt;td&gt;✅ Real Postgres/MySQL&lt;/td&gt;
&lt;td&gt;Actual Docker containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElastiCache&lt;/td&gt;
&lt;td&gt;✅ Real Redis&lt;/td&gt;
&lt;td&gt;With IAM auth support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECS&lt;/td&gt;
&lt;td&gt;✅ Real Docker&lt;/td&gt;
&lt;td&gt;Actual container execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena&lt;/td&gt;
&lt;td&gt;✅ Real SQL (DuckDB)&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM / STS&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Policies, roles, assume-role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Full CRUD + rotation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;String, SecureString, StringList&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Rules, targets, patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kinesis&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Shards, iterators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Logs&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Log groups, streams, events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Metrics&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Put/Get metric data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SES&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Send email (logged, not delivered)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Functions&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;State machine execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue&lt;/td&gt;
&lt;td&gt;In-memory&lt;/td&gt;
&lt;td&gt;Catalog, crawlers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;Moto&lt;/th&gt;
&lt;th&gt;LocalStack (was)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;BSL (was Apache)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Account required&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (now)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real databases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Pro only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real containers (ECS)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Pro only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~150 MB&lt;/td&gt;
&lt;td&gt;~90 MB&lt;/td&gt;
&lt;td&gt;N/A (library)&lt;/td&gt;
&lt;td&gt;~1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAM at idle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~30 MB&lt;/td&gt;
&lt;td&gt;~13 MB&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;~500 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Docker service&lt;/td&gt;
&lt;td&gt;Docker service&lt;/td&gt;
&lt;td&gt;In-process mock&lt;/td&gt;
&lt;td&gt;Docker service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Integration tests, real infra&lt;/td&gt;
&lt;td&gt;Quick emulation, CI&lt;/td&gt;
&lt;td&gt;Python unit tests&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  5-Minute Migration from LocalStack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Swap the image&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# OLD&lt;/span&gt;
  &lt;span class="c1"&gt;# localstack:&lt;/span&gt;
  &lt;span class="c1"&gt;#   image: localstack/localstack&lt;/span&gt;

  &lt;span class="c1"&gt;# NEW&lt;/span&gt;
  &lt;span class="na"&gt;ministack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nahuelnucera/ministack&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4566:4566"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REDIS_HOST=redis&lt;/span&gt;

  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6379:6379"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: There is no step 2.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Same port. Same endpoint format. Same AWS API. boto3 clients, Terraform configs, and CDK stacks don't change.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Actions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Start MiniStack&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;docker run -d -p 4566:4566 nahuelnucera/ministack&lt;/span&gt;
    &lt;span class="s"&gt;sleep 2&lt;/span&gt;
    &lt;span class="s"&gt;curl http://localhost:4566/_localstack/health&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;AWS_ENDPOINT_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;
    &lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pytest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Footprint Difference
&lt;/h2&gt;

&lt;p&gt;LocalStack's image was over 1 GB and consumed ~500 MB at idle. That's a real cost in CI — especially on GitHub Actions free tier or when running parallel jobs.&lt;/p&gt;

&lt;p&gt;MiniStack is ~150 MB and idles at ~30 MB. CI spins up faster, laptop fans stay quiet, and there's headroom for actual test workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is this production-ready?&lt;/strong&gt;&lt;br&gt;
It's a local dev and CI tool. Don't expose it to the internet — it has no authentication by design. That's the whole point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about Moto?&lt;/strong&gt;&lt;br&gt;
Moto is the right choice for Python unit tests where in-process speed matters and Docker isn't wanted. MiniStack is the right choice when real infrastructure is needed — actual database connections, real Redis commands, real container execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about Floci?&lt;/strong&gt;&lt;br&gt;
Floci is a solid project with faster startup and smaller footprint. MiniStack's differentiator is real infrastructure — actual Postgres/Redis containers out of the box, not in-memory fakes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will this stay free?&lt;/strong&gt;&lt;br&gt;
MIT is MIT. Fork it, embed it, sell it. No "community vs pro" split. No feature gates. No bait-and-switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🐙 GitHub: &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;github.com/Nahuel990/ministack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 Website: &lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;ministack.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐳 Docker Hub: &lt;a href="https://hub.docker.com/r/nahuelnucera/ministack" rel="noopener noreferrer"&gt;nahuelnucera/ministack&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Star the repo if this saved your pipeline. PRs welcome.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;No account. No API key. No telemetry. Just AWS APIs, locally.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cloud</category>
      <category>terraform</category>
      <category>testing</category>
    </item>
    <item>
      <title>Ministack, a free LocalStack alternative. v1.0.7 released</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Fri, 27 Mar 2026 21:07:42 +0000</pubDate>
      <link>https://forem.com/nahuel990/ministack-a-free-localstack-alternative-v107-released-378h</link>
      <guid>https://forem.com/nahuel990/ministack-a-free-localstack-alternative-v107-released-378h</guid>
      <description>&lt;p&gt;&lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;MiniStack&lt;/a&gt; is a free, MIT-licensed local AWS emulator — a drop-in replacement for LocalStack that runs 23 services on a single port with no account required. Today we're shipping v1.0.7.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Amazon Data Firehose
&lt;/h3&gt;

&lt;p&gt;MiniStack now emulates Amazon Data Firehose (all 12 API operations):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;fh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;firehose&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a delivery stream with S3 destination
&lt;/span&gt;&lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_delivery_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;DeliveryStreamName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DeliveryStreamType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DirectPut&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExtendedS3DestinationConfiguration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BucketARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::my-bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RoleARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::000000000000:role/firehose-role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BufferingHints&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SizeInMBs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IntervalInSeconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Prefix&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Put records — S3 destination writes synchronously to the local S3 emulator
&lt;/span&gt;&lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;DeliveryStreamName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;click&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Batch ingestion
&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;record-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_record_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DeliveryStreamName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Records&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FailedPutCount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full operation coverage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CreateDeliveryStream&lt;/code&gt; / &lt;code&gt;DeleteDeliveryStream&lt;/code&gt; / &lt;code&gt;DescribeDeliveryStream&lt;/code&gt; / &lt;code&gt;ListDeliveryStreams&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PutRecord&lt;/code&gt; / &lt;code&gt;PutRecordBatch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UpdateDestination&lt;/code&gt; — concurrency-safe via &lt;code&gt;CurrentDeliveryStreamVersionId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TagDeliveryStream&lt;/code&gt; / &lt;code&gt;UntagDeliveryStream&lt;/code&gt; / &lt;code&gt;ListTagsForDeliveryStream&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;StartDeliveryStreamEncryption&lt;/code&gt; / &lt;code&gt;StopDeliveryStreamEncryption&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Destination types accepted: &lt;code&gt;ExtendedS3&lt;/code&gt;, &lt;code&gt;S3&lt;/code&gt; (deprecated), &lt;code&gt;HttpEndpoint&lt;/code&gt;, &lt;code&gt;Redshift&lt;/code&gt;, &lt;code&gt;OpenSearch&lt;/code&gt;, &lt;code&gt;Splunk&lt;/code&gt;, &lt;code&gt;Snowflake&lt;/code&gt;, &lt;code&gt;Iceberg&lt;/code&gt;. S3 destinations write records to the local S3 emulator synchronously. All other destination types buffer records in-memory (great for testing your producer code without a real backend).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MiniStack's Firehose actually fixes 4 known LocalStack Community bugs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ExtendedS3DestinationConfiguration&lt;/code&gt; + &lt;code&gt;PutRecord&lt;/code&gt; crashes in LocalStack (issue #5936) — works fine here&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;KinesisStreamAsSource&lt;/code&gt; stream creation sometimes fails in LocalStack (issue #1758) — never fails here&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DescribeDeliveryStream&lt;/code&gt; doesn't return &lt;code&gt;HttpEndpointDestinationDescription&lt;/code&gt; in LocalStack (issue #3384) — returned correctly here&lt;/li&gt;
&lt;li&gt;Elasticsearch &lt;code&gt;KeyError: 'IndexName'&lt;/code&gt; in LocalStack (issue #5047) — no crash here&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Virtual-hosted style S3
&lt;/h3&gt;

&lt;p&gt;AWS SDKs can address S3 buckets via the host header (&lt;code&gt;bucket.s3.amazonaws.com&lt;/code&gt;). MiniStack now supports the local equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;addressing_style&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;virtual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Requests sent as: http://my-bucket.localhost:4566/key
# MiniStack rewrites to path-style internally — transparent to your code
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  DynamoDB &lt;code&gt;OR&lt;/code&gt;/&lt;code&gt;AND&lt;/code&gt; expression fix
&lt;/h3&gt;

&lt;p&gt;A subtle parser bug caused &lt;code&gt;ConditionExpression&lt;/code&gt; and &lt;code&gt;FilterExpression&lt;/code&gt; to crash with &lt;code&gt;Invalid expression: Expected RPAREN, got NAME_REF&lt;/code&gt; when using numeric &lt;code&gt;ExpressionAttributeNames&lt;/code&gt; keys (&lt;code&gt;#0&lt;/code&gt;, &lt;code&gt;#1&lt;/code&gt;) — which PynamoDB generates automatically on composite key tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This now works correctly
&lt;/span&gt;&lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;row#1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;row#1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;updated_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-01-01T00:00:00Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="n"&gt;ConditionExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(attribute_not_exists (#0) OR #1 &amp;lt;= :0)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionAttributeNames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;updated_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-01-01T00:00:00Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The recursive-descent expression evaluator used Python's &lt;code&gt;or&lt;/code&gt;/&lt;code&gt;and&lt;/code&gt; operators directly (&lt;code&gt;left = left or self._and_expr()&lt;/code&gt;). When &lt;code&gt;left&lt;/code&gt; was truthy, Python short-circuited and never consumed the right-hand tokens from the stream. The outer &lt;code&gt;expect('RPAREN')&lt;/code&gt; then found the wrong token. Fixed by always eagerly evaluating both sides before applying the logical operator.&lt;/p&gt;




&lt;h2&gt;
  
  
  By the numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;23 AWS services&lt;/strong&gt; on a single port&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;450 integration tests&lt;/strong&gt;, all passing against the Docker image&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~150 MB&lt;/strong&gt; Docker image, &lt;strong&gt;~30 MB&lt;/strong&gt; memory at idle, &lt;strong&gt;~2s&lt;/strong&gt; startup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack:v1.0.7

&lt;span class="c"&gt;# Or docker-compose&lt;/span&gt;
curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/Nahuel990/ministack/main/docker-compose.yml
docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then point any AWS SDK at &lt;code&gt;http://localhost:4566&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:4566 firehose list-delivery-streams
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker Hub: &lt;a href="https://hub.docker.com/r/nahuelnucera/ministack" rel="noopener noreferrer"&gt;hub.docker.com/r/nahuelnucera/ministack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Full changelog and source: &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;github.com/Nahuel990/ministack&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>MiniStack v1.0.4: API Gateway v2, Lambda Warm Workers, SNS SQS Fanout, State Persistence</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Thu, 26 Mar 2026 09:00:13 +0000</pubDate>
      <link>https://forem.com/nahuel990/ministack-v104-api-gateway-v2-lambda-warm-workers-sns-sqs-fanout-state-persistence-50ac</link>
      <guid>https://forem.com/nahuel990/ministack-v104-api-gateway-v2-lambda-warm-workers-sns-sqs-fanout-state-persistence-50ac</guid>
      <description>&lt;p&gt;&lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;MiniStack&lt;/a&gt; is a free, open-source, MIT-licensed drop-in replacement for LocalStack — 21 AWS services on a single port, no account, no license key, no telemetry.&lt;/p&gt;

&lt;p&gt;v1.0.4 is out today. This post covers everything that landed across the v1.0.2–v1.0.4 wave: the big features, the architectural decisions behind them, and the fixes that got us to a clean 379-test run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why this exists
&lt;/h2&gt;

&lt;p&gt;LocalStack moved its core services behind a paid plan. If you were using LocalStack Community for local dev and CI/CD, you now hit a paywall for S3, SQS, Lambda, and most things that matter.&lt;/p&gt;

&lt;p&gt;MiniStack covers the same surface — and goes further in a few areas — at $0, MIT licensed, forever. The full comparison is on &lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;ministack.org&lt;/a&gt;, but the short version: same developer experience, 150 MB image vs ~1 GB, ~30 MB RAM at idle vs ~500 MB, ~2 s startup vs 15–30 s.&lt;/p&gt;




&lt;h2&gt;
  
  
  What shipped in v1.0.2–v1.0.4
&lt;/h2&gt;

&lt;h3&gt;
  
  
  API Gateway HTTP API v2 — full control plane + data plane
&lt;/h3&gt;

&lt;p&gt;This was the biggest missing piece. v1.0.2 delivered the complete API Gateway v2 implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control plane&lt;/strong&gt; covers the full resource tree:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs: &lt;code&gt;CreateApi&lt;/code&gt;, &lt;code&gt;GetApi&lt;/code&gt;, &lt;code&gt;GetApis&lt;/code&gt;, &lt;code&gt;UpdateApi&lt;/code&gt;, &lt;code&gt;DeleteApi&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Routes: full CRUD, including &lt;code&gt;{param}&lt;/code&gt; path parameter placeholders and &lt;code&gt;{proxy+}&lt;/code&gt; greedy matching&lt;/li&gt;
&lt;li&gt;Integrations: &lt;code&gt;AWS_PROXY&lt;/code&gt; (Lambda) and &lt;code&gt;HTTP_PROXY&lt;/code&gt; (arbitrary HTTP backends)&lt;/li&gt;
&lt;li&gt;Stages, Deployments, Authorizers (JWT + Lambda), Tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Data plane&lt;/strong&gt; is the part most emulators skip. In MiniStack, &lt;code&gt;execute-api&lt;/code&gt; requests are dispatched by host header — &lt;code&gt;{apiId}.execute-api.localhost&lt;/code&gt; — before normal service routing. That means your actual API calls hit the right Lambda function with a proper payload format 2.0 envelope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;apigw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apigatewayv2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apigw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProtocolType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;api_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ApiId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;apigw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ApiId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RouteKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET /hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;apigw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_integration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ApiId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IntegrationType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS_PROXY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IntegrationUri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:lambda:us-east-1:000000000000:function:my-func&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PayloadFormatVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.execute-api.localhost:4566/hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Route matching supports path parameters and &lt;code&gt;{proxy+}&lt;/code&gt; greedy segments. There was a subtle bug in the initial implementation: &lt;code&gt;re.escape&lt;/code&gt; was applied before &lt;code&gt;{param}&lt;/code&gt; substitution, which meant every parameterised route silently fell through to a 404. Fixed in v1.0.2.&lt;/p&gt;

&lt;p&gt;Authorizers ship with full CRUD — JWT and Lambda authorizer types, with &lt;code&gt;identitySource&lt;/code&gt; stored and returned as an array of strings matching the AWS spec (was incorrectly a single string in the initial cut).&lt;/p&gt;




&lt;h3&gt;
  
  
  Lambda warm/cold start worker pool
&lt;/h3&gt;

&lt;p&gt;Prior to v1.0.2, every Lambda invocation spawned a fresh subprocess. That's fine for correctness but misses a whole class of bugs that only appear when your handler module is imported once and invoked repeatedly.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;core/lambda_runtime.py&lt;/code&gt; now maintains a persistent Python subprocess per function. The handler module is imported on first invocation (cold start), and subsequent calls reuse the warm worker without re-importing. Workers respawn automatically on crash.&lt;/p&gt;

&lt;p&gt;This matters for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Correctness&lt;/strong&gt; — module-level state (caches, connection pools, counters) behaves the same as in real Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt; — warm invocations are significantly faster in CI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bug surface&lt;/strong&gt; — you'll catch issues with global state that a fresh-subprocess model would mask
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buckets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_buckets&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Buckets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In MiniStack, that &lt;code&gt;_client&lt;/code&gt; initialization happens exactly once per function, just like AWS.&lt;/p&gt;




&lt;h3&gt;
  
  
  SNS → SQS fanout and SNS → Lambda dispatch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sqs&lt;/code&gt; protocol subscriptions now deliver messages directly to SQS queues with the standard SNS JSON notification envelope. &lt;code&gt;lambda&lt;/code&gt; protocol subscriptions invoke the Lambda function via &lt;code&gt;_execute_function()&lt;/code&gt; with a &lt;code&gt;Records[].Sns&lt;/code&gt; event structure. Both were stubs in v1.0.1 — SNS→SQS was wired in v1.0.2, SNS→Lambda was a no-op that got fixed in the same release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_topic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TopicArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;consumer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;queue_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;queue_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_queue_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AttributeNames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TopicArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_arn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TopicArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello fanout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;msgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# hello fanout
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fanout is synchronous within the same process — consistent with how LocalStack handles it and good enough for unit/integration testing.&lt;/p&gt;




&lt;h3&gt;
  
  
  SQS → Lambda event source mapping
&lt;/h3&gt;

&lt;p&gt;Full ESM lifecycle: &lt;code&gt;CreateEventSourceMapping&lt;/code&gt;, &lt;code&gt;DeleteEventSourceMapping&lt;/code&gt;, &lt;code&gt;GetEventSourceMapping&lt;/code&gt;, &lt;code&gt;ListEventSourceMappings&lt;/code&gt;, &lt;code&gt;UpdateEventSourceMapping&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A background poller picks up messages from the configured SQS queue and delivers them to the target Lambda as batched events. Batch size and enabled state are configurable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;lam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;queue_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trigger-queue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;queue_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_queue_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AttributeNames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QueueArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;lam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_event_source_mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;EventSourceArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-processor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;BatchSize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MessageBody&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order_placed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  State persistence
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PERSIST_STATE=1&lt;/code&gt; enables atomic state snapshots to disk. Writes go to a temp file first, then rename — so a crash mid-write doesn't corrupt the saved state. &lt;code&gt;STATE_DIR&lt;/code&gt; controls where (default &lt;code&gt;/tmp/ministack-state&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;API Gateway state is fully persisted across container restarts. The persistence framework is designed so other services can adopt the same &lt;code&gt;get_state()&lt;/code&gt; / &lt;code&gt;load_persisted_state()&lt;/code&gt; pattern incrementally.&lt;/p&gt;

&lt;p&gt;The reset endpoint (&lt;code&gt;POST /_ministack/reset&lt;/code&gt;) clears in-memory state and, when &lt;code&gt;PERSIST_STATE=1&lt;/code&gt;, also deletes &lt;code&gt;STATE_DIR/*.json&lt;/code&gt; and &lt;code&gt;S3_DATA_DIR&lt;/code&gt; contents — so a restart doesn't reload stale state. Also used in the test suite as a session-scoped &lt;code&gt;autouse&lt;/code&gt; fixture to keep tests idempotent across repeated runs.&lt;/p&gt;




&lt;h3&gt;
  
  
  DynamoDB TTL enforcement
&lt;/h3&gt;

&lt;p&gt;TTL was previously stored — &lt;code&gt;TimeToLiveSpecification&lt;/code&gt; accepted and returned without error — but never acted on. Items just sat there forever. A background daemon thread (&lt;code&gt;dynamodb-ttl-reaper&lt;/code&gt;) now scans all tables every 60 seconds and deletes items whose TTL attribute value is ≤ current epoch time.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lambda Function URLs
&lt;/h3&gt;

&lt;p&gt;Full CRUD: &lt;code&gt;CreateFunctionUrlConfig&lt;/code&gt;, &lt;code&gt;GetFunctionUrlConfig&lt;/code&gt;, &lt;code&gt;UpdateFunctionUrlConfig&lt;/code&gt;, &lt;code&gt;DeleteFunctionUrlConfig&lt;/code&gt;, &lt;code&gt;ListFunctionUrlConfigs&lt;/code&gt;. State persisted in &lt;code&gt;_function_urls&lt;/code&gt;. Was a 404 stub before.&lt;/p&gt;




&lt;h3&gt;
  
  
  SQS queue URL portability (v1.0.4)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;QueueUrl&lt;/code&gt; values now read &lt;code&gt;MINISTACK_HOST&lt;/code&gt; and &lt;code&gt;GATEWAY_PORT&lt;/code&gt; env vars instead of hardcoding &lt;code&gt;localhost:4566&lt;/code&gt;. This was causing silent failures in CI environments where MiniStack runs as a service container with a network alias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ministack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nahuelnucera/ministack&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MINISTACK_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ministack&lt;/span&gt;
      &lt;span class="na"&gt;GATEWAY_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4566&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AWS_ENDPOINT_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://ministack:4566&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before this fix, queue URLs returned &lt;code&gt;http://localhost:4566/...&lt;/code&gt; regardless of configuration, and any SQS operation using that URL from another container would fail.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bug fixes worth calling out
&lt;/h2&gt;

&lt;p&gt;Several of these look fine in smoke tests but break real workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;execute-api&lt;/code&gt; credential scope misrouted to &lt;code&gt;lambda&lt;/code&gt;&lt;/strong&gt; — In &lt;code&gt;core/router.py&lt;/code&gt;, the credential scope for execute-api requests was mapped to &lt;code&gt;lambda&lt;/code&gt; instead of &lt;code&gt;apigateway&lt;/code&gt;. Any data plane request was dispatched to the Lambda handler instead of the API Gateway data plane. Fixed in v1.0.2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;routeKey&lt;/code&gt; always &lt;code&gt;"$default"&lt;/code&gt; in Lambda proxy events&lt;/strong&gt; — The &lt;code&gt;routeKey&lt;/code&gt; field was hardcoded to &lt;code&gt;"$default"&lt;/code&gt; regardless of which route was matched. If your handler branched on &lt;code&gt;event["routeKey"]&lt;/code&gt; — common in single-function routers — every request hit the default branch. Fixed to reflect the actual matched route key (e.g. &lt;code&gt;"GET /users/{id}"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;identitySource&lt;/code&gt; wrong type on Authorizers&lt;/strong&gt; — Stored and returned as a plain string instead of an array of strings. boto3 raised a shape validation error on the response. Now returns &lt;code&gt;["$request.header.Authorization"]&lt;/code&gt; as the AWS spec requires.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;DeleteFunctionUrlConfig&lt;/code&gt; returning &lt;code&gt;{}&lt;/code&gt; body on 204&lt;/strong&gt; — boto3 expects an empty body on 204. The non-empty body caused a &lt;code&gt;RemoteDisconnected&lt;/code&gt; exception. Fixed to return a true empty 204.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/_ministack/reset&lt;/code&gt; not cleaning disk&lt;/strong&gt; — When &lt;code&gt;PERSIST_STATE=1&lt;/code&gt;, reset cleared in-memory state but left &lt;code&gt;STATE_DIR/*.json&lt;/code&gt; and &lt;code&gt;S3_DATA_DIR&lt;/code&gt; intact. On the next restart, old state was reloaded. Fixed to delete persisted files as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test idempotency&lt;/strong&gt; — Tests with hardcoded resource names failed on a second run because the resources already existed. Fixed by wiring &lt;code&gt;POST /_ministack/reset&lt;/code&gt; into a session-scoped &lt;code&gt;autouse&lt;/code&gt; fixture in &lt;code&gt;conftest.py&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test suite: 54 → 379
&lt;/h2&gt;

&lt;p&gt;v1.0.1 shipped with 54 integration tests. v1.0.4 has 379, all passing against both the in-process server and the Docker image.&lt;/p&gt;

&lt;p&gt;The growth came from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 new tests covering previously untested paths: health endpoint, STS &lt;code&gt;GetSessionToken&lt;/code&gt;, DynamoDB TTL, Lambda warm start, API Gateway execute-api Lambda proxy, &lt;code&gt;$default&lt;/code&gt; catch-all, path parameter matching, 404 on missing route, EventBridge → Lambda dispatch&lt;/li&gt;
&lt;li&gt;25 new tests for operations added post-v1.0.1: Kinesis shard split/merge, SSM label/tag operations, CloudWatch Logs retention/subscription/metric filters/Insights, CloudWatch composite alarms, EventBridge archives/permissions, DynamoDB &lt;code&gt;UpdateTable&lt;/code&gt;, S3 versioning/encryption/lifecycle/CORS/ACL, Athena workgroup and batch operations&lt;/li&gt;
&lt;li&gt;Full coverage of all v1.0.2 features: API Gateway control + data plane, SNS→SQS fanout, SQS→Lambda ESM, Lambda warm start, persistence, reset&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tests run against the Docker image — no mocking, no monkeypatching, real boto3 calls over the wire.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's still on the roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API Gateway REST API (v1)&lt;/li&gt;
&lt;li&gt;Cognito — user pools, sign-up/sign-in&lt;/li&gt;
&lt;li&gt;Route53 — hosted zones, record sets&lt;/li&gt;
&lt;li&gt;ACM — certificate management&lt;/li&gt;
&lt;li&gt;Firehose&lt;/li&gt;
&lt;li&gt;Virtual-hosted style S3 (&lt;code&gt;bucket.localhost:4566&lt;/code&gt; routing)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 nahuelnucera/ministack

&lt;span class="c"&gt;# With persistence and Redis sidecar&lt;/span&gt;
git clone https://github.com/Nahuel990/ministack
&lt;span class="nb"&gt;cd &lt;/span&gt;ministack
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

curl http://localhost:4566/_localstack/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works with boto3, AWS CLI, Terraform, CDK, Pulumi. Point &lt;code&gt;endpoint_url&lt;/code&gt; at &lt;code&gt;http://localhost:4566&lt;/code&gt;, credentials &lt;code&gt;test&lt;/code&gt;/&lt;code&gt;test&lt;/code&gt;, and you're done.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;github.com/Nahuel990/ministack&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Docker Hub:&lt;/strong&gt; &lt;a href="https://hub.docker.com/r/nahuelnucera/ministack" rel="noopener noreferrer"&gt;hub.docker.com/r/nahuelnucera/ministack&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Site:&lt;/strong&gt; &lt;a href="https://ministack.org" rel="noopener noreferrer"&gt;ministack.org&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stars, issues, and PRs welcome. Each service is a single self-contained Python file — adding a new one is straightforward.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>localstack</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
