Cloud Parts

Local cloud emulators

A process on your laptop or CI runner that answers a cloud provider’s API, so tests and dev loops run with no account, no bill and no network.

3 technologies on this page ↓
Live model
What you’re watchingTests use the normal AWS SDK, pointed at one local port. The emulator reads which API each request is for and answers from in-memory state in milliseconds. Lambda calls are the exception: the first one waits while a container starts to run the function.

How it works

A cloud SDK turns every call, such as put_item or send_message, into an HTTP request to the provider’s regional endpoint. An emulator is a server that listens on localhost and implements the same operations with the same request and response formats. You change one setting, not your code: AWS_ENDPOINT_URL=http://localhost:4566 for the AWS SDKs and CLI, PUBSUB_EMULATOR_HOST for Google’s Pub/Sub clients, or a connection string with Azurite’s well-known development account for Azure Storage. Some emulators fake one service (DynamoDB Local, the Pub/Sub emulator, Azurite); others put dozens behind one port and route each request by the API it names (LocalStack, Floci). State lives in memory or a local file and is usually gone after a restart, which gives every test run a clean cloud. Any dummy key is accepted, and IAM policies aren’t checked unless you switch that on. Where faking is hard, emulators start real engines in Docker: a Lambda function runs in a container built from AWS’s runtime image, and an emulated RDS database can be a real PostgreSQL. The catch is fidelity. Operations finish instantly, throughput settings and quotas are ignored, reads that are only eventually consistent in the cloud look strongly consistent, error messages can differ, and whole services or newer features may be missing. A mock replaces the client inside your test process; an emulator is a separate server, so the real SDK, its serialisation and tools like Terraform are exercised too. It still isn’t the cloud, so teams keep a smaller suite that runs against a real account before release.

Think of it as

A flight simulator. The controls, instruments and checklists are the real ones, so pilots can practise for hours with no fuel and no risk. It never quite reproduces real weather or a failing engine, though, so they still fly the real aircraft before carrying passengers.

Reach for it when

  • Integration tests in CI that call cloud APIs, with no shared account, no credentials in the pipeline and nothing to clean up
  • A fast inner loop: create a bucket, queue or table in milliseconds and reset everything with a restart
  • Trying infrastructure as code (Terraform, CDK, SAM) against a throwaway target before it touches a real account
  • Giving new contributors or AI coding agents a sandbox with no real credentials and nothing to bill

Think twice when

  • Testing IAM policies, quotas, throttling, latency or scaling: emulators skip or simplify them by default
  • Services or features the emulator covers only partly; read its coverage page before building on it
  • As the only test before production: keep a smaller suite that runs against a real account
  • Load and performance tests: a container on a laptop says nothing about cloud throughput

A real example

In CI, a job starts an emulator container on port 4566 and sets AWS_ENDPOINT_URL=http://localhost:4566 with the dummy keys test/test. Each test creates the orders table and the receipts queue, places an order and checks that the item and the message exist, then deletes both. The 40 tests finish in under a minute with no AWS account and nothing left behind. A nightly job runs the same suite against a real staging account; it is the one that catches a missing sqs:SendMessage permission, because the emulator, with IAM checks off, let the call through.

Technologies

Specific products, and what each does differently.

What each provider calls it

AWS
No general emulator. DynamoDB Local (JAR, Maven or Docker); `sam local` runs Lambda functions and API routes in Docker; the Lambda Runtime Interface Emulator tests container-image functions; Step Functions Local still downloads, but AWS now marks it unsupported and points to the TestState API
Google Cloud
`gcloud beta emulators` for Pub/Sub, Datastore, Firestore, Bigtable and Spanner; the Firebase Local Emulator Suite for Firestore, Realtime Database, Auth, Storage, Hosting and Functions
Azure
Azurite (open source: Blob, Queue and Table; replaces the deprecated Windows-only Storage Emulator); the Cosmos DB emulator for Windows plus a Linux Docker preview for the NoSQL API; Docker emulators for Service Bus and Event Hubs; Azure Functions Core Tools runs functions locally
Open source & others
LocalStack (AWS, plus Snowflake; the latest image needs a free or paid account since March 2026); Floci (MIT: AWS, Azure, Google Cloud and Oracle Cloud); moto (Python mocks with a standalone server mode); fake-gcs-server for Cloud Storage; S3-compatible servers such as Garage or SeaweedFS stand in for S3 alone

In code · python

# Start an emulator first, e.g.  docker run -d -p 4566:4566 floci/floci:latest
# (or LocalStack with LOCALSTACK_AUTH_TOKEN set). The code under test doesn't change.
import os
import boto3
import pytest

os.environ.setdefault("AWS_ENDPOINT_URL", "http://localhost:4566")  # every service, one port
os.environ.setdefault("AWS_ACCESS_KEY_ID", "test")                # dummy keys, no account
os.environ.setdefault("AWS_SECRET_ACCESS_KEY", "test")
os.environ.setdefault("AWS_DEFAULT_REGION", "us-east-1")

def place_order(ddb, sqs, queue_url, order_id):  # the code under test
    ddb.put_item(TableName="orders", Item={"id": {"S": order_id}})
    sqs.send_message(QueueUrl=queue_url, MessageBody=order_id)

@pytest.fixture
def aws():
    ddb, sqs = boto3.client("dynamodb"), boto3.client("sqs")
    ddb.create_table(TableName="orders", BillingMode="PAY_PER_REQUEST",
                     AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
                     KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}])
    url = sqs.create_queue(QueueName="receipts")["QueueUrl"]
    yield ddb, sqs, url
    ddb.delete_table(TableName="orders")  # a clean cloud for the next test
    sqs.delete_queue(QueueUrl=url)

def test_place_order(aws):
    ddb, sqs, url = aws
    place_order(ddb, sqs, url, "4812")
    assert "Item" in ddb.get_item(TableName="orders", Key={"id": {"S": "4812"}})
    assert len(sqs.receive_message(QueueUrl=url, WaitTimeSeconds=1)["Messages"]) == 1

Often confused with

Where it came from2008–2026

Testing code that talks to a cloud once meant two bad options: deploy to a real account and wait, pay and clean up, or mock the client and hope the mock behaved like the service. Google App Engine launched in April 2008 with an SDK that included a “fully featured local development environment”, so the platform could run on a laptop. Microsoft’s Azure SDK shipped a Storage Emulator that faked Blob, Queue and Table storage on top of a local SQL Server database.

In 2013 the open-source moto project began mocking AWS calls in Python tests, and that September AWS released DynamoDB Local so developers could write DynamoDB code “while sitting in a tree, on the beach, or in the desert”. In August 2016 Waldemar Hummer’s first commit of LocalStack, at Atlassian, put eight AWS APIs on a laptop at once; the team’s stated aim was to make train commutes productive. Google and Microsoft followed with emulators of their own, and LocalStack grew into a company that sells an AWS and a Snowflake emulator.

In March 2026 LocalStack stopped updating its free open-source image and required an account for its latest releases. Floci, an MIT-licensed replacement, shipped 1.0 that same month and now pitches emulators as a safe cloud for AI coding agents.

Why it’s built this way

Speak the real wire protocol instead of mocking a library
An in-process mock works for one language. A server on a port works with every SDK, the CLI and IaC tools, and the code under test changes only its endpoint. moto added a standalone server mode for exactly this reason.
Keep state in memory and throw it away
Tests want a clean, fast cloud every time. DynamoDB Local can run fully in memory, and LocalStack’s state is ephemeral unless persistence is switched on.
Accept any credentials and skip IAM by default
The point is to need no account. DynamoDB Local uses the access key only to name its database file and ignores the secret key; LocalStack and Floci evaluate IAM policies only when you enable it.
Run real engines where faking falls short
Re-implementing a function runtime or a database is hard to get right, so sam local, LocalStack and Floci run Lambda functions in containers built from AWS’s runtime images, and Floci backs RDS with real PostgreSQL or MySQL.

Timeline

  1. 2008

    App Engine ships with an SDK and a local development environment

  2. 2013

    moto starts mocking AWS in February; DynamoDB Local arrives in September

  3. 2016

    LocalStack is open-sourced at Atlassian with eight AWS APIs

  4. 2024

    LocalStack adds an emulator for Snowflake, beyond the cloud providers

  5. 2026

    LocalStack gates its latest image behind accounts; Floci 1.0 ships under MIT

Sources24

Improve this page on GitHub