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.
Shelley handles the setup. About 10 minutes.
Prefer a terminal?
curl -fsSL https://exe-staging.dev/deploy/aws-emulator/prompt.txt | ssh exe.dev new --prompt=/dev/stdinAWS 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
exedevon 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-identityanswers 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
~/projectwhose AGENTS.md tells agents to use the emulator, never real AWS
What's running, and where
https://127.0.0.1:9297
the emulator's AWS API, on the VM only; signed with this run's keys
ssh -L 9297:127.0.0.1:9297 my-aws-emulator.exe.xyz
the same API on your laptop, through an SSH tunnel
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-staging.dev/deploy/aws-emulator/prompt.txt \
| ssh exe.dev new --prompt=/dev/stdinWhat Shelley is told
Set up AWS Emulator on this VM by following the exe.dev recipe at https://exe-staging.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.envkeeps 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 nextterraform applynotices 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-backupon 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. Runsource ~/aws-emulator.envin that shell, which unsetsAWS_ACCESS_KEY_IDand 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-emulatorandjournalctl -u aws-emulator -e. On your laptop, check the tunnel.- Certificate errors. The client doesn't read
AWS_CA_BUNDLE. The env file setsNODE_EXTRA_CA_CERTSfor Node.js too; Rust and .NET needSSL_CERT_FILE, and Java thetruststore.p12settings from AWS's guide. Use127.0.0.1, notlocalhost, in the endpoint, or some SDKs put the bucket name in the host name. ResourceNotFoundExceptionright after a create. As on AWS, tables and functions start asCREATINGorPending; useaws dynamodb wait table-existsoraws 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 errorSdkCallNotMadenames 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