AWS Emulator tests AWS applications locally without cloud cost

- Reading
- 7 min
- Code
- 7
AWS released a free emulator that runs locally and responds to the same API calls as S3, DynamoDB, and other services, letting you run tests and code agents without waiting for cloud deploys.
What is the AWS Emulator
AWS announced the AWS Emulator, a local emulator for AWS services that runs entirely on your machine. It exposes a local HTTPS endpoint that speaks the same APIs the AWS CLI, AWS SDKs, and code agents (such as Kiro or Claude Code) already use against the real cloud. The difference is that the calls never leave your laptop.
There is no additional service fee to run the emulator: you only pay for AWS resources when you actually deploy to the cloud. The source code is on GitHub, and installation is via an RPM or Debian package on Linux; on macOS and Windows, Amazon does not publish a ready-made image, and you need to build a Docker image from a Dockerfile available in the documentation.
Installing and starting the emulator
On Amazon Linux 2023, installation is straightforward via dnf, pulling the package from an AWS downloads address:
PKG=https://downloads.aws.com/aws-emulator/packages/aws-emulator/LTS/latest
sudo dnf install -y $PKG/aws-emulator-x86_64.rpm
aws-emulator -vThe package registers the aws-emulator binary in /usr/bin/, so it's already on the PATH. To actually start the server, run the command without the flag:
aws-emulatorThe process starts an HTTPS server at 127.0.0.1:9297, generates its own credentials, a configuration file, and a certificate, and writes everything to ~/.aws/emulator/9297/. The log is output as JSON lines and shows the address, the config file, and the certificate. Separately, the emulator exposes the /__admin/health endpoint, which CI scripts can use to wait for the service to be ready before running tests.
To point the AWS CLI at the emulator instead of the real cloud, you need to export a set of environment variables and, above all, remove any real credentials from the shell:
export AWS_ENDPOINT_URL=https://127.0.0.1:9297
export AWS_SHARED_CREDENTIALS_FILE=~/.aws/emulator/9297/emulator_credentials
export AWS_CONFIG_FILE=~/.aws/emulator/9297/config
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
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_PROFILEThe access keys generated by the emulator start with EMULATOR, on purpose, so no one mistakes them for a real AWS key. Any request signed with a different key is rejected. To confirm the shell is talking to the emulator and not the cloud, you can check the health endpoint and the caller identity:
curl --cacert ~/.aws/emulator/9297/ca.pem https://127.0.0.1:9297/__admin/health
aws sts get-caller-identityThe emulator works with a single fixed account, 000000000000. That's how you know, without ambiguity, that you're testing locally and not in production.
Running real commands against the emulator
With the endpoint configured, the AWS CLI commands you already use keep working without any application code changes. A test that creates a bucket, uploads an object, and lists its contents runs exactly as it would in the cloud:
aws s3 mb s3://local-getting-started
echo "hello from the emulator" > hello.txt
aws s3 cp hello.txt s3://local-getting-started/
aws s3 ls s3://local-getting-started/The same applies to more elaborate DynamoDB flows, including ones a code agent runs on its own. In the announcement itself, AWS shows Kiro creating a table, writing an item, and reading it back, all against the emulator instead of the real account:
aws dynamodb create-table --table-name kiro-demo \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
aws dynamodb put-item --table-name kiro-demo \
--item '{"id": {"S": "item-1"}, "msg": {"S": "hello from kiro"}}'
aws dynamodb get-item --table-name kiro-demo \
--key '{"id": {"S": "item-1"}}'For those who already have a test suite, the path is the same: start the emulator before the job, export the same variables in the CI script that runs the tests, and let the suite run without any code changes.
Embedded mode for real testing
Running the emulator as a separate process works well for exploring commands manually, but in an automated test suite it's best not to depend on an external server. That's what the embedded mode is for: it instantiates the emulator inside the test process itself and connects directly to the SDK's HTTP client, without going over the network.
Each embedded instance keeps its own isolated state, so tests don't share a bucket, table, or queue with each other, and you can run several in parallel without interference. Today this mode is only available for the AWS SDK for Rust, via the aws-emulator-embedded crate, added under [dev-dependencies] in Cargo.toml:
use aws_emulator_embedded::{AwsEmulatorEmbedded, EmbeddedOptions};
use aws_sdk_dynamodb::types::{
AttributeDefinition, AttributeValue, KeySchemaElement, KeyType,
ScalarAttributeType, BillingMode,
};
#[tokio::test]
async fn put_then_get_returns_the_item() {
let emulator = AwsEmulatorEmbedded::new(EmbeddedOptions::default());
let config = aws_config::defaults(aws_config::BehaviorVersion::latest())
.http_client(emulator.http_client())
.region(aws_config::Region::new("us-east-1"))
.credentials_provider(emulator.aws_credentials_provider())
.load()
.await;
let ddb = aws_sdk_dynamodb::Client::new(&config);
ddb.create_table()
.table_name("emulator-demo-table")
.attribute_definitions(
AttributeDefinition::builder()
.attribute_name("id")
.attribute_type(ScalarAttributeType::S)
.build().unwrap())
.key_schema(
KeySchemaElement::builder()
.attribute_name("id")
.key_type(KeyType::Hash)
.build().unwrap())
.billing_mode(BillingMode::PayPerRequest)
.send().await.unwrap();
ddb.put_item()
.table_name("emulator-demo-table")
.item("id", AttributeValue::S("item-1".into()))
.item("msg", AttributeValue::S("hi".into()))
.send().await.unwrap();
let got = ddb.get_item()
.table_name("emulator-demo-table")
.key("id", AttributeValue::S("item-1".into()))
.send().await.unwrap();
assert_eq!(got.item().unwrap().get("msg").unwrap().as_s().unwrap(), "hi");
}What broke along the way
When the emulator runs as a separate process, not every SDK trusts the automatically generated certificate right out of the box. The AWS SDK for Java, for example, requires configuring a trust store pointing to the emulator's certificate before any call works, which isn't obvious on the first try.
Another point of friction is operation coverage. If the emulator doesn't yet implement a specific service call, it returns HTTP 501 (Not Implemented), and the test fails immediately instead of simulating behavior similar to AWS's. This is good because it avoids false positives, but it means not every suite runs on the first try without adjustments.
How AWS measures how trustworthy the emulator is
An emulator is only worth it if a test that passes on it also passes on real AWS. To ensure that, the team publishes fidelity tests: short programs that use an AWS SDK to run a real flow (for example, writing an object, reading it, and then deleting it) and compare the emulator's response with the real AWS response, including status code, body, and the exact error message.
With each release, AWS publishes the fidelity score per service in a k-of-n format: 40 of 50, for example, means 40 of the 50 tests for that service pass. The full test set is public in the aws-emulator-evaluation-suite repository on GitHub, including the ones that still fail, so you can audit exactly what was verified and run the tests yourself.
In short: a low score doesn't mean a regression; it may mean the team just added stricter tests for a service that is, in practice, improving.
Security and limits that matter for whoever runs this
The emulator is built and maintained by AI agents based on AWS's API models, using Amazon Bedrock AgentCore Runtime Instances, AWS CodePipeline, and AWS CodeCommit. One group of agents identifies gaps by comparing the emulator with real AWS and with the published Smithy models; another group implements the fix; and an independent security agent adversarially reviews every change, both before and after it goes into the release pipeline.
That doesn't excuse carelessness from whoever uses the tool. By default, the emulator only listens on 127.0.0.1 and rejects any request not signed with its own generated keys, but the security of your usage remains a shared responsibility. Three limits deserve attention before trusting the emulator too much:
- It does not evaluate IAM policies or resource-based policies the way AWS does, so a call that AWS would deny may succeed on the emulator.
- Each emulator instance acts as a single account (
000000000000), with no cross-account or cross-Region behavior. - Fidelity varies by service and by operation, so it's worth checking the score before blindly trusting a less-covered service.
AWS itself is direct about this: don't use the emulator to test whether your application is secure, and keep real credentials out of the test job environment. Real security testing still happens against real AWS before production.
What changes for those working in DevOps and SRE
The practical gain is in the feedback cycle. A code agent, or a CI pipeline, that today waits for a deploy to a Region to check whether a DynamoDB table was created correctly can now run that same test in milliseconds, with no deploy queue and no cost for provisioned resources. This lowers the cost of iterating: less CI time, less resource provisioned just for testing, less friction in the local development loop.
The trade-off is clear and documented by AWS itself: partial fidelity and the lack of IAM evaluation mean the emulator shortens the cycle, but it doesn't replace security validation and access policy checks against the real account.
For those deciding on a test pipeline, the practical rule is to separate layers: use the emulator to quickly validate application logic and API integration, and keep a stage against real AWS (or at least against a staging account) before anything goes to production. It's the same principle as always: automate what is repetitive and deterministic, measure what still depends on real cloud behavior, and don't trade security validation for loop speed.
Translated from the Brazilian Portuguese original · Read the original
Pod Security Admission Replaced PodSecurityPolicy in Kubernetes: What to Reconfigure Before the Next Upgrade
PodSecurityPolicy was removed from Kubernetes in an update to the project's most recent versions, and Pod Security Admission, stable since v1.25, is the native mechanism that took its place. Platform teams that postponed the migration risk silently breaking workloads on the next cluster upgrade.
