<?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>DEV Community: Lucas Vieira</title>
    <description>The latest articles on DEV Community by Lucas Vieira (@vieiralucas).</description>
    <link>https://dev.to/vieiralucas</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%2F11402%2F7764293.jpeg</url>
      <title>DEV Community: Lucas Vieira</title>
      <link>https://dev.to/vieiralucas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vieiralucas"/>
    <language>en</language>
    <item>
      <title>Testing Bedrock Guardrails without the AWS bill</title>
      <dc:creator>Lucas Vieira</dc:creator>
      <pubDate>Sat, 25 Apr 2026 19:23:26 +0000</pubDate>
      <link>https://dev.to/vieiralucas/testing-bedrock-guardrails-without-the-aws-bill-38a8</link>
      <guid>https://dev.to/vieiralucas/testing-bedrock-guardrails-without-the-aws-bill-38a8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://fakecloud.dev/blog/bedrock-guardrails-local/" rel="noopener noreferrer"&gt;fakecloud.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Bedrock Guardrails are one of the better pieces of AWS infrastructure in the last year. You define a policy — filter out hate, block PII leakage, deny certain topics, enforce contextual grounding on RAG outputs — and either apply it to an entire model invocation via &lt;code&gt;guardrailIdentifier&lt;/code&gt;, or call &lt;code&gt;ApplyGuardrail&lt;/code&gt; directly on arbitrary text. Useful, sharp, limited-in-scope.&lt;/p&gt;

&lt;p&gt;The problem is testing. A serious guardrail config is an iterative thing — you tune filter strengths, add denied topics, tweak word filters, and test each change. Each test-cycle turn against real AWS costs tokens and time. And if the thing you're trying to guard against is LLM output, you're paying for the model call &lt;em&gt;and&lt;/em&gt; the guardrail evaluation.&lt;/p&gt;

&lt;p&gt;fakecloud runs the full Bedrock Guardrails surface locally. &lt;code&gt;CreateGuardrail&lt;/code&gt;, versioning, &lt;code&gt;UpdateGuardrail&lt;/code&gt;, &lt;code&gt;DeleteGuardrail&lt;/code&gt;, &lt;code&gt;ApplyGuardrail&lt;/code&gt;, &lt;code&gt;ListGuardrails&lt;/code&gt; — the whole thing. More interestingly, the data plane actually evaluates content. PII detection works. Content filters return sensible scores on real test text. You can write tests that verify your guardrail actually blocks what you think it blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of the problem
&lt;/h2&gt;

&lt;p&gt;The standard AWS recipe, cleaned up:&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;bedrock&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;bedrock&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;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_guardrail&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;customer-facing-v1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;contentPolicyConfig&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;filtersConfig&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&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;HATE&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;inputStrength&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;HIGH&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;outputStrength&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;HIGH&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;type&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;VIOLENCE&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;inputStrength&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;HIGH&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;outputStrength&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;HIGH&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;type&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;SEXUAL&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;inputStrength&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;HIGH&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;outputStrength&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;HIGH&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;type&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;INSULTS&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;inputStrength&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;MEDIUM&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;outputStrength&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;MEDIUM&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="n"&gt;sensitiveInformationPolicyConfig&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;piiEntitiesConfig&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&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;action&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;BLOCK&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;type&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;PHONE&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;action&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;BLOCK&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;type&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;CREDIT_DEBIT_CARD_NUMBER&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;action&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;BLOCK&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="n"&gt;topicPolicyConfig&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;topicsConfig&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="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;medical-advice&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;definition&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;Providing specific medical diagnoses or treatment recommendations&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;examples&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;What dose should I take?&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;Do I have cancer?&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;type&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;DENY&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="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;blockedInputMessaging&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Cannot process this input.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;blockedOutputsMessaging&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Cannot respond to this.&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;Now you want to test: does an email containing &lt;code&gt;bob@example.com&lt;/code&gt; trigger the EMAIL filter and return the blocked-input message? Does a prompt asking "what medication should I take for my headache" get denied via the medical-advice topic? Does the HATE filter catch slurs at HIGH strength?&lt;/p&gt;

&lt;p&gt;Against real AWS: each of those is an API call that costs a few cents, takes a second, and requires valid credentials. You'll do 50 of these across a tuning session. Repeated for each PR that touches guardrail config.&lt;/p&gt;

&lt;h2&gt;
  
  
  Against fakecloud
&lt;/h2&gt;

&lt;p&gt;Same code, different endpoint:&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;bedrock&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;bedrock&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;# same create_guardrail call as above
&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_guardrail&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;

&lt;span class="n"&gt;rt&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;bedrock-runtime&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;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_guardrail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;guardrailIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;guardrailId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;guardrailVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DRAFT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;INPUT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&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;text&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;text&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;contact me at bob@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="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;action&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GUARDRAIL_INTERVENED&lt;/span&gt;&lt;span class="sh"&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;outputs&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;text&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Cannot process this input.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&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="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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&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;assessments&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;sensitiveInformationPolicy&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;piiEntities&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;Real PII detection — the email is caught. Real blocked-input messaging returned. Assessments returned in the shape real AWS would return.&lt;/p&gt;

&lt;p&gt;No tokens. No AWS bill. No credentials. 30ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually evaluated
&lt;/h2&gt;

&lt;p&gt;fakecloud's guardrail evaluator ships opinionated implementations of each policy type. Concrete checks you can rely on in tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PII entities&lt;/strong&gt;: regex + context for email, phone (various formats), credit card (Luhn), SSN, IP, URL, name, address hints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content filters&lt;/strong&gt;: keyword + pattern matching tuned for the common categories. HIGH strength catches more, LOW catches less — same directional behavior as real AWS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Topic filters&lt;/strong&gt;: pattern matching against the topic definition + examples you provided. Won't pass a Stanford NLI benchmark, but does what you need for testing: asserts the policy fires on obvious positives and passes obvious negatives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Word filters&lt;/strong&gt;: exact match + stemming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex filters&lt;/strong&gt;: the regex you supplied, as-is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's shape-correct evaluation. For tests that verify "my guardrail config blocks what I intended," that's exactly what you need. If you want bulletproof production-grade content filtering, you're going to tune against real AWS or roll your own anyway — this gets you 95% of the iteration speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# publish a version
&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_guardrail_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;guardrailIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;guardrailId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;initial pinned version for prod&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;# list versions
&lt;/span&gt;&lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_guardrails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guardrailIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;guardrailId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# apply a specific version in production
&lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_guardrail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;guardrailIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;guardrailId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;guardrailVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# '1'
&lt;/span&gt;    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;INPUT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&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;text&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;text&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;...&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;DRAFT is mutable. Numbered versions (1, 2, 3, ...) are immutable once created — same as real AWS. Your prod code should pin. Your dev iteration happens against DRAFT.&lt;/p&gt;

&lt;h2&gt;
  
  
  A realistic test suite
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FakeCloud&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fakecloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplyGuardrailCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-bedrock-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BedrockClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CreateGuardrailCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-bedrock&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fc&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;FakeCloud&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;control&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;BedrockClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rt&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;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;guardrailId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreateGuardrailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test-guardrail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentPolicyConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;filtersConfig&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HATE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inputStrength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HIGH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;outputStrength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HIGH&lt;/span&gt;&lt;span class="dl"&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="na"&gt;sensitiveInformationPolicyConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;piiEntitiesConfig&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EMAIL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BLOCK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PHONE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ANONYMIZE&lt;/span&gt;&lt;span class="dl"&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="na"&gt;blockedInputMessaging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Blocked.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;blockedOutputsMessaging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Blocked.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="nx"&gt;guardrailId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guardrailId&lt;/span&gt;&lt;span class="o"&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;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;preserve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bedrock:guardrails&lt;/span&gt;&lt;span class="dl"&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blocks input containing email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApplyGuardrailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;guardrailIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;guardrailId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;guardrailVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DRAFT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INPUT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contact me at alice@example.com&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GUARDRAIL_INTERVENED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputs&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Blocked.&lt;/span&gt;&lt;span class="dl"&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;anonymizes phone in output&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApplyGuardrailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;guardrailIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;guardrailId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;guardrailVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DRAFT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OUTPUT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;call me at 555-123-4567 tomorrow&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GUARDRAIL_INTERVENED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputs&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;555-123-4567&lt;/span&gt;&lt;span class="dl"&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;passes clean input through&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApplyGuardrailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;guardrailIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;guardrailId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;guardrailVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DRAFT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INPUT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;what's the weather in Paris?&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NONE&lt;/span&gt;&lt;span class="dl"&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;These run in milliseconds, are deterministic, and test the thing that matters — that your guardrail config enforces what you think it enforces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for agent systems
&lt;/h2&gt;

&lt;p&gt;Guardrails become critical when you're building tool-using agents. A tool call that leaks customer PII is a real incident; a model output that contains violence content is a real incident. You want those failure modes caught in CI, not in production.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Compose the guardrail config as code (Terraform / CDK) alongside your agent definition.&lt;/li&gt;
&lt;li&gt;In CI, deploy the guardrail to fakecloud, run a test matrix of adversarial inputs through your agent, assert that the guardrail fires (or doesn't fire) where expected.&lt;/li&gt;
&lt;li&gt;On merge to main, deploy the same config to real AWS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two steps cost nothing. The third is the one that actually consumes budget.&lt;/p&gt;

&lt;p&gt;This is the test loop real AWS makes nearly impossible — not because of technical obstacles, but because the cost-per-iteration is too high for the number of iterations you actually need.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock emulator&lt;/strong&gt; (landing): &lt;a href="https://fakecloud.dev/bedrock-emulator/" rel="noopener noreferrer"&gt;fakecloud.dev/bedrock-emulator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Bedrock locally&lt;/strong&gt;: &lt;a href="https://fakecloud.dev/test-bedrock-locally/" rel="noopener noreferrer"&gt;fakecloud.dev/test-bedrock-locally&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why not a real LLM in tests&lt;/strong&gt;: &lt;a href="https://fakecloud.dev/blog/bedrock-tests-real-llm/" rel="noopener noreferrer"&gt;fakecloud.dev/blog/bedrock-tests-real-llm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing LLM guardrails&lt;/strong&gt; (broader view): &lt;a href="https://fakecloud.dev/blog/llm-guardrails/" rel="noopener noreferrer"&gt;fakecloud.dev/blog/llm-guardrails&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/faiscadev/fakecloud" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>bedrock</category>
      <category>testing</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Free LocalStack alternatives in 2026: fakecloud, MiniStack, floci, Moto</title>
      <dc:creator>Lucas Vieira</dc:creator>
      <pubDate>Fri, 24 Apr 2026 12:44:54 +0000</pubDate>
      <link>https://dev.to/vieiralucas/free-localstack-alternatives-in-2026-fakecloud-ministack-floci-moto-4h78</link>
      <guid>https://dev.to/vieiralucas/free-localstack-alternatives-in-2026-fakecloud-ministack-floci-moto-4h78</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://fakecloud.dev/blog/localstack-alternatives-compared/" rel="noopener noreferrer"&gt;fakecloud.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;LocalStack's Community Edition went proprietary in March 2026. Since then a few free, open-source alternatives have surfaced or gained momentum. This post covers the four that keep coming up — fakecloud, MiniStack, floci, and Moto — and what each one is, architecturally.&lt;/p&gt;

&lt;p&gt;Upfront disclosure: I maintain fakecloud. Bias declared. What this post avoids: a head-to-head scorecard with numbers I haven't personally measured on all four. That kind of comparison gets out of date in days and reads as marketing when the numbers are favorable to the author. Instead: what each project is, the design choices it's made, and how to think about the fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architectural split that matters
&lt;/h2&gt;

&lt;p&gt;Two ways to emulate AWS for tests:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. In-process library that patches the SDK.&lt;/strong&gt; Import a library, decorate your test, library intercepts SDK calls inside your test process. Moto works this way. Fast, no external process, trivial Python setup. Does not speak the AWS wire protocol. Cannot run real Lambda code, real databases, or real Redis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Real HTTP server speaking the AWS wire protocol.&lt;/strong&gt; Separate process on port 4566. SDK points at it via &lt;code&gt;endpoint_url&lt;/code&gt;. LocalStack, MiniStack, floci, and fakecloud all work this way. Any AWS SDK in any language works. Cross-service wiring runs server-side.&lt;/p&gt;

&lt;p&gt;If your tests are "does my Python function call boto3 correctly," Moto. If your tests are "does my whole system actually work end-to-end," HTTP server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two approaches among the HTTP servers
&lt;/h2&gt;

&lt;p&gt;Among the HTTP emulators there's another split that matters, especially for AI-assisted dev and real integration tests:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breadth-first:&lt;/strong&gt; ship a large catalog of AWS services fast, with partial/surface coverage on each — enough to accept the common calls and return plausible shapes. Good when your tests lean lightly on many services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Depth-first:&lt;/strong&gt; ship fewer services but at 100% behavioral parity, with real code execution, real stateful backends, and real cross-service wire-ups. Good when your tests actually exercise cross-service flows, or when you want an AI coding agent to be able to rely on the emulator behaving like AWS.&lt;/p&gt;

&lt;p&gt;These are philosophies, not rankings. Different workloads prefer different ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  fakecloud
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; single static binary written in Rust, ~19 MB. Speaks the AWS wire protocol on port 4566. No account, no token, no paid tier. AGPL-3.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach:&lt;/strong&gt; depth-first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit goal:&lt;/strong&gt; 100% of AWS services, each at 100% behavioral conformance with 100% of cross-service integrations. Services are added one at a time; a service lands when it passes the full Smithy-model test variants and the cross-service wire-ups that matter for it — not when the API surface looks filled in. The roadmap is driven by real-project demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it covers today:&lt;/strong&gt; 23 services, 1,680 operations: S3, SQS, SNS, DynamoDB, Lambda, IAM, STS, KMS, Secrets Manager, SSM, CloudWatch Logs, CloudFormation, EventBridge, EventBridge Scheduler, SES (v2 + v1 inbound), Cognito User Pools, Kinesis, RDS, ElastiCache, Step Functions, API Gateway v2, Bedrock, Bedrock Runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distinctive choices driven by the depth-first goal:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real Lambda execution across 13 runtimes in Docker containers — your function code runs, not a stub.&lt;/li&gt;
&lt;li&gt;Real stateful backends: RDS runs real PostgreSQL/MySQL/MariaDB via Docker; ElastiCache runs real Redis/Valkey.&lt;/li&gt;
&lt;li&gt;Cross-service wiring that actually fires: EventBridge -&amp;gt; Step Functions, S3 -&amp;gt; Lambda, SES inbound -&amp;gt; S3/SNS/Lambda, 15+ more integrations.&lt;/li&gt;
&lt;li&gt;Multi-account, SCPs, ABAC, permission boundaries, session policies, KMS key policies, bucket policies — full Allow/Deny/NotPrincipal semantics.&lt;/li&gt;
&lt;li&gt;First-party test-assertion SDKs in TypeScript, Python, Go, PHP, Java, Rust.&lt;/li&gt;
&lt;li&gt;Conformance validated on every commit against 54,000+ Smithy-generated test variants, plus the upstream &lt;code&gt;hashicorp/terraform-provider-aws&lt;/code&gt; &lt;code&gt;TestAcc*&lt;/code&gt; suites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/faiscadev/fakecloud" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;A free LocalStack alternative that surfaced during the March 2026 paywall window. Check the repo for current service coverage, architecture, and approach — the project moves quickly, so numbers in any blog post will be stale within weeks. Evaluate on your test suite's actual needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  floci
&lt;/h2&gt;

&lt;p&gt;Another free LocalStack alternative from the same window. Landing page publishes performance claims — verify them against the version you'd actually run before relying on the numbers. Evaluate on your test suite's actual needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moto
&lt;/h2&gt;

&lt;p&gt;Longest-lived open-source AWS mocking project. Python library, in-process, patches &lt;code&gt;boto3&lt;/code&gt;. Broad service surface at varying depth. Excellent for Python unit tests where you want boto3 to respond plausibly inside your test process. Not usable from other languages, does not run Lambda code, does not talk to Terraform/CDK deploys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/getmoto/moto" rel="noopener noreferrer"&gt;github.com/getmoto/moto&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  LocalStack Pro (for completeness)
&lt;/h2&gt;

&lt;p&gt;Paid tier, mature catalog, commercial support, proprietary license. Fits teams OK with proprietary and per-seat pricing who need the whole LocalStack catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to pick
&lt;/h2&gt;

&lt;p&gt;Avoid the temptation to pick by "which has more services." All four of the HTTP emulators (fakecloud, MiniStack, floci, LocalStack Pro) are moving targets; service counts change week to week. The durable questions are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do your tests exercise cross-service flows?&lt;/strong&gt; (S3 triggers Lambda, EventBridge routes to Step Functions, SES inbound writes to S3, etc.) If yes, depth-first matters more than breadth. The emulator needs to actually &lt;em&gt;wire&lt;/em&gt; those flows, not just accept each call in isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you need real code execution?&lt;/strong&gt; (Does your Lambda function actually run, or do you just need a response shape?) fakecloud runs Lambda code in real Docker runtimes; some alternatives return synthetic responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you need real stateful backends?&lt;/strong&gt; (Does your Postgres schema matter? Your Redis data structures?) fakecloud runs real PostgreSQL/MySQL/MariaDB/Redis/Valkey; alternatives vary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What language are your tests?&lt;/strong&gt; Moto is Python-only. Everything else is language-agnostic over HTTP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How much of AWS does your test mix actually hit?&lt;/strong&gt; Open your test suite. Count unique AWS services called. That number is almost always smaller than "total AWS services" and is the only count that matters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run your real test suite against each option you're considering. That's the only benchmark that applies to your codebase.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;fakecloud: &lt;a href="https://github.com/faiscadev/fakecloud" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Moto: &lt;a href="https://github.com/getmoto/moto" rel="noopener noreferrer"&gt;github.com/getmoto/moto&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LocalStack: &lt;a href="https://localstack.cloud" rel="noopener noreferrer"&gt;localstack.cloud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;fakecloud install options: &lt;a href="https://fakecloud.dev/docs/getting-started/" rel="noopener noreferrer"&gt;fakecloud.dev/docs/getting-started&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;fakecloud migration guide: &lt;a href="https://fakecloud.dev/blog/migrate-from-localstack/" rel="noopener noreferrer"&gt;Migrating from LocalStack to fakecloud&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>testing</category>
      <category>localstack</category>
      <category>opensource</category>
    </item>
    <item>
      <title>LocalStack Ultimate covers 4 Bedrock ops. Here's why that's the wrong bet for tests.</title>
      <dc:creator>Lucas Vieira</dc:creator>
      <pubDate>Thu, 23 Apr 2026 18:19:44 +0000</pubDate>
      <link>https://dev.to/vieiralucas/localstack-ultimate-covers-4-bedrock-ops-heres-why-thats-the-wrong-bet-for-tests-2dga</link>
      <guid>https://dev.to/vieiralucas/localstack-ultimate-covers-4-bedrock-ops-heres-why-thats-the-wrong-bet-for-tests-2dga</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://fakecloud.dev/blog/localstack-ultimate-bedrock/" rel="noopener noreferrer"&gt;fakecloud.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've been looking for a local Bedrock emulator and landed on LocalStack's documentation, you've seen this: Bedrock is available, but only on the Ultimate plan, and only four operations are supported. Those four operations are backed by &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, the local-LLM runtime.&lt;/p&gt;

&lt;p&gt;This is a real product decision, not an accident. It's worth understanding what it gets you, what it doesn't, and why fakecloud took a very different approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  What LocalStack Ultimate gives you for Bedrock
&lt;/h2&gt;

&lt;p&gt;Per &lt;a href="https://docs.localstack.cloud/aws/services/bedrock/" rel="noopener noreferrer"&gt;docs.localstack.cloud/aws/services/bedrock&lt;/a&gt;, as of April 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tier&lt;/strong&gt;: Ultimate plan only. This is the top paid tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations&lt;/strong&gt;: &lt;code&gt;InvokeModel&lt;/code&gt;, &lt;code&gt;Converse&lt;/code&gt;, &lt;code&gt;ListFoundationModels&lt;/code&gt;, &lt;code&gt;CreateModelInvocationJob&lt;/code&gt;. Four, total.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Ollama, called via its HTTP API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Models&lt;/strong&gt;: whatever Ollama supports — Llama, Mistral, Phi, etc. You call them via &lt;code&gt;ollama.&amp;lt;model-id&amp;gt;&lt;/code&gt; strings through the Bedrock SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitations per the docs&lt;/strong&gt;: only text models, no GPU, no persistence, and responsiveness issues on Docker Desktop due to storage/memory constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole product. The "Bedrock" you're calling in your code is a LocalStack process forwarding your request to Ollama, which runs a model like Llama 3 on your CPU or GPU, and returns the response in a Bedrock-shaped envelope.&lt;/p&gt;

&lt;h2&gt;
  
  
  What that actually means at the test bench
&lt;/h2&gt;

&lt;p&gt;Let's be concrete. You're writing tests for code that calls &lt;code&gt;anthropic.claude-3-haiku-20240307-v1:0&lt;/code&gt; (Haiku). You point your SDK at LocalStack Ultimate's Bedrock. What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Haiku doesn't exist locally. You have to set &lt;code&gt;DEFAULT_BEDROCK_MODEL=ollama.llama3.2&lt;/code&gt; (or whichever Ollama model you have installed).&lt;/li&gt;
&lt;li&gt;Your code's &lt;code&gt;modelId: "anthropic.claude-3-haiku-20240307-v1:0"&lt;/code&gt; string gets routed to Llama 3.2.&lt;/li&gt;
&lt;li&gt;Your prompt, which was tuned for Claude, runs through Llama, which interprets it differently.&lt;/li&gt;
&lt;li&gt;The response comes back. Llama's output goes through Bedrock's response envelope shape.&lt;/li&gt;
&lt;li&gt;Your code parses the envelope, extracts the text, and... what?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your code is doing structured extraction (return JSON matching schema X), Llama and Claude don't produce identical JSON even at temperature 0. If your code is doing classification, they disagree on edge cases. If your code is doing agent work with tool use, they have different tool-use protocols entirely (Claude's tool-use format differs from Llama's function-calling format).&lt;/p&gt;

&lt;p&gt;You are not testing your Claude code. You are testing whether your Claude code happens to also work against Llama when shimmed through a Bedrock envelope.&lt;/p&gt;

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

&lt;p&gt;Ollama on CPU: 1-30 seconds per call depending on model and prompt length.&lt;/p&gt;

&lt;p&gt;Ollama on GPU (if you have one): 100ms-3s per call.&lt;/p&gt;

&lt;p&gt;Bedrock on real AWS: 100-2000ms per call.&lt;/p&gt;

&lt;p&gt;The "local" Bedrock is often slower than the cloud one. And you get non-determinism either way.&lt;/p&gt;

&lt;p&gt;The LocalStack docs explicitly mention &lt;code&gt;BEDROCK_PREWARM&lt;/code&gt; to reduce startup delays, which is an acknowledgment of the pain — you're essentially waiting for Ollama to load a multi-GB model into memory before your test suite can start. CI runners without &lt;code&gt;--shm-size&lt;/code&gt; tuning routinely OOM during this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four operations
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;InvokeModel&lt;/code&gt; and &lt;code&gt;Converse&lt;/code&gt; are the data plane — the calls your application makes at runtime. That's the part Ollama can back.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ListFoundationModels&lt;/code&gt; returns which models exist.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CreateModelInvocationJob&lt;/code&gt; starts an async batch job against S3 input. This is partial support (based on what the docs describe) — it's the only control-plane operation covered.&lt;/p&gt;

&lt;p&gt;What's not covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guardrails&lt;/li&gt;
&lt;li&gt;Custom models / customization jobs&lt;/li&gt;
&lt;li&gt;Model import&lt;/li&gt;
&lt;li&gt;Inference profiles&lt;/li&gt;
&lt;li&gt;Provisioned throughput&lt;/li&gt;
&lt;li&gt;Model copy jobs&lt;/li&gt;
&lt;li&gt;Prompt management&lt;/li&gt;
&lt;li&gt;Foundation model agreements&lt;/li&gt;
&lt;li&gt;Resource policies&lt;/li&gt;
&lt;li&gt;Logging configuration&lt;/li&gt;
&lt;li&gt;Evaluation jobs&lt;/li&gt;
&lt;li&gt;Marketplace&lt;/li&gt;
&lt;li&gt;Automated reasoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your test code touches anything in that list — and any serious Bedrock integration touches several of them — LocalStack Ultimate's Bedrock emulation doesn't help.&lt;/p&gt;

&lt;h2&gt;
  
  
  What fakecloud does differently
&lt;/h2&gt;

&lt;p&gt;fakecloud covers 111 Bedrock operations. The data plane (InvokeModel, Converse, streaming variants) is not backed by a real LLM — it returns exactly the responses you configure, per-prompt rule.&lt;/p&gt;

&lt;p&gt;This is the deliberate opposite of LocalStack Ultimate's choice:&lt;/p&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 Ultimate Bedrock&lt;/th&gt;
&lt;th&gt;fakecloud Bedrock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pricing&lt;/td&gt;
&lt;td&gt;Top-tier paid plan&lt;/td&gt;
&lt;td&gt;Free, AGPL-3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data plane&lt;/td&gt;
&lt;td&gt;Ollama (real local LLM)&lt;/td&gt;
&lt;td&gt;Configurable responses per prompt rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Determinism&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;Speed&lt;/td&gt;
&lt;td&gt;1-30s per call&lt;/td&gt;
&lt;td&gt;Milliseconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guardrails&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Full CRUD + real content evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom models&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Full control plane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async batch&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Full flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt management&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Prompts + prompt routers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU required&lt;/td&gt;
&lt;td&gt;Usually&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;"Not supported" (from docs)&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;
  
  
  Why Ollama is the wrong backing for a Bedrock emulator
&lt;/h2&gt;

&lt;p&gt;The word "emulator" and the word "Bedrock" together set an expectation: I can run a simulation of Bedrock locally. People reach for that when they want to test their Bedrock-calling code.&lt;/p&gt;

&lt;p&gt;Testing code that calls an LLM API and testing an LLM are different problems. The first wants determinism, speed, fault injection, and call-history introspection. The second wants a real model.&lt;/p&gt;

&lt;p&gt;LocalStack's Ultimate Bedrock chose the second. That decision makes sense if your goal is "let me prototype against a Bedrock SDK without burning tokens." It's the wrong decision if your goal is "let me have fast, deterministic integration tests of my production Bedrock code."&lt;/p&gt;

&lt;p&gt;fakecloud picked the first job and optimized hard for it. If you want real inference locally, run Ollama directly — it's free, open source, and does that job excellently. If you want deterministic Bedrock behavior in your test suite, you want an emulator with configured responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to pay for LocalStack Ultimate anyway
&lt;/h2&gt;

&lt;p&gt;There are reasons to pay for LocalStack Ultimate that aren't about Bedrock. Their core strength is deep coverage of the long tail of AWS services — things like ACM, Certificate Manager, Backup, Config, etc. If you rely on those in your infrastructure and you want a local mirror of them, paying for Ultimate may be worth it.&lt;/p&gt;

&lt;p&gt;If the Bedrock emulation is the reason you were about to open a procurement ticket for Ultimate, though, stop. Bedrock-on-Ollama isn't going to solve your test problem. What will solve it is an emulator built for tests — which you can get for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do next
&lt;/h2&gt;

&lt;p&gt;If you're currently testing against real Bedrock, start with &lt;a href="https://fakecloud.dev/blog/bedrock-local-testing/" rel="noopener noreferrer"&gt;how to test Bedrock code locally&lt;/a&gt; — walks through the full pattern end to end.&lt;/p&gt;

&lt;p&gt;If you're on LocalStack Ultimate and Bedrock was the draw, look at &lt;a href="https://fakecloud.dev/localstack-bedrock-alternative/" rel="noopener noreferrer"&gt;the migration comparison&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to understand why "just use Ollama" doesn't work as a test strategy, &lt;a href="https://fakecloud.dev/blog/bedrock-tests-real-llm/" rel="noopener noreferrer"&gt;here's a longer version of the argument&lt;/a&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install fakecloud&lt;/strong&gt;: &lt;code&gt;curl -fsSL https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LocalStack Bedrock docs (cited)&lt;/strong&gt;: &lt;a href="https://docs.localstack.cloud/aws/services/bedrock/" rel="noopener noreferrer"&gt;docs.localstack.cloud/aws/services/bedrock&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock emulator landing&lt;/strong&gt;: &lt;a href="https://fakecloud.dev/bedrock-emulator/" rel="noopener noreferrer"&gt;fakecloud.dev/bedrock-emulator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration landing&lt;/strong&gt;: &lt;a href="https://fakecloud.dev/localstack-bedrock-alternative/" rel="noopener noreferrer"&gt;fakecloud.dev/localstack-bedrock-alternative&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/faiscadev/fakecloud" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>bedrock</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Migrating from LocalStack to fakecloud in 10 minutes</title>
      <dc:creator>Lucas Vieira</dc:creator>
      <pubDate>Wed, 22 Apr 2026 19:25:54 +0000</pubDate>
      <link>https://dev.to/vieiralucas/migrating-from-localstack-to-fakecloud-in-10-minutes-1ij0</link>
      <guid>https://dev.to/vieiralucas/migrating-from-localstack-to-fakecloud-in-10-minutes-1ij0</guid>
      <description>&lt;p&gt;&lt;em&gt;Canonical: &lt;a href="https://fakecloud.dev/blog/migrate-from-localstack/" rel="noopener noreferrer"&gt;fakecloud.dev/blog/migrate-from-localstack&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In March 2026, LocalStack replaced its open-source Community Edition with a proprietary image that requires an account and an auth token. If your build broke last month, this guide is for you. If you are still on a pinned older tag and worried about the next pull, this is also for you.&lt;/p&gt;

&lt;p&gt;fakecloud is a free, open-source AWS emulator — single binary, no account, no token, no paid tier — that covers the services most teams relied on LocalStack Community for, plus several that moved to LocalStack Pro (RDS, ElastiCache, Cognito User Pools, SES v2, API Gateway v2).&lt;/p&gt;

&lt;p&gt;This guide is step-by-step. Copy, paste, done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line summary
&lt;/h2&gt;

&lt;p&gt;Change the image or the install command. Keep &lt;code&gt;http://localhost:4566&lt;/code&gt; and your dummy credentials. Everything else stays the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Stop LocalStack
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose down
&lt;span class="c"&gt;# or: docker kill $(docker ps -q --filter ancestor=localstack/localstack)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Install fakecloud
&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;# Option A: single binary, no Docker&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash
fakecloud

&lt;span class="c"&gt;# Option B: Docker&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 ghcr.io/faiscadev/fakecloud

&lt;span class="c"&gt;# Option C: cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;fakecloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;fakecloud listens on &lt;code&gt;http://localhost:4566&lt;/code&gt; — same as LocalStack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Keep your SDK wiring
&lt;/h2&gt;

&lt;p&gt;Your application code does not change. Endpoint URL and dummy credentials stay identical:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TypeScript&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;forcePathStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Python (boto3)
&lt;/span&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Update docker-compose.yml
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&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="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;localstack&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;localstack/localstack:latest&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;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;SERVICES=s3,sqs,sns,dynamodb,lambda&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEBUG=1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&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="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fakecloud&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;ghcr.io/faiscadev/fakecloud:latest&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;fakecloud starts all services by default (they are lazy and cheap — no &lt;code&gt;SERVICES&lt;/code&gt; env var needed).&lt;/p&gt;

&lt;p&gt;For Lambda execution you will need Docker-in-Docker or a mounted Docker socket (same as LocalStack Pro required):&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;fakecloud&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;ghcr.io/faiscadev/fakecloud:latest&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Update GitHub Actions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&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="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;localstack&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;localstack/localstack&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="s"&gt;4566:4566&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;LOCALSTACK_AUTH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.LOCALSTACK_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (install-and-run, no Docker):&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="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&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;curl -fsSL https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash&lt;/span&gt;
  &lt;span class="pi"&gt;-&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;fakecloud &amp;amp;&lt;/span&gt;
  &lt;span class="pi"&gt;-&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;for i in $(seq 1 30); do&lt;/span&gt;
        &lt;span class="s"&gt;curl -sf http://localhost:4566/_fakecloud/health &amp;amp;&amp;amp; exit 0&lt;/span&gt;
        &lt;span class="s"&gt;sleep 1&lt;/span&gt;
      &lt;span class="s"&gt;done&lt;/span&gt;
      &lt;span class="s"&gt;exit 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;~500ms startup vs ~3s for LocalStack container boot. On a cold CI runner the difference adds up over hundreds of test runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Terraform
&lt;/h2&gt;

&lt;p&gt;Provider block stays the same — only the running emulator changes.&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;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="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;skip_credentials_validation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_metadata_api_check&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_requesting_account_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;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="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;lambda&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;fakecloud's CI runs the upstream &lt;code&gt;hashicorp/terraform-provider-aws&lt;/code&gt; &lt;code&gt;TestAcc*&lt;/code&gt; suites against itself, so Terraform flows that worked against LocalStack Community should work against fakecloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that may need attention
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SERVICES&lt;/code&gt; env var.&lt;/strong&gt; Drop it. fakecloud starts all services by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;LOCALSTACK_AUTH_TOKEN&lt;/code&gt;.&lt;/strong&gt; Drop it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persisted state.&lt;/strong&gt; LocalStack Pro has &lt;code&gt;PERSISTENCE=1&lt;/code&gt;. fakecloud has &lt;code&gt;--persist /path/to/dir&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Services that moved from LocalStack Community to Pro (and what fakecloud does)
&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;LocalStack Community now&lt;/th&gt;
&lt;th&gt;fakecloud&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cognito User Pools&lt;/td&gt;
&lt;td&gt;Paid only&lt;/td&gt;
&lt;td&gt;122 ops, full auth flows + MFA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SES v2&lt;/td&gt;
&lt;td&gt;Paid only&lt;/td&gt;
&lt;td&gt;110 ops, full send + templates + DKIM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway v2&lt;/td&gt;
&lt;td&gt;Paid only&lt;/td&gt;
&lt;td&gt;28 ops, HTTP APIs + JWT/Lambda authorizers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS&lt;/td&gt;
&lt;td&gt;Paid only&lt;/td&gt;
&lt;td&gt;163 ops, real PostgreSQL/MySQL/MariaDB via Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElastiCache&lt;/td&gt;
&lt;td&gt;Paid only&lt;/td&gt;
&lt;td&gt;75 ops, real Redis/Valkey via Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;td&gt;111 ops (control plane + runtime)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Test-assertion SDKs (bonus)
&lt;/h2&gt;

&lt;p&gt;fakecloud ships test-assertion SDKs that let you inspect side effects from tests without raw HTTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FakeCloud&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fakecloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fc&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;FakeCloud&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;emails&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEmails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emails&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveLength&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emails&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="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toAddresses&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alice@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SDKs for TypeScript, Python, Go, PHP, Java, Rust. See &lt;a href="https://fakecloud.dev/docs/sdks" rel="noopener noreferrer"&gt;the SDK docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the migration
&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; http://localhost:4566 s3 mb s3://test-bucket
&lt;span class="nb"&gt;echo &lt;/span&gt;hello | aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:4566 s3 &lt;span class="nb"&gt;cp&lt;/span&gt; - s3://test-bucket/hello.txt
aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:4566 s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://test-bucket/
aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:4566 s3 rb s3://test-bucket &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it works, migration is done.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Install: &lt;code&gt;curl -fsSL https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/faiscadev/fakecloud" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Site: &lt;a href="https://fakecloud.dev" rel="noopener noreferrer"&gt;fakecloud.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Issues: &lt;a href="https://github.com/faiscadev/fakecloud/issues" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud/issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>testing</category>
      <category>localstack</category>
      <category>devops</category>
    </item>
    <item>
      <title>Free, Open-Source LocalStack Alternative for AWS Testing</title>
      <dc:creator>Lucas Vieira</dc:creator>
      <pubDate>Sat, 11 Apr 2026 14:36:32 +0000</pubDate>
      <link>https://dev.to/vieiralucas/free-open-source-localstack-alternative-for-aws-testing-3ac4</link>
      <guid>https://dev.to/vieiralucas/free-open-source-localstack-alternative-for-aws-testing-3ac4</guid>
      <description>&lt;p&gt;In March 2026, LocalStack replaced its Community Edition with a proprietary image that requires an account and auth token. For teams that relied on LocalStack for local AWS testing, this broke CI pipelines and forced a choice: pay for a Pro license or find an alternative.&lt;/p&gt;

&lt;p&gt;fakecloud is that alternative.&lt;/p&gt;

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

&lt;p&gt;fakecloud is a free, open-source local AWS emulator for integration testing and local development. It runs on a single port (4566), requires no account or auth token, and aims to faithfully replicate AWS behavior.&lt;/p&gt;

&lt;p&gt;Currently supports 20 AWS services with 1,000+ API operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; — objects, multipart uploads, versioning, lifecycle, notifications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; — real code execution via Docker, 13 runtimes, event source mappings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS&lt;/strong&gt; — FIFO queues, dead-letter queues, long polling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS&lt;/strong&gt; — fan-out to SQS/Lambda/HTTP, filter policies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt; — tables, transactions, PartiQL, streams, global tables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge&lt;/strong&gt; — pattern matching, scheduled rules, API destinations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS&lt;/strong&gt; — PostgreSQL/MySQL/MariaDB via Docker, snapshots, read replicas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache&lt;/strong&gt; — Redis/Valkey via Docker, replication groups, failover&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SES&lt;/strong&gt; — v2 API (97 operations), inbound email pipeline, event destinations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito User Pools&lt;/strong&gt; — authentication, MFA, user management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kinesis&lt;/strong&gt; — data streams, shard iterators, stream retention&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step Functions&lt;/strong&gt; — ASL interpreter, Lambda/SQS/SNS/DynamoDB integrations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Logs&lt;/strong&gt; — groups, streams, filtering, subscriptions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway v2&lt;/strong&gt; — HTTP APIs with Lambda integration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM, STS, SSM, Secrets Manager, KMS, CloudFormation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/faiscadev/fakecloud#supported-services" rel="noopener noreferrer"&gt;Full service list with operation counts&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  LocalStack vs fakecloud
&lt;/h2&gt;

&lt;p&gt;In March 2026, LocalStack moved many previously-free services behind a paywall (Cognito, SES v2, RDS, ElastiCache, ECR, ECS). The Community Edition now requires authentication and has limited service availability.&lt;/p&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;fakecloud&lt;/th&gt;
&lt;th&gt;LocalStack Community&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;AGPL-3.0 (free, open-source)&lt;/td&gt;
&lt;td&gt;Proprietary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth required&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (account + token)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commercial use&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Paid plans only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker required&lt;/td&gt;
&lt;td&gt;No (standalone binary)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup time&lt;/td&gt;
&lt;td&gt;~500ms&lt;/td&gt;
&lt;td&gt;~3s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle memory&lt;/td&gt;
&lt;td&gt;~10 MiB&lt;/td&gt;
&lt;td&gt;~150 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install size&lt;/td&gt;
&lt;td&gt;~19 MB binary&lt;/td&gt;
&lt;td&gt;~1 GB Docker image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS services&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;30+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test assertion SDKs&lt;/td&gt;
&lt;td&gt;TypeScript, Python, Go, Rust&lt;/td&gt;
&lt;td&gt;Python, Java&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito User Pools&lt;/td&gt;
&lt;td&gt;80 operations&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.localstack.cloud/references/licensing/" rel="noopener noreferrer"&gt;Paid only&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SES v2&lt;/td&gt;
&lt;td&gt;97 operations&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.localstack.cloud/references/licensing/" rel="noopener noreferrer"&gt;Paid only&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SES inbound email&lt;/td&gt;
&lt;td&gt;Real receipt rule action execution&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.localstack.cloud/user-guide/aws/ses/" rel="noopener noreferrer"&gt;Stored but never executed&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS&lt;/td&gt;
&lt;td&gt;22 operations, PostgreSQL/MySQL/MariaDB via Docker&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.localstack.cloud/references/licensing/" rel="noopener noreferrer"&gt;Paid only&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElastiCache&lt;/td&gt;
&lt;td&gt;44 operations, Redis/Valkey via Docker&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.localstack.cloud/references/licensing/" rel="noopener noreferrer"&gt;Paid only&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;LocalStack has more services overall, but many are behind a paywall. fakecloud focuses on implementing fewer services completely, with 100% conformance to AWS behavior.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;LocalStack went proprietary.&lt;/strong&gt; In March 2026, &lt;code&gt;localstack:latest&lt;/code&gt; started requiring authentication. CI pipelines broke. The message was clear: LocalStack Community Edition was no longer community.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correctness matters for testing.&lt;/strong&gt; If you're building on AWS, your integration tests should talk to something that behaves like AWS — not mocks that return fake success responses. When fakecloud behaves differently from AWS, that's a bug we fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real infrastructure, not stubs.&lt;/strong&gt; fakecloud runs actual software via Docker: real Lambda runtimes (13 languages), real databases (PostgreSQL, MySQL, MariaDB), real Redis/Valkey instances. When your Lambda function reads from RDS, it's talking to an actual Postgres instance, not a mock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing tools should be free.&lt;/strong&gt; fakecloud is a development dependency, not production infrastructure. It should be free and open-source, the same way test frameworks like Jest, Mocha, and pytest are free.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we verify correctness
&lt;/h2&gt;

&lt;p&gt;fakecloud doesn't claim to be "AWS-compatible" without backing it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;34,000+ conformance test variants&lt;/strong&gt; validated against official AWS Smithy models, covering every operation parameter, boundary condition, and error case&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;280+ end-to-end tests&lt;/strong&gt; using the official AWS SDKs (aws-sdk-rust, boto3, aws-sdk-js)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated testing against real AWS&lt;/strong&gt; — our test suite runs against both fakecloud and actual AWS to verify behavioral parity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When tests fail, we fix the behavior. We don't stub responses or return fake success codes.&lt;/p&gt;

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

&lt;p&gt;Install and run fakecloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash
fakecloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or via Docker:&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;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4566:4566 ghcr.io/faiscadev/fakecloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your AWS SDK at &lt;code&gt;http://localhost:4566&lt;/code&gt; with dummy credentials:&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 sqs create-queue &lt;span class="nt"&gt;--queue-name&lt;/span&gt; test-queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test assertions with fakecloud SDKs
&lt;/h2&gt;

&lt;p&gt;fakecloud exposes introspection endpoints that AWS doesn't provide, so you can assert on side effects in your tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FakeCloud&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fakecloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fc&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;FakeCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// After your app sends an email via SES&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;emails&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEmails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emails&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveLength&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emails&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="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toAddresses&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// After your Lambda processes an event&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;invocations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInvocations&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invocations&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="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SDKs available for TypeScript, Python, Go, and Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's not implemented (yet)
&lt;/h2&gt;

&lt;p&gt;fakecloud is younger than LocalStack and has fewer services. If you need EC2, ECS, or other services not listed above, LocalStack may still be your best option (if you're willing to pay for Pro).&lt;/p&gt;

&lt;p&gt;We're prioritizing services based on real-world demand. &lt;a href="https://github.com/faiscadev/fakecloud/issues" rel="noopener noreferrer"&gt;Open an issue&lt;/a&gt; if there's a service you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;p&gt;Coming next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock&lt;/strong&gt; — AI/ML testing (high demand across the ecosystem)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Containers&lt;/strong&gt; — practical container support with ECR&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFront&lt;/strong&gt; — CDN configuration testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Metrics&lt;/strong&gt; — complete monitoring story alongside Logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena&lt;/strong&gt; — SQL analytics on S3&lt;/li&gt;
&lt;li&gt;More RDS engines (Oracle, SQL Server)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/faiscadev/fakecloud/blob/main/ROADMAP.md" rel="noopener noreferrer"&gt;Full roadmap&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other alternatives to LocalStack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Moto&lt;/strong&gt; — Python-based AWS mocking library. More services than fakecloud (100+), but designed for unit tests with mocks rather than integration tests. Doesn't support cross-service integrations or real Lambda execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LocalStack Pro (paid)&lt;/strong&gt; — If you need the full service catalog and don't mind paying, LocalStack Pro is mature and feature-complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real AWS&lt;/strong&gt; — For the highest fidelity, test against real AWS. fakecloud is for fast local iteration; real AWS is for pre-production validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try fakecloud
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/faiscadev/fakecloud" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Site:&lt;/strong&gt; &lt;a href="https://fakecloud.dev" rel="noopener noreferrer"&gt;fakecloud.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install:&lt;/strong&gt; &lt;code&gt;curl -fsSL https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find a case where fakecloud behaves differently from AWS, &lt;a href="https://github.com/faiscadev/fakecloud/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; — that's a bug, and we'll fix it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://fakecloud.dev/blog/localstack-alternative/" rel="noopener noreferrer"&gt;fakecloud.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>testing</category>
      <category>opensource</category>
      <category>localstack</category>
    </item>
    <item>
      <title>Why I'm building a free, open-source AWS emulator</title>
      <dc:creator>Lucas Vieira</dc:creator>
      <pubDate>Mon, 06 Apr 2026 22:27:35 +0000</pubDate>
      <link>https://dev.to/vieiralucas/why-im-building-a-free-open-source-aws-emulator-5gfl</link>
      <guid>https://dev.to/vieiralucas/why-im-building-a-free-open-source-aws-emulator-5gfl</guid>
      <description>&lt;p&gt;A few days ago, our CI started failing. The culprit: &lt;code&gt;localstack:latest&lt;/code&gt; now requires an account and an auth token. Broken builds.&lt;/p&gt;

&lt;p&gt;I'd seen LocalStack creeping toward proprietary for a while, but this was the moment it actually hit. A colleague noticed it at the same time as me, but I was already working on a fix. I patched the build, but the question stuck: how long can we keep using the old version until we start having real problems?&lt;/p&gt;

&lt;h2&gt;
  
  
  I don't trust mocks
&lt;/h2&gt;

&lt;p&gt;I should back up. I care about tests a lot. Enough to become a &lt;a href="https://github.com/chaijs/chai" rel="noopener noreferrer"&gt;maintainer of Chai.js&lt;/a&gt;. Enough that when I build something, the test infrastructure comes first.&lt;/p&gt;

&lt;p&gt;But I have a specific opinion about &lt;em&gt;what kind&lt;/em&gt; of tests matter. I hate mocks. Not the concept — the way most codebases use them. You end up with tests that don't test anything real. You're asserting that you call functions in the right order. You're verifying the plumbing, not the behavior.&lt;/p&gt;

&lt;p&gt;The worst part is when mocked tests give you false confidence. I've seen tests that assert you do the wrong thing successfully — the mock doesn't care, it just returns what you told it to. Your tests pass. Your code has a bug. You ship it.&lt;/p&gt;

&lt;p&gt;If you're building on AWS, you need integration tests that actually talk to something that behaves like AWS. Not a mock. Not a stub that returns 200 to everything. Something that implements the real behavior: if you call API A then API B, and AWS would produce side effect C, then your test environment should produce side effect C too.&lt;/p&gt;

&lt;p&gt;That's what LocalStack used to give us. And that's what I wanted to keep — but free and open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I built it
&lt;/h2&gt;

&lt;p&gt;I started fakecloud on April 4th, 2026. Three days later: 13 AWS services, 300+ commits, 1,000+ tests.&lt;/p&gt;

&lt;p&gt;That pace was possible because of two things. First, I used LLMs heavily throughout — not to generate code I don't understand, but as a force multiplier with strong guardrails. Every feature ships with E2E tests. The tests are the guardrails. If the LLM generates something that doesn't match real AWS behavior, the tests catch it.&lt;/p&gt;

&lt;p&gt;Second, Rust. I chose Rust because I love static-typed compiled languages, and Rust's type system is genuinely amazing. You get the performance of no garbage collector without having to manually manage memory. It means fakecloud starts in under 100ms and runs as a single binary — no Docker required, no runtime dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correctness is the whole point
&lt;/h2&gt;

&lt;p&gt;fakecloud doesn't try to be a scalable production cloud. It's not that. It's a testing tool. And the one thing a testing tool needs to get right is correctness.&lt;/p&gt;

&lt;p&gt;What does that mean in practice? If you call &lt;code&gt;CreateQueue&lt;/code&gt;, then &lt;code&gt;SendMessage&lt;/code&gt;, then &lt;code&gt;ReceiveMessage&lt;/code&gt; on real AWS and get back your message with specific attributes — fakecloud should do exactly the same thing. If it doesn't, that's a fakecloud bug.&lt;/p&gt;

&lt;p&gt;We currently verify this with 280+ E2E tests that use the official &lt;code&gt;aws-sdk-rust&lt;/code&gt; crate and 34,856 auto-generated conformance test variants validated against official AWS Smithy models — covering all 983 API operations across 13 services at 100% conformance. The plan for the near future: set up a real AWS account and run our test suite against both fakecloud and real AWS side by side, so we can verify behavioral parity automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's here today
&lt;/h2&gt;

&lt;p&gt;13 services, 983 API operations, all open source, all free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; (107 actions) — objects, multipart uploads, versioning, lifecycle, notifications, encryption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS&lt;/strong&gt; (23 actions) — FIFO queues, dead-letter queues, long polling, batch operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS&lt;/strong&gt; (34 actions) — fan-out to SQS, HTTP delivery, filter policies, platform applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge&lt;/strong&gt; (57 actions) — pattern matching, scheduled rules, connections, API destinations, endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; (128 actions) — users, roles, policies, groups, instance profiles, OIDC/SAML providers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STS&lt;/strong&gt; (11 actions) — assume role, session tokens, federation, credential expiration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSM&lt;/strong&gt; (146 actions) — parameters, documents, commands, maintenance windows, associations, automation, sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt; (57 actions) — tables, items, transactions, PartiQL, backups, global tables, streaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; (10 actions) — function CRUD, invoke, event source mappings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets Manager&lt;/strong&gt; (23 actions) — versioning, soft delete, rotation, replication, resource policies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Logs&lt;/strong&gt; (113 actions) — groups, streams, filtering, deliveries, transformers, anomaly detection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KMS&lt;/strong&gt; (53 actions) — encryption, aliases, key management, grants, custom key stores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFormation&lt;/strong&gt; (8 actions) — template parsing, resource provisioning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Services talk to each other: S3 notifications deliver to SNS/SQS. SNS fans out to SQS. EventBridge rules fire on schedule and deliver to targets. This is the kind of cross-service behavior that matters in integration tests and that most emulators get wrong or skip entirely.&lt;/p&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;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash
fakecloud
&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; with dummy credentials. That's it.&lt;/p&gt;

&lt;p&gt;The code is at &lt;a href="https://github.com/faiscadev/fakecloud" rel="noopener noreferrer"&gt;github.com/faiscadev/fakecloud&lt;/a&gt;. It's AGPL-3.0 — free and open source, including for commercial use.&lt;/p&gt;

&lt;p&gt;If you need a local AWS emulator for your integration tests, give it a try. And if something doesn't behave like real AWS — &lt;a href="https://github.com/faiscadev/fakecloud/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;. That's a bug, and we'll fix it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>rust</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
