Nimbus
A free, open-source AWS emulator for local development. Forever.
Nimbus runs S3, SQS, DynamoDB, Secrets Manager, SSM Parameter Store, SES, Lambda, and API Gateway locally in a single Docker container on port 4566 — a drop-in replacement for LocalStack Community Edition. No account. No auth token. No commercial restrictions. MIT licensed.
Why Nimbus?
LocalStack built something genuinely useful on the backs of open-source contributors, then locked it behind a paywall. Nimbus exists because local AWS emulation should be free for everyone — individual developers, startups, enterprises, and open-source projects alike.
"Free for everyone, forever."
Quickstart
docker run -p 4566:4566 ghcr.io/nimbus-local/nimbus:latest
Or with Docker Compose:
services:
nimbus:
image: ghcr.io/nimbus-local/nimbus:latest
ports:
- "4566:4566"
environment:
AWS_DEFAULT_REGION: us-east-1
volumes:
- nimbus_data:/var/lib/nimbus
dynamodb-local:
image: amazon/dynamodb-local:latest
command: "-jar DynamoDBLocal.jar -sharedDb -dbPath /data"
volumes:
- dynamodb_data:/data
volumes:
nimbus_data:
dynamodb_data:
Services
| Service |
Status |
Detection |
Docs |
| S3 |
✅ Core |
catch-all (path / virtual-hosted) |
PutObject, GetObject, DeleteObject, ListObjectsV2, HeadObject, CreateBucket, DeleteBucket, multipart uploads, presigned URLs |
| S3 Control |
✅ Core |
x-amz-account-id header or /v20180820/ path prefix |
Account-level tag and public access block stubs — accepts Pulumi/TF provider calls as no-ops |
| SQS |
✅ Core |
Action param or AmazonSQS.* target |
CreateQueue, SendMessage, ReceiveMessage, DeleteMessage, PurgeQueue, visibility timeout |
| DynamoDB |
✅ Full |
DynamoDB_* target |
Proxied to DynamoDB Local — full parity |
| Secrets Manager |
✅ Core |
secretsmanager.* target |
CreateSecret, GetSecretValue, PutSecretValue, UpdateSecret, DeleteSecret, ListSecrets, DescribeSecret, RestoreSecret |
| SSM Parameter Store |
✅ Core |
AmazonSSM.* target |
PutParameter, GetParameter, GetParameters, GetParametersByPath, DeleteParameter, DescribeParameters — String, StringList, SecureString, path hierarchy, versioning |
| SES |
✅ Core |
AmazonSimpleEmailService.* target or /v2/email/ path |
SendEmail (v1+v2), SendRawEmail, VerifyEmailIdentity, ListIdentities — emails captured in memory, never sent |
| Lambda |
✅ Core |
/2015-03-31/ path prefix |
Functions (CRUD, versions, publish), invocations, aliases, permissions, event source mappings, concurrency, layers, code signing, function URLs, event invoke config, runtime & recursion settings, tags |
| AppSync |
✅ Core |
/v1/apis path prefix (mgmt); /_appsync/{id}/graphql or {id}.appsync-api.{region}.nimbus.local (exec) |
GraphQL API (CRUD), schema upload, Lambda/NONE data sources, resolvers, API keys, tags — management plane + GraphQL execution (Lambda invocation, basic VTL: $context.arguments, $context.result, $util.toJson) |
| API Gateway |
✅ Core |
/restapis (REST v1), /apis (HTTP v2 + WebSocket) |
REST API: resources, methods, integrations (AWS_PROXY + MOCK), stages. HTTP API: routes, integrations (AWS_PROXY, payload format v1+v2), stages, $default catch-all. WebSocket API: control plane (CRUD), $connect/$disconnect/$default routes, routeSelectionExpression — data plane (WS upgrade + Lambda dispatch) in progress |
| ECR |
✅ Core |
AmazonEC2ContainerRegistry_V20150921.* target or /v2/ path |
CreateRepository, GetAuthorizationToken, ListImages, BatchDeleteImage, BatchGetImage + full Docker V2 registry (push/pull blobs and manifests) |
| ECS |
✅ Core |
AmazonEC2ContainerServiceV20141113.* target |
Clusters (CRUD + settings), task definitions (register/deregister/describe/list), tasks (run/stop/describe/list), services (CRUD) — tasks simulated as immediately RUNNING; containerInsights clusters publish performance events to CloudWatch Logs |
| EC2 (VPC) |
✅ Core |
POST / form-encoded body |
VPC, Subnet, Internet Gateway, Security Group (create/delete + default), Route Table, Route, RouteTableAssociation, Tags, DescribeAvailabilityZones — full lifecycle for Pulumi NewVpc construct |
| IAM |
✅ Core |
form-encoded body, Version=2010-05-08 |
CreateRole/GetRole/DeleteRole/ListRoles, policy attachments, inline policies, managed policies, instance profiles; no enforcement — AssumeRole always succeeds |
| CloudWatch Logs |
✅ Core |
Logs_20140328.* target |
CreateLogGroup/DeleteLogGroup/DescribeLogGroups, CreateLogStream/DescribeLogStreams, PutLogEvents, GetLogEvents/FilterLogEvents with JSON, space-delimited, and term filter patterns |
| KMS |
✅ Core |
TrentService.* target |
CreateKey, Encrypt/Decrypt (real AES-256-GCM), GenerateDataKey, ReEncrypt, aliases, tags, key lifecycle (enable/disable/schedule-deletion) |
| SNS |
✅ Core |
AmazonSimpleNotificationService.* target or Action param |
CreateTopic, Subscribe (all protocols, auto-confirmed), Publish, PublishBatch — messages captured in memory, never delivered |
| EventBridge |
✅ Core |
AmazonEventBridge.* target |
PutEvents (captured in memory), event buses (CRUD), rules (CRUD, enable/disable), targets (put/remove/list) |
| EventBridge Scheduler |
✅ Core |
/schedules or /schedule-groups bare path prefix |
Schedule groups (CRUD), schedules (CRUD), tags — expressions stored; firing added in Part 2 |
| CloudFront |
✅ Core |
/2020-05-31/ path prefix |
Distribution CRUD — localhost-based DomainName, status always Deployed, ETag per distribution |
| ALB |
✅ Core |
form-encoded body, Version=2015-12-01 |
Load balancer, target group, listener, and rule CRUD — localhost-based DNSName, state always active; listeners start a real reverse proxy that routes requests to registered IP targets |
| RDS / Aurora |
✅ Core |
form-encoded body, Version=2014-10-31 |
Subnet group, parameter group, cluster, and instance CRUD — cluster endpoint resolves to a real Postgres sidecar; status always available; Describe Filters supported (db-instance-id, db-cluster-id); Performance Insights attributes round-trip (DbiResourceId, PerformanceInsightsEnabled, KMS key, retention); subnet groups record their subnets and block deletion of a subnet a DB still uses; DB Proxy CRUD with a default target group and cluster/instance targets |
| Performance Insights |
✅ Core |
X-Amz-Target: PerformanceInsightsv20180227.* |
GetResourceMetrics, DescribeDimensionKeys, ListAvailableResourceMetrics/Dimensions, GetResourceMetadata — deterministic synthetic load data keyed off RDS DbiResourceId |
| ElastiCache |
✅ Core |
form-encoded body, Version=2015-02-02 |
Subnet group, parameter group, cache cluster, and replication group CRUD — endpoints resolve to a real Valkey sidecar; status always available |
| ACM |
✅ Core |
X-Amz-Target: CertificateManager.* |
Certificate CRUD — RequestCertificate generates a real self-signed cert; status always ISSUED; PEM downloadable via /_nimbus/acm/certs/{arn} |
| Route 53 |
✅ Core |
/2013-04-01/ path prefix |
Hosted zone and record set CRUD — GetChange always returns INSYNC; no DNS resolution |
| CloudWatch Metrics |
✅ Core |
X-Amz-Target: GraniteServiceVersion20100801.* |
PutMetricData, ListMetrics (with name-only and name+value dimension filters), GetMetricStatistics, GetMetricData, metric alarms (structural, always OK), tags |
| Cognito |
✅ Core |
X-Amz-Target: AWSCognitoIdentityProviderService.* |
User pool and client CRUD, tags — infra lifecycle for terraform apply; auth flows (JWT) in Phase 2 |
| Kinesis |
✅ Core |
X-Amz-Target: Kinesis_20131202.* |
Stream CRUD, PutRecord/PutRecords, GetShardIterator, GetRecords — in-memory ring buffer per shard; partition key hashed to shard via MD5; Lambda ESM runner (1 s poll, full Kinesis event envelope) |
| Step Functions |
✅ Core |
X-Amz-Target: AWSStepFunctions.* |
CreateStateMachine / StartExecution; Pass, Succeed, Fail, Choice, Wait, Task (→Lambda), Parallel, Map; Catch + Retry |
| Internal Dev APIs |
✅ Full |
/_nimbus/ path prefix |
State dump, reset, health, live Lambda registration (forge dev tunnel), per-service inspection endpoints |
Using the AWS SDK
Point your AWS SDK at http://localhost:4566. Nimbus accepts any credentials.
Python (boto3):
import boto3
s3 = boto3.client(
"s3",
endpoint_url="http://localhost:4566",
aws_access_key_id="test",
aws_secret_access_key="test",
region_name="us-east-1",
)
s3.create_bucket(Bucket="my-bucket")
JavaScript (AWS SDK v3):
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "http://localhost:4566",
region: "us-east-1",
credentials: { accessKeyId: "test", secretAccessKey: "test" },
forcePathStyle: true,
});
Go:
cfg, _ := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("us-east-1"),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("test", "test", "")),
config.WithEndpointResolverWithOptions(
aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{URL: "http://localhost:4566"}, nil
}),
),
)
AWS CLI v2:
Set AWS_ENDPOINT_URL once and omit --endpoint-url from every command:
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
aws s3 mb s3://my-bucket
aws sqs create-queue --queue-name my-queue
aws lambda invoke --function-name my-func --payload '{}' --cli-binary-format raw-in-base64-out response.json
These variables are exported automatically when using the infra/ dev harness (make start).
nimbuslocal CLI
nimbuslocal is a thin wrapper around the aws CLI that automatically injects the Nimbus endpoint. It's a drop-in replacement for awslocal.
nimbuslocal s3 mb s3://my-bucket
nimbuslocal sqs create-queue --queue-name my-queue
nimbuslocal dynamodb list-tables
nimbuslocal secretsmanager create-secret --name /myapp/db-password --secret-string "secret"
nimbuslocal ssm put-parameter --name /myapp/db-host --value localhost --type String
nimbuslocal ses verify-email-identity --email-address sender@example.com
nimbuslocal lambda invoke --function-name my-func --payload '{}' response.json
nimbuslocal apigateway create-rest-api --name my-api
Install:
curl -fsSL https://raw.githubusercontent.com/nimbus-local/nimbus/master/install.sh | sh
The script detects your OS and architecture, downloads the right binary to ~/.local/bin, and adds it to your shell profile automatically.
Or install manually with Go:
go install github.com/nimbus-local/nimbus/cmd/nimbuslocal@latest
# ensure $GOPATH/bin is on your PATH
export PATH="$PATH:$HOME/go/bin"
To uninstall:
# If installed via the script:
rm ~/.local/bin/nimbuslocal
# Then remove the managed block from your shell profile (~/.zshrc etc.):
# ### MANAGED BY NIMBUSLOCAL START (DO NOT EDIT)
# ...
# ### MANAGED BY NIMBUSLOCAL END (DO NOT EDIT)
# If installed via go install:
rm ~/go/bin/nimbuslocal
Configuration
All configuration is via environment variables:
| Variable |
Default |
Description |
NIMBUS_PORT |
4566 |
Edge port — advertised in generated SQS queue URLs and followed by the container HEALTHCHECK, so docker compose up --wait works on non-default ports (EXPOSE 4566 remains documentation of the default) |
NIMBUS_EXTERNAL_URL |
(none) |
Base URL advertised in generated resource URLs (SQS queue URLs) when it differs from the listen address — reverse proxies, remapped Docker ports |
NIMBUS_DATA_DIR |
/var/lib/nimbus (Docker) |
Storage root for S3 objects |
AWS_DEFAULT_REGION |
us-east-1 |
Default region |
NIMBUS_DYNAMODB_ENDPOINT |
http://dynamodb-local:8000 |
DynamoDB Local sidecar URL |
NIMBUS_LOG_LEVEL |
info |
debug, info, warn, error |
SERVICES |
(all) |
Comma-separated list to enable |
NIMBUS_ENDPOINT_URL |
http://localhost:4566 |
Used by nimbuslocal CLI |
Health Check
GET /_nimbus/health
GET /_localstack/health (alias for LocalStack compatibility)
{"status":"running","services":["dynamodb","lambda","apigateway","ses","secretsmanager","ssm","sqs","s3"]}
Migrating from LocalStack
- Replace
localstack/localstack with ghcr.io/nimbus-local/nimbus in your docker-compose.yml
- Add the
dynamodb-local sidecar if you use DynamoDB
- Change
S3_ENDPOINT_URL (or equivalent) from http://localstack:4566 to http://nimbus:4566
- Replace
awslocal with nimbuslocal in scripts
- That's it. The port, credential handling, and API responses are compatible.
Architecture
Nimbus is a single Go binary. All AWS service traffic enters on port 4566. The edge router inspects each request — via X-Amz-Target header, Action query param, or URL path — and dispatches to the appropriate service handler. S3 is the catch-all and is always registered last.
Each service is a self-contained package implementing a simple Service interface. Adding a new service means implementing the interface and registering it in cmd/nimbus/main.go — nothing else changes.
internal/
router/ # Edge router — detects and dispatches
services/
s3/ # S3 implementation (filesystem-backed)
sqs/ # SQS (in-memory)
dynamodb/ # DynamoDB proxy to DynamoDB Local
secretsmanager/ # Secrets Manager (in-memory)
ssm/ # SSM Parameter Store (in-memory)
ses/ # SES — captures emails, never sends
lambda/ # Lambda REST API
apigateway/ # API Gateway management + execute-api
auth/ # Credential extraction (accepts anything)
config/ # Environment-based configuration
uid/ # UUID generation
cmd/
nimbus/ # Server entrypoint
nimbuslocal/ # AWS CLI wrapper
docs/
services/ # Per-service API reference
Local development
The infra/ directory contains a full dev harness: Docker Compose, Terraform fixtures, and a smoke test script that exercises every service end-to-end.
Prerequisites: Docker, Terraform, AWS CLI v2.
cd infra
| Goal |
Command |
| Start Nimbus + DynamoDB Local |
make start |
| Provision all test resources |
make apply |
| Run smoke tests |
make smoke-test |
| Provision + smoke test in one step |
make test |
| Rebuild after Go changes |
make stop && make start && make apply |
| Tear everything down |
make clean |
make apply is idempotent — safe to re-run. All AWS credentials are set automatically to dummy values so no environment setup is required.
Contributing
PRs welcome. If you're adding a new AWS service, implement the services.Service interface in internal/services/<n>/ and register it in cmd/nimbus/main.go.
Please keep the spirit of the project: no accounts, no tokens, no telemetry, no commercial restrictions. MIT licensed contributions only.
See CONTRIBUTING.md for details.
License
MIT — see LICENSE.
This project is not affiliated with Amazon Web Services or LocalStack.