Skip to content

Run AWS Emulator on a remote server for your agents

A fake AWS that's always up, for agents that shouldn't have real AWS keys.

Deploy on exe.dev

Shelley handles the setup. About 10 minutes.

Prefer a terminal?
curl -fsSL https://exe.dev/deploy/aws-emulator/prompt.txt | ssh exe.dev new --prompt=/dev/stdin

AWS Emulator is AWS's own local emulator of the AWS APIs: S3, DynamoDB, Lambda, SQS, IAM and hundreds more on one HTTPS endpoint, with no AWS account. Run it on an exe.dev VM and your coding agents get an AWS to build against that's always up and holds no real credentials. Claude Code or Codex can create buckets, deploy Lambdas and run terraform apply all day, and the worst they can do is break a fake.

That's safer than handing an agent your AWS keys. The VM has none, and an AGENTS.md in your project tells every agent to source one file that points the CLI and SDKs at the emulator. Your laptop can use the same emulator through an SSH tunnel.

Warning

AWS Emulator keeps everything in memory. Restarting the service, a reboot or an update empties it and makes new keys. It's for building and testing, not for production data or for testing IAM policies and other security settings, which it doesn't enforce the way AWS does.

What you get#

  • AWS Emulator from AWS's package, running as exedev on 127.0.0.1:9297, back after a reboot
  • The emulator's usage telemetry turned off
  • The AWS CLI, and ~/aws-emulator.env, which points the CLI and SDKs at the emulator
  • aws sts get-caller-identity answers with the emulator's account, 000000000000
  • Claude Code, Codex and Pi using exe.dev's LLM integration, with AWS's aws-emulator skill
  • A Git project at ~/project whose AGENTS.md tells agents to use the emulator, never real AWS
What's running, and where
Private https://127.0.0.1:9297 the emulator's AWS API, on the VM only; signed with this run's keys
Private ssh -L 9297:127.0.0.1:9297 my-aws-emulator.exe.xyz the same API on your laptop, through an SSH tunnel
my-aws-emulator.exe.xyz exe.dev VM
AWS Emulator the aws-emulator Debian package from AWS, run as exedev by a systemd service :9297in memory only; keys and certificate in ~/.aws/emulator/9297
AWS CLI v2 AWS's official installer
Claude Code, Codex and Pi preinstalled; connected to exe.dev's LLM integration, with AWS's aws-emulator skill ~/.claude, ~/.codex
Your project an AGENTS.md that sends every AWS call to the emulator ~/project

Deploy with Shelley#

Click Deploy and Shelley will set up AWS Emulator on a new VM, then check that it works. No config-file wrangling required.

Prefer a terminal?
curl -fsSL \
  https://exe.dev/deploy/aws-emulator/prompt.txt \
  | ssh exe.dev new --prompt=/dev/stdin
What Shelley is told
Set up AWS Emulator on this VM by following the exe.dev recipe at https://exe.dev/deploy/aws-emulator.md from top to bottom.

The recipe's example VM name is my-aws-emulator. This VM's name is the output of `hostname`: use it wherever the recipe writes my-aws-emulator. Skip the step that creates the VM; you are on it.

You are done when all of these are true. Check each one yourself:
- AWS Emulator from AWS's package, running as `exedev` on 127.0.0.1:9297, back after a reboot
- The emulator's usage telemetry turned off
- The AWS CLI, and `~/aws-emulator.env`, which points the CLI and SDKs at the emulator
- `aws sts get-caller-identity` answers with the emulator's account, 000000000000
- Claude Code, Codex and Pi using exe.dev's LLM integration, with AWS's aws-emulator skill
- A Git project at `~/project` whose AGENTS.md tells agents to use the emulator, never real AWS

Run the VM-side commands in "Set up the VM" only; skip creating the VM.
If `exeuntu configure` reports an existing user config, leave it alone.
If `~/project` or its AGENTS.md already exists, add the AWS section to
it instead of replacing it. Never put real AWS credentials on this VM,
never bind the emulator to anything but 127.0.0.1, never run it with
`--auth off`, and never expose port 9297 through the HTTPS proxy.
Verify with the checks at the end of that section: the service is
active, the health check prints `ok`, the log says telemetry is
disabled, and `aws sts get-caller-identity` shows account 000000000000. Then run the demo in "Try it" once to prove
S3, DynamoDB and Lambda work. Skip the later sections and the FAQ.
Finish by telling the owner that everything in the emulator lives in
memory and is gone when the service restarts.

Finish with the URLs I should open.

Also at /deploy/aws-emulator/prompt.txt. The recipe it follows is /deploy/aws-emulator.md.

Set up the VM#

On your computer, create a VM and connect to it. Pick any free name; if you've already clicked Deploy or have a VM, skip the first command and use its name:

ssh exe.dev new --name=my-aws-emulator
ssh my-aws-emulator.exe.xyz

On the VM, install AWS Emulator from AWS's Debian package, and the AWS CLI with AWS's installer:

curl -fsSLo /tmp/aws-emulator.deb "https://downloads.aws.com/aws-emulator/packages/aws-emulator/LTS/latest/aws-emulator_$(dpkg --print-architecture).deb"
sudo apt-get install -y /tmp/aws-emulator.deb
curl -fsSLo /tmp/awscliv2.zip "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m).zip"
(cd /tmp && unzip -qo awscliv2.zip && sudo ./aws/install --update)
aws-emulator -v
aws --version

The emulator sends anonymous usage telemetry to AWS by default. Turn it off for every run on this VM, in the settings file AWS documents:

mkdir -p ~/.aws/emulator
printf '[telemetry]\nenabled = false\n' > ~/.aws/emulator/config.toml

Run the emulator as a service, as exedev. It listens on 127.0.0.1:9297 only, accepts only requests signed with this run's keys, and writes those keys, a config file and its certificate to ~/.aws/emulator/9297/. The memory cap is AWS's suggestion; the needrestart line stops Ubuntu's package upgrades from restarting it, which would wipe its state:

sudo tee /etc/systemd/system/aws-emulator.service >/dev/null <<EOF
[Unit]
Description=AWS Emulator
After=network.target

[Service]
User=$USER
Environment=HOME=$HOME
ExecStart=/usr/bin/aws-emulator
Restart=on-failure
MemoryMax=2G
MemorySwapMax=0

[Install]
WantedBy=multi-user.target
EOF
echo '$nrconf{override_rc}{qr(^aws-emulator\.service$)} = 0;' | sudo tee /etc/needrestart/conf.d/aws-emulator.conf
sudo systemctl daemon-reload
sudo systemctl enable --now aws-emulator

Write one file that points the AWS CLI and SDKs at it. These are the variables from AWS's guide, plus Node.js's certificate setting. They name files, not keys, so they keep working when the emulator restarts with new ones:

cat > ~/aws-emulator.env <<'EOF'
# Send the AWS CLI and SDKs to AWS Emulator on this VM, never to AWS.
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_PROFILE
export AWS_ENDPOINT_URL=https://127.0.0.1:9297
export AWS_SHARED_CREDENTIALS_FILE=$HOME/.aws/emulator/9297/emulator_credentials
export AWS_CONFIG_FILE=$HOME/.aws/emulator/9297/config
export AWS_CA_BUNDLE=$HOME/.aws/emulator/9297/ca.pem
export NODE_EXTRA_CA_CERTS=$HOME/.aws/emulator/9297/ca.pem
export AWS_EC2_METADATA_DISABLED=true
export AWS_DISABLE_HOST_PREFIX_INJECTION=true
export AWS_REGION=us-east-1
export AWS_DEFAULT_REGION=us-east-1
EOF
echo 'source ~/aws-emulator.env' >> ~/.bashrc

Connect the agents to exe.dev's LLM integration, install AWS's aws-emulator skill for them (it explains the emulator's errors and checks whether a project's AWS calls are covered), and make a project whose AGENTS.md says where AWS is:

exeuntu configure claude
exeuntu configure codex
env -u AWS_ENDPOINT_URL -u AWS_CONFIG_FILE -u AWS_SHARED_CREDENTIALS_FILE -u AWS_CA_BUNDLE \
  aws agent-toolkit add-skill --skill-name aws-emulator --region us-east-1
mkdir -p ~/project && cd ~/project && git init -q
cat >> AGENTS.md <<'EOF'
# AWS on this VM

This VM has no AWS account. AWS calls go to AWS Emulator, a local emulator
of the AWS APIs, which runs as the systemd service `aws-emulator` on
https://127.0.0.1:9297.

- Before any `aws`, `terraform`, `cdk`, `sam` or SDK command, run
  `source ~/aws-emulator.env` in the same shell.
- Don't install or start another emulator, and don't change the service.
- State lives in memory: `sudo systemctl restart aws-emulator` empties it
  and makes new keys. Ask before restarting it.
- Account 000000000000, Region us-east-1. Use one Region.
- An operation that answers 501 NotImplemented is an emulator gap, not a
  bug in this code.
EOF
[ -e CLAUDE.md ] || ln -s AGENTS.md CLAUDE.md

Check it. The service is active, telemetry is off, the health route says ok, and the AWS CLI talks to the emulator's account, 000000000000, with the user arn:aws:iam::000000000000:user/emulator:

source ~/aws-emulator.env
systemctl is-active aws-emulator
journalctl -u aws-emulator | grep -o 'telemetry: disabled'
curl -s --cacert "$AWS_CA_BUNDLE" https://127.0.0.1:9297/__admin/health; echo
aws sts get-caller-identity

Try it: a bucket, a table and a Lambda#

On the VM, in a shell where you've run source ~/aws-emulator.env (new logins do it for you):

aws s3 mb s3://demo-bucket
aws dynamodb create-table --table-name demo-table \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH --billing-mode PAY_PER_REQUEST
aws dynamodb wait table-exists --table-name demo-table

Now a Python Lambda that writes to both. Like AWS, the emulator checks the function's role, so give it a policy:

mkdir -p ~/demo-fn && cd ~/demo-fn
cat > app.py <<'EOF'
import boto3

s3 = boto3.client("s3")
table = boto3.resource("dynamodb").Table("demo-table")

def handler(event, context):
    name = event.get("name", "world")
    s3.put_object(Bucket="demo-bucket", Key=f"greetings/{name}.txt", Body=f"hello, {name}\n")
    table.put_item(Item={"id": name, "msg": "greeted"})
    return {"greeted": name}
EOF
rm -f fn.zip && python3 -m zipfile -c fn.zip app.py
aws iam create-role --role-name demo-fn --assume-role-policy-document \
  '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam put-role-policy --role-name demo-fn --policy-name demo --policy-document \
  '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:PutObject","dynamodb:PutItem"],"Resource":"*"}]}'
aws lambda create-function --function-name demo-fn --runtime python3.13 \
  --handler app.handler --role arn:aws:iam::000000000000:role/demo-fn --zip-file fileb://fn.zip
aws lambda wait function-active-v2 --function-name demo-fn
aws lambda invoke --function-name demo-fn --cli-binary-format raw-in-base64-out \
  --payload '{"name":"exe.dev"}' /tmp/out.json && cat /tmp/out.json; echo

The invoke prints {"greeted": "exe.dev"}, and the function's writes are there:

aws s3 cp s3://demo-bucket/greetings/exe.dev.txt -
aws dynamodb get-item --table-name demo-table --key '{"id":{"S":"exe.dev"}}'

The emulator runs Python and Node.js functions itself, with no Docker. As AWS warns, function code isn't isolated from the VM, so deploy only code you'd run yourself.

Let the agents build against it#

Start an agent in the project:

cd ~/project && claude

and give it something with AWS in it:

Write todo.py: store todo items in a DynamoDB table named todos (create it if it's missing) with boto3, and list them. Run it to add "buy milk".

It reads AGENTS.md, sources ~/aws-emulator.env, and creates the table in the emulator. In our test, Claude Code did it in one go. Codex (codex) and Pi (pi) read the same AGENTS.md. Shelley, exe.dev's agent at https://my-aws-emulator.shelley.exe.xyz/, works too if you point it at ~/project.

The skill can also answer "will this repo work on AWS Emulator?": it checks the AWS calls your code makes against AWS's fidelity tests.

Terraform#

Terraform's AWS provider reads the same variables, so a plain provider block targets the emulator. Pin the provider below 6.23: from 6.23, aws_s3_bucket reads tags through S3 Control, whose requests can't reach the emulator (issue #56).

terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 6.0, < 6.23.0" }
  }
}

provider "aws" {}

resource "aws_s3_bucket" "uploads" {
  bucket = "tf-demo-uploads"
}

resource "aws_dynamodb_table" "items" {
  name         = "tf-demo-items"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "id"
  attribute {
    name = "id"
    type = "S"
  }
}

We ran terraform apply and destroy with this (provider 6.22.1, and 5.x) on Terraform 1.17. Install Terraform from HashiCorp's apt repository first. Support for CloudFormation, CDK and SAM varies by service; check the fidelity scores on the releases page.

Use it from your laptop#

The emulator listens on the VM's 127.0.0.1 only, and its certificate is for 127.0.0.1. An SSH tunnel keeps both true on your laptop. You need the AWS CLI or an SDK there.

On your computer, copy this run's keys and certificate, and open the tunnel (it stays in the background until you close it):

mkdir -p ~/.aws/my-aws-emulator
scp 'my-aws-emulator.exe.xyz:.aws/emulator/9297/*' ~/.aws/my-aws-emulator/
ssh -fN -L 9297:127.0.0.1:9297 my-aws-emulator.exe.xyz

Then, in the shell that runs your code:

D=~/.aws/my-aws-emulator
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_PROFILE
export AWS_ENDPOINT_URL=https://127.0.0.1:9297 AWS_CONFIG_FILE=$D/config \
  AWS_SHARED_CREDENTIALS_FILE=$D/emulator_credentials AWS_CA_BUNDLE=$D/ca.pem \
  NODE_EXTRA_CA_CERTS=$D/ca.pem AWS_DISABLE_HOST_PREFIX_INJECTION=true \
  AWS_EC2_METADATA_DISABLED=true AWS_REGION=us-east-1 AWS_DEFAULT_REGION=us-east-1
aws sts get-caller-identity
aws s3 ls

You see the agents' buckets. Each restart of the emulator makes new keys and a new certificate, so run the scp again after one. If port 9297 is busy on your laptop, tunnel another local port (-L 9300:127.0.0.1:9297) and use it in AWS_ENDPOINT_URL.

Another exe.dev VM can open the same tunnel with an SSH key of its own that reaches only this VM: on your computer, cat key.pub | ssh exe.dev ssh-key add --vm=my-aws-emulator (see VM-scoped SSH keys). That key gets a shell on the emulator's VM, which holds nothing but fakes.

In CI#

For CI, AWS's advice is an emulator per job, which also gives every run clean state. On exe.dev, where CI jobs run on fresh copies of a base VM, set up the base VM with the install, the telemetry setting and ~/aws-emulator.env from Set up the VM, without the service. Each job then starts its own:

aws-emulator --idle-timeout 900 > /tmp/aws-emulator.log 2>&1 &
source ~/aws-emulator.env
for i in $(seq 30); do curl -sf --cacert "$AWS_CA_BUNDLE" https://127.0.0.1:9297/__admin/health && break; sleep 1; done
# run your tests here

--idle-timeout stops it after 15 minutes without a call, in case the job forgets.

When it's time for real AWS#

Keep real credentials off this VM; that's the point of it. When code is ready for an actual AWS account, deploy from a different VM with exe.dev's AWS integration: the VM gets short-lived credentials for one IAM role through workload identity federation, with no access keys on disk. Agent secrets covers the same idea for other APIs.

Restarts and starting over#

  • Clean slate on purpose: sudo systemctl restart aws-emulator. Every resource is gone; ~/aws-emulator.env keeps working because it names the files the new run writes.
  • A reboot or an update does the same. Keep what you need as code (Terraform, a seed script in ~/project) and run it again. A Terraform state file from before the restart still lists the old resources; the next terraform apply notices they're gone and creates them again.
  • The VM itself keeps the emulator, the CLI, your code and the agents' setup. For a copy of all of it, run ssh exe.dev cp my-aws-emulator my-aws-emulator-backup on your computer.

Update#

AWS ships releases several times a day, and security fixes only in the latest. Updating restarts the emulator, so its state goes too. On the VM:

curl -fsSLo /tmp/aws-emulator.deb "https://downloads.aws.com/aws-emulator/packages/aws-emulator/LTS/latest/aws-emulator_$(dpkg --print-architecture).deb"
sudo apt-get install -y /tmp/aws-emulator.deb
sudo systemctl restart aws-emulator
aws-emulator -v

To hold a version your tests pass on, install a numbered package from versions.json instead, as AWS's versioning guide shows.

Troubleshooting#

  • AccessDenied: Sign the request with the emulator credentials. Something supplied other keys. Run source ~/aws-emulator.env in that shell, which unsets AWS_ACCESS_KEY_ID and friends. On your laptop, copy the files again after an emulator restart.
  • Could not connect to the endpoint URL. The service is down or restarting: systemctl status aws-emulator and journalctl -u aws-emulator -e. On your laptop, check the tunnel.
  • Certificate errors. The client doesn't read AWS_CA_BUNDLE. The env file sets NODE_EXTRA_CA_CERTS for Node.js too; Rust and .NET need SSL_CERT_FILE, and Java the truststore.p12 settings from AWS's guide. Use 127.0.0.1, not localhost, in the endpoint, or some SDKs put the bucket name in the host name.
  • ResourceNotFoundException right after a create. As on AWS, tables and functions start as CREATING or Pending; use aws dynamodb wait table-exists or aws lambda wait function-active-v2.
  • 501 NotImplemented, or a call that behaves unlike AWS. A gap. Look in the repository's issues folder and report it on GitHub. Inside a Lambda, the bundled SDK makes only some calls; an error SdkCallNotMade names the ones it supports.
  • Everything's gone. The service restarted: journalctl -u aws-emulator | grep 'starting' shows when.

Frequently asked questions#

What is AWS Emulator?

AWS's own emulator of the AWS APIs, released as open source (CC0) in September 2026 and on the front page of Hacker News in November. One program serves every service on one HTTPS endpoint. You point the AWS CLI or an SDK at it with environment variables, and your code doesn't change. No AWS account is needed, and it costs nothing.

Does it keep my buckets and tables after a restart?

No. The emulator keeps everything in memory, and AWS says it isn't built for long-running use. A restart of the service, a reboot or an update empties it and makes new keys and a new certificate. What the VM keeps is everything around it: the emulator, your code, your Terraform and seed scripts, and the agents' setup, so getting back to a known state is one terraform apply away.

How is it different from LocalStack?

It's written by AWS, generated from AWS's API models, covers every service with a public model (operations it hasn't built yet answer 501 NotImplemented), and AWS publishes per-service fidelity scores with each release. It runs natively on Linux with no Docker. It's also new: releases come several times a day, and gaps are tracked in the repository's issues/ folder.

Can I put it behind exe.dev's HTTPS proxy or make it public?

Don't. AWS says not to open its ports to the internet. It serves HTTPS with a certificate for 127.0.0.1 only, Lambda functions run as your user on the VM, and AWS SDKs can't sign in to exe.dev's proxy anyway. Use it on the VM, or through an SSH tunnel, which keeps the address 127.0.0.1 and uses your exe.dev SSH key.

Does it send data to AWS?

Not your requests, responses or resources. By default it sends anonymous usage and error telemetry; this setup turns that off for every run on the VM in ~/.aws/emulator/config.toml. To turn it back on, delete that file and restart the service.

What does it cost?

The emulator is free and needs no AWS account. You pay for the VM as part of your exe.dev plan, and agent model usage through the LLM integration is billed to your exe.dev account. The emulator uses about 160 MiB idle.

Updated Nov 14, 2026 · tested end to end Nov 13, 2026 · Markdown · manifest.json · Something wrong? Tell us