Skip to content
doze AWS
0.3

These are the docs for doze AWS 0.3.

Every release on GitHub →

doze-aws is one small static binary that emulates the AWS services a development stack leans on — built from the wire protocol up, verified against both AWS SDK generations. No Docker, no JVM, no cloud.

Terminal window
curl -fsSL https://raw.githubusercontent.com/doze-dev/doze-aws/main/install.sh | sh
exec $SHELL # or: export PATH="$HOME/.doze-aws/bin:$PATH"
cd ~/code/harbour && doze-aws

The installer verifies a SHA-256 against the release checksums and puts one binary in ~/.doze-aws/bin. There is nothing else to install — no runtime, no daemon, no container. (--uninstall takes it away again and leaves your data alone.)

The new shell is not optional. The installer adds that directory to your .zshrc or .bashrc, but it cannot change the PATH of the shell that ran it — no process can do that to its parent. So doze-aws is found in the next shell, and exec $SHELL is the shortest way to get one. If your $SHELL is neither zsh nor bash the installer will not guess at a profile; it prints the export line for you to put wherever you keep such things.

It prints where it is and what to do next: the endpoint, the console URL, how many services it is serving in which region and account, and the two commands worth knowing — doze-aws env and doze-aws doctor. The README shows the block in full.

That block goes to stdout; the structured log lines go to stderr, so doze-aws 2>/dev/null leaves just the summary and anything parsing the logs is unaffected.

That is the whole setup: it answers on 127.0.0.1:4566, and there is nothing to set up on the machine and no sudo. A second instance, or a container, takes --listen (below).

The instance is labelled after the directory, and its state lands in ./data — which doze-aws marks ignored as it creates it, so git never offers to commit two megabytes of local databases.

Point any AWS SDK at it, or the AWS CLI if you have one (doze-aws does not install or need it — the examples below are just the shortest way to show the thing working):

Terminal window
eval "$(doze-aws env)"
aws s3 mb s3://my-bucket
aws s3 cp ./file.txt s3://my-bucket/

cli.md has the longer set — DynamoDB, SQS, the by-hand export block, and the per-service endpoint variables.

http://127.0.0.1:4566/_console/ — on by default, --console=false to turn it off.

It opens on the wire: every call your app has made, newest first, with the work each one caused nested under it. An S3 upload that fired a notification that invoked a Lambda is drawn as one thing, not three unrelated rows. Each row carries the status, the duration, the request id the SDK printed, and a copy as curl button whose body is the redacted copy, so a repro you paste into a ticket keeps secrets masked.

The calls the console makes on its own behalf never appear there — it talks to the services directly rather than through the recorder — so the tail is your app’s traffic and nothing else.

From there, every service has pages that do real work: browse and upload S3 objects, actually receive (not peek at) SQS messages and redrive from a DLQ, run PartiQL against DynamoDB, watch a Step Functions execution graph, invoke a Lambda and tail its logs, simulate an IAM policy before you trust it.

Two things worth knowing about:

  • Connect — the same endpoint written four ways: an AWS CLI profile, the environment block, a Terraform provider, and the deploy commands. Built from the address you reached the console on, with a button that runs a real GetCallerIdentity to prove it works.
  • The fidelity ledger, on each service page — which operations are functional, which are cosmetic round-trips that accept and return your config without acting on it, and which are honest stubs. Real AWS never has to answer “is this call real here”; an emulator does, and this is where it does.

Queue URLs, invoke URLs and function URLs come back in path shapes at an address (http://127.0.0.1:4566/000000000000/jobs), so an SDK uses path-style S3. Run under doze as the aws module, doze-aws has a name and they come back AWS-shaped under it (http://sqs.us-east-1.aws.shop.doze/000000000000/jobs). See endpoints.md.

In Docker, if that is where your team already is

Section titled “In Docker, if that is where your team already is”

doze-aws ships no image and no Dockerfile, deliberately — the whole argument against a 1.88 GB emulator container is weakened by shipping one, and a static binary with no runtime dependencies is a two-line Dockerfile anyone can write for themselves. If your team’s stack is already docker compose up, this is enough:

# Dockerfile
FROM alpine:3
COPY doze-aws /usr/local/bin/doze-aws
ENTRYPOINT ["doze-aws", "--listen", "0.0.0.0:4566", "--data-dir", "/data"]
compose.yaml
services:
aws:
build: .
ports: ["4566:4566"]
volumes: ["aws-data:/data"] # drop this line to start clean every run
volumes:
aws-data:

Then point the SDKs at http://aws:4566 from sibling services, or http://localhost:4566 from the host. Two things to know: --listen 0.0.0.0:4566 is required, because the default 127.0.0.1 is unreachable from outside the container; and the data directory is worth a volume only if you want state to survive down — a test run usually does not, and starting clean is one line shorter.

Everything is a flag or a TOML key (flags win). Print the effective config:

Terminal window
doze-aws config print

Copy doze-aws.example.toml to ./doze-aws.toml — it is auto-loaded from the working directory — to name the instance, pick a region, enable a subset of services, or set the data directory. Every key is optional, paths resolve against the file rather than your shell, and cli.md documents the lot.

Data lives under data/<region>/<service>/ — with IAM and STS under data/_global/ — and survives restarts. Delete the directory to reset. There is nothing else to clean up.

Everything is behind one endpoint. The gateway routes each request to the right service by its wire signals (the X-Amz-Target header, the SigV4 scope, the request path, or the S3 fallback) — exactly how AWS SDKs address a custom endpoint. Cross-service features work out of the box: an S3 event notification lands in SQS, an SNS publish fans out to SQS and Lambda, an EventBridge rule routes to its targets, a Lambda handler reaches every sibling service.

Functions run as real local processes speaking the AWS Lambda Runtime API. For quick local iteration, point a function at code in place instead of zipping it (the _local_ extension — see SUPPORT.md).

Every implemented service documents its operation support in SUPPORT.md: Functional (real local semantics), Cosmetic (config round-trips, no local effect), or an honest Stub for what’s physically meaningless locally.

doze-aws has no file format of its own. Deploy with whatever you already use — the AWS CLI, SAM, CDK or Serverless Framework all work against the endpoint:

Terminal window
aws cloudformation deploy --template-file template.yaml --stack-name shop

doze-aws also applies ./template.yaml at boot if one is present, so “clone the repo, run doze-aws” is the whole onboarding story. See cloudformation.md.