Clouds On A Laptop - the cover: one cloud API built, S3; the rest emulated as a proposal

Clouds On A Laptop

Cloud services can be emulated well enough for a laptop: Moto, DynamoDB Local, Azurite, the Google emulators - with SDK endpoint overrides, one record per resource, and every emulator placed inside the same failure domains as the rest. Only S3 is built.

👋 Hi, I'm Anton - a software engineer. These days I work on a platform: contracts, generated code, data paths and the tooling around them. Running notes live on my GitHub: github.com/brilliant-almazov.

Everything here except S3 is a proposal - none of it is built on our stand. Every claim about a tool was checked against its own documentation for this article.

This is part 7 of the series on testing cross-zone, cross-datacenter and cross-provider behaviour on a laptop. Maybe you already do it better, maybe you'd do it another way - this is how it is going, and where it stops.

The only cloud we touch

Part 6, Every Verdict Carries A Trace, was about how a result is checked. This part asks what a laptop can stand in for when the thing being called is a cloud service.

For us the honest answer starts small. The platform touches the cloud through exactly one API: S3. The stand already emulates it with a small S3-compatible server, versitygw, Apache-2.0. Because the platform speaks only the S3 API, any S3-compatible endpoint would do.

A flow: services, the S3 API, a local S3-compatible server; a pill reads built, the only cloud API in use

That is all that is built. No queue emulator, no function emulator, no document database emulator - no service of ours depends on one yet. The rest of this part is how it can be done when one does, with forms that are proposals, each marked "not built".

What exists to emulate the rest

The landscape, checked against primary sources:

Tool Emulates Licence and status Source
Moto (server mode) S3, DynamoDB, SQS, SNS, Lambda, API Gateway and more, on one endpoint Apache-2.0, active Moto, server mode
DynamoDB Local DynamoDB a free download from AWS AWS
ElasticMQ a queue with an SQS-compatible interface open source ElasticMQ
Azurite Azure Blob, Queue and Table (Table in preview) MIT, active Azurite, Microsoft Learn
gcloud emulators Pub/Sub, Bigtable, Firestore, Spanner ship with the gcloud CLI Pub/Sub, Bigtable, gcloud
fake-gcs-server Google Cloud Storage BSD-2-Clause fake-gcs-server
LocalStack a wide AWS surface needs an auth token; free plan non-commercial only auth token, pricing

Eight tiles, one per tool, each with a status pill: Moto active, DynamoDB Local a free download, ElasticMQ open source, Azurite active, gcloud emulators with the CLI, fake-gcs-server BSD-2-Clause, LocalStack a token and paid for commercial use, MinIO archived and not used

One correction from reading the sources: an earlier analysis said Lambda in Moto is only mocked. The Moto documentation says otherwise - by default it runs the function code inside a Docker container, and only without Docker does it return a canned response (Moto Lambda).

Two that changed

Two well-known names have changed status, and both were checked at the source:

  • LocalStack - the community repository was archived on 23 March 2026 as the project consolidated into a single image (GitHub). An auth token is now a mandatory credential to start the container, with a dedicated token for CI (docs). The free Hobby plan is for non-commercial use; commercial development needs a paid plan (pricing).
  • MinIO - the repository was archived on 25 April 2026 and says it is no longer maintained (GitHub). We do not use it; the S3 server above is a different one.

Neither is a judgement of the tools. Both are reasons to check the source before a dependency goes into a stand.

Pointing a service at an emulator

The cheapest part is the one that needs no code. Every major cloud SDK already has a way to change where it sends requests. For AWS it is a standard variable, global or per service, with a defined order of precedence (AWS SDKs). A proposal for the stand, not built:

# not built - a proposal AWS_ENDPOINT_URL=http://cloud-aws.internal:5000 AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_REGION=eu-central-1 AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=<azurite well-known dev key>;BlobEndpoint=http://cloud-azure.internal:10000/devstoreaccount1; PUBSUB_EMULATOR_HOST=cloud-gcp.internal:8085 STORAGE_EMULATOR_HOST=http://cloud-gcp-gcs.internal:4443 

A dark terminal card with the SDK variables: AWS endpoint URL and dummy keys, an Azure connection string with the dev account, the Pub/Sub and Storage emulator hosts; a pill reads not built

Services keep production-shaped names (*.internal), and credentials are dummies - the emulators do not check them. LocalStack's token is the one exception. For Pub/Sub, setting PUBSUB_EMULATOR_HOST is enough for most client libraries; Java, C# and Rust need a code change (Pub/Sub emulator).

One record per resource

On the stand every resource is one record, the same shape as the S3 one. The cloud emulators would be records too, each behind its own optional profile:

# cloud-aws (PROPOSAL, not built) resource: true kind: cloud-aws image: motoserver/moto:<pin> profiles: [cloud-aws] port: 5000 # one endpoint for all mocked AWS services mem: <measured when added> # cloud-aws-dynamodb (only if real DynamoDB semantics are needed): amazon/dynamodb-local, port 8000 # cloud-azure: mcr.microsoft.com/azure-storage/azurite, profiles [cloud-azure], ports 10000 / 10001 / 10002 # cloud-gcp: the google-cloud-cli emulators image, `gcloud beta emulators pubsub start --host-port=0.0.0.0:8085` # cloud-aws-localstack (paid, the owner's decision only): localstack/localstack, port 4566, secrets [LOCALSTACK_AUTH_TOKEN] 

A dark card with the cloud-aws record: resource, kind, a pinned Moto image, a profile, one port, memory to be measured; comments for DynamoDB Local, Azurite, the gcloud emulator and LocalStack; a pill reads not built

The recommendation behind it: not now, because nothing depends on it. When the first integration appears, three optional profiles - cloud-aws on Moto, cloud-azure on Azurite, cloud-gcp on the CLI emulators plus fake-gcs-server - and LocalStack only if the owner decides it is needed and pays for it.

Clouds inside the failure domains

This is the idea of the part. An emulator is a container like any other, so it can be placed on the planet of part 3 - in a zone of "its" provider:

# not built - a proposal placement: cloud-aws: [dc-a1-z1] # prov-a cloud-azure: [dc-b1-z1] # prov-b cloud-gcp: [dc-a2-z1] 

Then the chaos of part 3 applies to the cloud as well. provider-down prov-a takes the AWS emulator down together with every site of prov-a - and, in this placement, the Google emulator too, since dc-a2 is a prov-a site. link-degrade dc-a1~dc-b2 rtt_ms=200 adds latency between a service on the US side and the AWS emulator. Nothing new is needed - the same netem and iptables rules as any placed container.

An animation: the planet map with three cloud emulators placed in zones; provider-down prov-a runs; every prov-a site turns red together with the AWS and Google emulators placed there, while the Azure emulator in prov-b stays green

A cloud outage stops being a special case to mock. It becomes "the provider's sites went down", which the stand already knows how to do.

One command

All of it would hang off the same tool and the same profiles:

# not built - a proposal ./env up --profile dev --profile cloud-aws ./env up --profile ha --profile cloud-aws --profile cloud-azure --latency declared ./env scenario provider-down --latency declared 

A dark terminal card with three ./env commands: up with dev and cloud-aws, up with ha and two cloud profiles and declared latency, a provider-down scenario; a pill reads not built

Limits

An emulator is not a cloud, and the gaps are known:

  • one emulator is one more container; its memory is to be measured when it is added;
  • IAM, quotas, cold starts, service limits and regional differences are not emulated, or are simplified - the Pub/Sub emulator, for one, supports no IAM operations (Pub/Sub emulator);
  • full fidelity for functions and API gateways is what LocalStack sells; Moto runs function code in Docker, but it is still Moto's model of the service;
  • every cloud has its own API, so "one function on three providers" is three integrations in code, not one.

Four icon tiles: one more container, memory measured when added; IAM and quotas, not emulated; functions, Moto's model; three clouds, three integrations

A comparison of event runners - Lambda event source mappings, Knative, OpenFaaS, Temporal, KEDA - is planned, not written.

One way to check it

How would anyone know an emulator is good enough? QA has one way to check it - their idea, not approved by the owner yet. An emulated queue or function is checked as if it were the real one:

  • contract - status codes and error classes match the declared API;
  • semantics - at-least-once delivery, order, dead-letter, visibility timeout;
  • failure - the same daemon × queue matrix as part 6;
  • observability - a trace passes through the emulator, queue metrics reach the metrics store;
  • isolation - zero outbound connections;
  • and a separate row, emulator vs cloud, that names every known difference.

Six columns: contract, semantics, failure, observability, isolation, and emulator vs cloud; a pill reads one way to check it, a proposal

The last row is the honest one. An emulator that passes everything else still differs somewhere, and the row is where that is written down instead of discovered.

What I do not claim

  • Not that any cloud emulator except S3 runs on our stand. Everything else is a proposal.
  • Not that emulators are clouds. IAM, quotas, cold starts and limits are not there.
  • Not that the check is agreed. It is QA's proposal.
  • Not a recommendation for or against any vendor. The status lines are what their own pages say.

What of this is method

  • Check the source before the dependency. Two well-known tools changed status this year.
  • Point, do not patch. SDK endpoint variables move a service to an emulator without code.
  • Place the cloud on the planet. An emulator in a zone falls under the same chaos as everything else.

Cross-anything on a laptop - Part 7. Next: Providers On A Laptop.

If you emulate AWS locally, which tool, and what did it get wrong? If you dropped LocalStack or MinIO this year, what replaced it? If you have tested a cloud outage, how did you fake it? Which cloud service would you emulate first?