Free, open-source local AWS emulator — drop-in LocalStack replacement
Project description
MiniStack — Free, Open-Source Local AWS Emulator
LocalStack is no longer free. MiniStack is a fully open-source, zero-cost drop-in replacement. Single port · No account · No license key · No telemetry · Just AWS APIs, locally.
Why MiniStack?
LocalStack recently moved its core services behind a paid plan. If you relied on LocalStack Community for local development and CI/CD pipelines, MiniStack is your free alternative.
- 23 AWS services emulated on a single port (4566)
- Drop-in compatible — works with
boto3, AWS CLI, Terraform, CDK, Pulumi, any SDK - Real infrastructure — RDS spins up actual Postgres/MySQL containers, ElastiCache spins up real Redis, Athena runs real SQL via DuckDB, ECS runs real Docker containers
- Tiny footprint — ~150MB image, ~30MB RAM at idle vs LocalStack's ~1GB image and ~500MB RAM
- Fast startup — under 2 seconds
- MIT licensed — use it, fork it, contribute to it
Quick Start
# Option 1: Docker Hub (recommended)
docker run -p 4566:4566 nahuelnucera/ministack
# Option 2: Clone and build
git clone https://github.com/Nahuel990/ministack
cd ministack
# Start with Docker Compose (includes Redis sidecar)
docker compose up -d
# Verify
curl http://localhost:4566/_localstack/health
That's it. No account, no API key, no sign-up.
Using with AWS CLI
# Configure a local profile (one-time)
aws configure --profile local
# AWS Access Key ID: test
# AWS Secret Access Key: test
# Default region: us-east-1
# Default output format: json
# Use --endpoint-url on any command
aws --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket
aws --endpoint-url=http://localhost:4566 s3 cp ./file.txt s3://my-bucket/
aws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name my-queue
aws --endpoint-url=http://localhost:4566 dynamodb list-tables
aws --endpoint-url=http://localhost:4566 sts get-caller-identity
# Or set the endpoint globally for a session
export AWS_ENDPOINT_URL=http://localhost:4566
aws s3 ls
aws sqs list-queues
awslocal wrapper
chmod +x bin/awslocal
./bin/awslocal s3 ls
./bin/awslocal dynamodb list-tables
Using with boto3
import boto3
# All clients use the same endpoint
def client(service):
return boto3.client(
service,
endpoint_url="http://localhost:4566",
aws_access_key_id="test",
aws_secret_access_key="test",
region_name="us-east-1",
)
# S3
s3 = client("s3")
s3.create_bucket(Bucket="my-bucket")
s3.put_object(Bucket="my-bucket", Key="hello.txt", Body=b"Hello, MiniStack!")
obj = s3.get_object(Bucket="my-bucket", Key="hello.txt")
print(obj["Body"].read()) # b'Hello, MiniStack!'
# SQS
sqs = client("sqs")
q = sqs.create_queue(QueueName="my-queue")
sqs.send_message(QueueUrl=q["QueueUrl"], MessageBody="hello")
msgs = sqs.receive_message(QueueUrl=q["QueueUrl"])
print(msgs["Messages"][0]["Body"]) # hello
# DynamoDB
ddb = client("dynamodb")
ddb.create_table(
TableName="Users",
KeySchema=[{"AttributeName": "userId", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "userId", "AttributeType": "S"}],
BillingMode="PAY_PER_REQUEST",
)
ddb.put_item(TableName="Users", Item={"userId": {"S": "u1"}, "name": {"S": "Alice"}})
# SSM Parameter Store
ssm = client("ssm")
ssm.put_parameter(Name="/app/db/host", Value="localhost", Type="String")
param = ssm.get_parameter(Name="/app/db/host")
print(param["Parameter"]["Value"]) # localhost
# Secrets Manager
sm = client("secretsmanager")
sm.create_secret(Name="db-password", SecretString='{"password":"s3cr3t"}')
# Kinesis
kin = client("kinesis")
kin.create_stream(StreamName="events", ShardCount=1)
kin.put_record(StreamName="events", Data=b'{"event":"click"}', PartitionKey="user1")
# EventBridge
eb = client("events")
eb.put_events(Entries=[{
"Source": "myapp",
"DetailType": "UserSignup",
"Detail": '{"userId": "123"}',
"EventBusName": "default",
}])
# Step Functions
sfn = client("stepfunctions")
sfn.create_state_machine(
name="my-workflow",
definition='{"StartAt":"Hello","States":{"Hello":{"Type":"Pass","End":true}}}',
roleArn="arn:aws:iam::000000000000:role/role",
)
Supported Services
Core Services
| Service | Operations | Notes |
|---|---|---|
| S3 | CreateBucket, DeleteBucket, ListBuckets, HeadBucket, PutObject, GetObject, DeleteObject, HeadObject, CopyObject, ListObjects v1/v2, DeleteObjects, GetBucketVersioning, PutBucketVersioning, GetBucketEncryption, PutBucketEncryption, DeleteBucketEncryption, GetBucketLifecycleConfiguration, PutBucketLifecycleConfiguration, DeleteBucketLifecycle, GetBucketCors, PutBucketCors, DeleteBucketCors, GetBucketAcl, PutBucketAcl, GetBucketTagging, PutBucketTagging, DeleteBucketTagging, GetBucketPolicy, PutBucketPolicy, DeleteBucketPolicy, GetBucketNotificationConfiguration, PutBucketNotificationConfiguration, GetBucketLogging, PutBucketLogging, ListObjectVersions, CreateMultipartUpload, UploadPart, CompleteMultipartUpload, AbortMultipartUpload | Optional disk persistence via S3_PERSIST=1 |
| SQS | CreateQueue, DeleteQueue, ListQueues, GetQueueUrl, GetQueueAttributes, SetQueueAttributes, PurgeQueue, SendMessage, ReceiveMessage, DeleteMessage, ChangeMessageVisibility, ChangeMessageVisibilityBatch, SendMessageBatch, DeleteMessageBatch, TagQueue, UntagQueue, ListQueueTags | Both Query API and JSON protocol; FIFO queues with deduplication; DLQ support |
| SNS | CreateTopic, DeleteTopic, ListTopics, GetTopicAttributes, SetTopicAttributes, Subscribe, Unsubscribe, ListSubscriptions, ListSubscriptionsByTopic, GetSubscriptionAttributes, SetSubscriptionAttributes, ConfirmSubscription, Publish, PublishBatch, TagResource, UntagResource, ListTagsForResource, CreatePlatformApplication, CreatePlatformEndpoint | SNS→SQS fanout delivery; SNS→Lambda fanout (synchronous invocation) |
| DynamoDB | CreateTable, UpdateTable, DeleteTable, DescribeTable, ListTables, PutItem, GetItem, DeleteItem, UpdateItem, Query, Scan, BatchWriteItem, BatchGetItem, TransactWriteItems, TransactGetItems, DescribeTimeToLive, UpdateTimeToLive, DescribeContinuousBackups, UpdateContinuousBackups, DescribeEndpoints, TagResource, UntagResource, ListTagsOfResource | TTL enforced via background reaper (60s cadence) |
| Lambda | CreateFunction, DeleteFunction, GetFunction, ListFunctions, Invoke, UpdateFunctionCode, UpdateFunctionConfiguration, AddPermission, RemovePermission, ListVersionsByFunction, PublishVersion, TagResource, UntagResource, ListTags, CreateEventSourceMapping, DeleteEventSourceMapping, GetEventSourceMapping, ListEventSourceMappings, UpdateEventSourceMapping, CreateFunctionUrlConfig, GetFunctionUrlConfig, UpdateFunctionUrlConfig, DeleteFunctionUrlConfig, ListFunctionUrlConfigs | Python runtimes actually execute with warm worker pool; SQS event source mapping; Function URL CRUD |
| IAM | CreateUser, GetUser, ListUsers, DeleteUser, CreateRole, GetRole, ListRoles, DeleteRole, CreatePolicy, GetPolicy, DeletePolicy, AttachRolePolicy, DetachRolePolicy, PutRolePolicy, GetRolePolicy, DeleteRolePolicy, ListRolePolicies, ListAttachedRolePolicies, CreateAccessKey, ListAccessKeys, DeleteAccessKey, CreateInstanceProfile, GetInstanceProfile, DeleteInstanceProfile, AddRoleToInstanceProfile, RemoveRoleFromInstanceProfile, ListInstanceProfiles, CreateGroup, GetGroup, AddUserToGroup, RemoveUserFromGroup, CreateServiceLinkedRole, CreateOpenIDConnectProvider, TagRole, UntagRole, TagUser, UntagUser, TagPolicy, UntagPolicy | |
| STS | GetCallerIdentity, AssumeRole, GetSessionToken | |
| SecretsManager | CreateSecret, GetSecretValue, ListSecrets, DeleteSecret, UpdateSecret, DescribeSecret, PutSecretValue, RestoreSecret, RotateSecret, GetRandomPassword, ListSecretVersionIds, TagResource, UntagResource, PutResourcePolicy, GetResourcePolicy, DeleteResourcePolicy, ValidateResourcePolicy | |
| CloudWatch Logs | CreateLogGroup, DeleteLogGroup, DescribeLogGroups, CreateLogStream, DeleteLogStream, DescribeLogStreams, PutLogEvents, GetLogEvents, FilterLogEvents, PutRetentionPolicy, DeleteRetentionPolicy, PutSubscriptionFilter, DeleteSubscriptionFilter, DescribeSubscriptionFilters, PutMetricFilter, DeleteMetricFilter, DescribeMetricFilters, TagLogGroup, UntagLogGroup, ListTagsLogGroup, TagResource, UntagResource, ListTagsForResource, StartQuery, GetQueryResults, StopQuery, PutDestination, DeleteDestination, DescribeDestinations |
Extended Services
| Service | Operations | Notes |
|---|---|---|
| SSM Parameter Store | PutParameter, GetParameter, GetParameters, GetParametersByPath, DeleteParameter, DeleteParameters, DescribeParameters, GetParameterHistory, LabelParameterVersion, AddTagsToResource, RemoveTagsFromResource, ListTagsForResource | Supports String, SecureString, StringList |
| EventBridge | CreateEventBus, DeleteEventBus, ListEventBuses, PutRule, DeleteRule, ListRules, DescribeRule, EnableRule, DisableRule, PutTargets, RemoveTargets, ListTargetsByRule, PutEvents, TagResource, UntagResource, ListTagsForResource, CreateArchive, DeleteArchive, DescribeArchive, ListArchives, PutPermission, RemovePermission, CreateConnection, DescribeConnection, DeleteConnection, ListConnections, CreateApiDestination, DescribeApiDestination, DeleteApiDestination, ListApiDestinations | Lambda target dispatch on PutEvents |
| Kinesis | CreateStream, DeleteStream, DescribeStream, ListStreams, ListShards, PutRecord, PutRecords, GetShardIterator, GetRecords, MergeShards, SplitShard, UpdateShardCount, StartStreamEncryption, StopStreamEncryption, EnableEnhancedMonitoring, DisableEnhancedMonitoring, RegisterStreamConsumer, DeregisterStreamConsumer, ListStreamConsumers, DescribeStreamConsumer, AddTagsToStream, RemoveTagsFromStream, ListTagsForStream | Partition key → shard routing |
| CloudWatch Metrics | PutMetricData, GetMetricStatistics, GetMetricData, ListMetrics, PutMetricAlarm, PutCompositeAlarm, DescribeAlarms, DescribeAlarmsForMetric, DescribeAlarmHistory, DeleteAlarms, SetAlarmState, EnableAlarmActions, DisableAlarmActions, TagResource, UntagResource, ListTagsForResource, PutDashboard, GetDashboard, DeleteDashboards, ListDashboards | CBOR and JSON protocol |
| SES | SendEmail, SendRawEmail, SendTemplatedEmail, SendBulkTemplatedEmail, VerifyEmailIdentity, VerifyEmailAddress, VerifyDomainIdentity, VerifyDomainDkim, ListIdentities, GetIdentityVerificationAttributes, GetIdentityDkimAttributes, DeleteIdentity, GetSendQuota, GetSendStatistics, CreateConfigurationSet, DeleteConfigurationSet, DescribeConfigurationSet, ListConfigurationSets, CreateTemplate, GetTemplate, UpdateTemplate, DeleteTemplate, ListTemplates | Emails stored in-memory, not sent |
| Step Functions | CreateStateMachine, DeleteStateMachine, DescribeStateMachine, UpdateStateMachine, ListStateMachines, StartExecution, StartSyncExecution, StopExecution, DescribeExecution, DescribeStateMachineForExecution, ListExecutions, GetExecutionHistory, SendTaskSuccess, SendTaskFailure, SendTaskHeartbeat, TagResource, UntagResource, ListTagsForResource | Full ASL interpreter; Retry/Catch; waitForTaskToken; Pass/Task/Choice/Wait/Succeed/Fail/Map/Parallel |
| API Gateway v2 | CreateApi, GetApi, GetApis, UpdateApi, DeleteApi, CreateRoute, GetRoute, GetRoutes, UpdateRoute, DeleteRoute, CreateIntegration, GetIntegration, GetIntegrations, UpdateIntegration, DeleteIntegration, CreateStage, GetStage, GetStages, UpdateStage, DeleteStage, CreateDeployment, GetDeployment, GetDeployments, DeleteDeployment, CreateAuthorizer, GetAuthorizer, GetAuthorizers, UpdateAuthorizer, DeleteAuthorizer, TagResource, UntagResource, GetTags | HTTP API (v2) protocol; Lambda proxy (AWS_PROXY) and HTTP proxy (HTTP_PROXY) integrations; data plane via {apiId}.execute-api.localhost; {param} and {proxy+} path matching; JWT/Lambda authorizer CRUD |
| API Gateway v1 | CreateRestApi, GetRestApi, GetRestApis, UpdateRestApi, DeleteRestApi, CreateResource, GetResource, GetResources, UpdateResource, DeleteResource, PutMethod, GetMethod, DeleteMethod, UpdateMethod, PutMethodResponse, GetMethodResponse, DeleteMethodResponse, PutIntegration, GetIntegration, DeleteIntegration, UpdateIntegration, PutIntegrationResponse, GetIntegrationResponse, DeleteIntegrationResponse, CreateDeployment, GetDeployment, GetDeployments, UpdateDeployment, DeleteDeployment, CreateStage, GetStage, GetStages, UpdateStage, DeleteStage, CreateAuthorizer, GetAuthorizer, GetAuthorizers, UpdateAuthorizer, DeleteAuthorizer, CreateModel, GetModel, GetModels, DeleteModel, CreateApiKey, GetApiKey, GetApiKeys, UpdateApiKey, DeleteApiKey, CreateUsagePlan, GetUsagePlan, GetUsagePlans, UpdateUsagePlan, DeleteUsagePlan, CreateUsagePlanKey, GetUsagePlanKeys, DeleteUsagePlanKey, CreateDomainName, GetDomainName, GetDomainNames, DeleteDomainName, CreateBasePathMapping, GetBasePathMapping, GetBasePathMappings, DeleteBasePathMapping, TagResource, UntagResource, GetTags | REST API (v1) protocol; Lambda proxy format 1.0 (AWS_PROXY), HTTP proxy (HTTP_PROXY), MOCK integration; data plane via {apiId}.execute-api.localhost; resource tree with {param} and {proxy+} path matching; JSON Patch for all PATCH operations; state persistence |
Infrastructure Services (with real Docker execution)
| Service | Operations | Real Execution |
|---|---|---|
| ECS | CreateCluster, UpdateCluster, DeleteCluster, DescribeClusters, ListClusters, RegisterTaskDefinition, DeregisterTaskDefinition, DescribeTaskDefinition, ListTaskDefinitions, CreateService, DeleteService, DescribeServices, UpdateService, ListServices, RunTask, StopTask, DescribeTasks, ListTasks, CreateCapacityProvider, DeleteCapacityProvider, DescribeCapacityProviders, PutClusterCapacityProviders, TagResource, UntagResource, ListTagsForResource | RunTask starts real Docker containers via Docker socket |
| RDS | CreateDBInstance, DeleteDBInstance, DescribeDBInstances, StartDBInstance, StopDBInstance, RebootDBInstance, ModifyDBInstance, CreateDBCluster, DeleteDBCluster, DescribeDBClusters, StartDBCluster, StopDBCluster, CreateDBSubnetGroup, DescribeDBSubnetGroups, ModifyDBSubnetGroup, DeleteDBSubnetGroup, CreateDBParameterGroup, DescribeDBParameterGroups, ModifyDBParameterGroup, DeleteDBParameterGroup, DescribeDBParameters, CreateDBClusterParameterGroup, DescribeDBEngineVersions, DescribeOrderableDBInstanceOptions, CreateDBSnapshot, DeleteDBSnapshot, DescribeDBSnapshots, CreateDBClusterSnapshot, DeleteDBClusterSnapshot, DescribeDBClusterSnapshots, CreateDBInstanceReadReplica, RestoreDBInstanceFromDBSnapshot, CreateOptionGroup, DescribeOptionGroups, AddTagsToResource, RemoveTagsFromResource, ListTagsForResource | CreateDBInstance spins up real Postgres/MySQL Docker container, returns actual host:port endpoint |
| ElastiCache | CreateCacheCluster, DeleteCacheCluster, DescribeCacheClusters, ModifyCacheCluster, RebootCacheCluster, CreateReplicationGroup, DeleteReplicationGroup, DescribeReplicationGroups, ModifyReplicationGroup, IncreaseReplicaCount, DecreaseReplicaCount, CreateCacheSubnetGroup, DescribeCacheSubnetGroups, ModifyCacheSubnetGroup, DeleteCacheSubnetGroup, CreateCacheParameterGroup, DescribeCacheParameterGroups, ModifyCacheParameterGroup, ResetCacheParameterGroup, DeleteCacheParameterGroup, DescribeCacheParameters, DescribeCacheEngineVersions, CreateUser, DescribeUsers, DeleteUser, ModifyUser, CreateUserGroup, DescribeUserGroups, DeleteUserGroup, ModifyUserGroup, CreateSnapshot, DeleteSnapshot, DescribeSnapshots, DescribeEvents | CreateCacheCluster spins up real Redis/Memcached Docker container |
| Glue | CreateDatabase, DeleteDatabase, GetDatabase, GetDatabases, CreateTable, DeleteTable, GetTable, GetTables, UpdateTable, BatchDeleteTable, CreatePartition, GetPartitions, BatchCreatePartition, BatchGetPartition, CreatePartitionIndex, GetPartitionIndexes, CreateConnection, GetConnections, CreateCrawler, UpdateCrawler, GetCrawler, GetCrawlerMetrics, StartCrawler, CreateJob, GetJob, GetJobs, StartJobRun, GetJobRun, GetJobRuns, CreateTrigger, GetTrigger, DeleteTrigger, UpdateTrigger, StartTrigger, StopTrigger, ListTriggers, GetTriggers, CreateWorkflow, GetWorkflow, DeleteWorkflow, UpdateWorkflow, StartWorkflowRun, CreateSecurityConfiguration, GetSecurityConfiguration, GetSecurityConfigurations, DeleteSecurityConfiguration, CreateClassifier, GetClassifier, GetClassifiers, DeleteClassifier, TagResource, UntagResource, GetTags | Python shell jobs actually execute via subprocess |
| Athena | StartQueryExecution, GetQueryExecution, GetQueryResults, StopQueryExecution, ListQueryExecutions, BatchGetQueryExecution, CreateWorkGroup, DeleteWorkGroup, GetWorkGroup, ListWorkGroups, UpdateWorkGroup, CreateNamedQuery, DeleteNamedQuery, GetNamedQuery, ListNamedQueries, BatchGetNamedQuery, CreateDataCatalog, GetDataCatalog, ListDataCatalogs, DeleteDataCatalog, UpdateDataCatalog, CreatePreparedStatement, GetPreparedStatement, DeletePreparedStatement, ListPreparedStatements, GetTableMetadata, ListTableMetadata, TagResource, UntagResource, ListTagsForResource | Real SQL via DuckDB when installed (pip install duckdb), otherwise returns mock results; result pagination; column type metadata |
| Firehose | CreateDeliveryStream, DeleteDeliveryStream, DescribeDeliveryStream, ListDeliveryStreams, PutRecord, PutRecordBatch, UpdateDestination, TagDeliveryStream, UntagDeliveryStream, ListTagsForDeliveryStream, StartDeliveryStreamEncryption, StopDeliveryStreamEncryption | S3 destinations write records to the local S3 emulator; all other destination types buffer in-memory; concurrency-safe UpdateDestination via VersionId |
Real Database Endpoints (RDS)
When you create an RDS instance, MiniStack starts a real database container and returns the actual connection endpoint:
import boto3
import psycopg2 # pip install psycopg2-binary
rds = boto3.client("rds", endpoint_url="http://localhost:4566",
aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1")
resp = rds.create_db_instance(
DBInstanceIdentifier="mydb",
DBInstanceClass="db.t3.micro",
Engine="postgres",
MasterUsername="admin",
MasterUserPassword="password",
DBName="appdb",
AllocatedStorage=20,
)
endpoint = resp["DBInstance"]["Endpoint"]
# Connect directly — it's a real Postgres instance
conn = psycopg2.connect(
host=endpoint["Address"], # localhost
port=endpoint["Port"], # 15432 (auto-assigned)
user="admin",
password="password",
dbname="appdb",
)
Supported engines: postgres, mysql, mariadb, aurora-postgresql, aurora-mysql
Real Redis Endpoints (ElastiCache)
import boto3
import redis # pip install redis
ec = boto3.client("elasticache", endpoint_url="http://localhost:4566",
aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1")
resp = ec.create_cache_cluster(
CacheClusterId="my-redis",
Engine="redis",
CacheNodeType="cache.t3.micro",
NumCacheNodes=1,
)
node = resp["CacheCluster"]["CacheNodes"][0]["Endpoint"]
r = redis.Redis(host=node["Address"], port=node["Port"])
r.set("key", "value")
print(r.get("key")) # b'value'
A Redis sidecar is also always available at localhost:6379 when using Docker Compose.
Athena with Real SQL
Athena queries run via DuckDB and can query files in your local S3 data directory:
import boto3, time
athena = boto3.client("athena", endpoint_url="http://localhost:4566",
aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1")
# Query runs real SQL via DuckDB
resp = athena.start_query_execution(
QueryString="SELECT 42 AS answer, 'hello' AS greeting",
ResultConfiguration={"OutputLocation": "s3://athena-results/"},
)
query_id = resp["QueryExecutionId"]
# Poll for result
while True:
status = athena.get_query_execution(QueryExecutionId=query_id)
if status["QueryExecution"]["Status"]["State"] == "SUCCEEDED":
break
time.sleep(0.1)
results = athena.get_query_results(QueryExecutionId=query_id)
for row in results["ResultSet"]["Rows"][1:]: # skip header
print([col["VarCharValue"] for col in row["Data"]])
# ['42', 'hello']
ECS with Real Containers
import boto3
ecs = boto3.client("ecs", endpoint_url="http://localhost:4566",
aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1")
ecs.create_cluster(clusterName="dev")
ecs.register_task_definition(
family="web",
containerDefinitions=[{
"name": "nginx",
"image": "nginx:alpine",
"cpu": 128,
"memory": 256,
"portMappings": [{"containerPort": 80, "hostPort": 8080}],
}],
)
# This actually runs an nginx container via Docker
resp = ecs.run_task(cluster="dev", taskDefinition="web", count=1)
task_arn = resp["tasks"][0]["taskArn"]
# Stop it (removes the container)
ecs.stop_task(cluster="dev", task=task_arn)
Configuration
| Variable | Default | Description |
|---|---|---|
GATEWAY_PORT |
4566 |
Port to listen on |
MINISTACK_HOST |
localhost |
Hostname used in SQS queue URLs returned to clients |
LOG_LEVEL |
INFO |
DEBUG, INFO, WARNING, ERROR |
S3_PERSIST |
0 |
Set 1 to persist S3 objects to disk |
S3_DATA_DIR |
/tmp/localstack-data/s3 |
S3 persistence directory |
REDIS_HOST |
redis |
Redis host for ElastiCache fallback |
REDIS_PORT |
6379 |
Redis port for ElastiCache fallback |
RDS_BASE_PORT |
15432 |
Starting host port for RDS containers |
ELASTICACHE_BASE_PORT |
16379 |
Starting host port for ElastiCache containers |
PERSIST_STATE |
0 |
Set 1 to persist service state across restarts |
STATE_DIR |
/tmp/ministack-state |
Directory for persisted state files |
State Persistence
When PERSIST_STATE=1, MiniStack saves service state to STATE_DIR on shutdown and reloads it on startup. Writes are atomic (write-to-tmp then rename) to prevent corruption on crash.
Services currently supporting persistence: API Gateway v1, API Gateway v2
docker run -p 4566:4566 \
-e PERSIST_STATE=1 \
-e STATE_DIR=/data/ministack-state \
-v /tmp/ministack-data:/data \
nahuelnucera/ministack
Lambda Warm Starts
MiniStack keeps Python Lambda functions warm between invocations. After the first call (cold start), the handler module stays imported in a persistent subprocess. Subsequent calls skip the import step, matching real AWS warm-start behaviour and making test suites significantly faster.
Architecture
┌─────────────────────────────────────────┐
AWS CLI / boto3 │ ASGI Gateway :4566 │
Terraform / CDK ──►│ ┌───────────────────────────────────┐ │
Any AWS SDK │ │ Request Router │ │
│ │ 1. X-Amz-Target header │ │
│ │ 2. Authorization credential scope │ │
│ │ 3. Action query param │ │
│ │ 4. URL path pattern │ │
│ │ 5. Host header │ │
│ │ 6. Default → S3 │ │
│ └──────────────┬────────────────────┘ │
│ │ │
│ ┌──────────────▼──────────────────┐ │
│ │ Service Handlers │ │
│ │ │ │
│ │ S3 SQS SNS DynamoDB │ │
│ │ Lambda IAM STS Secrets │ │
│ │ SSM EventBridge Kinesis │ │
│ │ CloudWatch SES StepFunctions │ │
│ │ ECS RDS ElastiCache │ │
│ │ Glue Athena API Gateway │ │
│ └──────────────────────────────────┘ │
│ │
│ In-Memory Storage + Optional Docker │
└─────────────────────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Redis:6379 Postgres:15432+ MySQL:15433+
(ElastiCache) (RDS) (RDS)
Running Tests
# Install test dependencies
pip install boto3 pytest duckdb docker cbor2
# Start MiniStack
docker compose up -d
# Run the full test suite (450 tests across all 23 services)
pytest tests/ -v
Expected output:
collected 450 items
tests/test_services.py::test_s3_create_bucket PASSED
...
tests/test_services.py::test_apigwv1_execute_mock_response_parameters PASSED
450 passed in ~60s
Terraform / CDK / Pulumi
Terraform
provider "aws" {
region = "us-east-1"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
s3 = "http://localhost:4566"
sqs = "http://localhost:4566"
dynamodb = "http://localhost:4566"
lambda = "http://localhost:4566"
iam = "http://localhost:4566"
sts = "http://localhost:4566"
secretsmanager = "http://localhost:4566"
ssm = "http://localhost:4566"
kinesis = "http://localhost:4566"
sns = "http://localhost:4566"
rds = "http://localhost:4566"
ecs = "http://localhost:4566"
glue = "http://localhost:4566"
athena = "http://localhost:4566"
elasticache = "http://localhost:4566"
stepfunctions = "http://localhost:4566"
cloudwatch = "http://localhost:4566"
logs = "http://localhost:4566"
events = "http://localhost:4566"
ses = "http://localhost:4566"
apigateway = "http://localhost:4566"
}
}
AWS CDK
// cdk.json or in your app
const app = new cdk.App();
// Set endpoint override via environment
process.env.AWS_ENDPOINT_URL = "http://localhost:4566";
Pulumi
# Pulumi.dev.yaml
config:
aws:endpoints:
- s3: http://localhost:4566
dynamodb: http://localhost:4566
# ... etc
Comparison
| Feature | MiniStack | LocalStack Free | LocalStack Pro |
|---|---|---|---|
| S3, SQS, SNS, DynamoDB | ✅ | ✅ | ✅ |
| Lambda (Python execution) | ✅ | ✅ | ✅ |
| IAM, STS, SecretsManager | ✅ | ✅ | ✅ |
| CloudWatch Logs | ✅ | ✅ | ✅ |
| SSM Parameter Store | ✅ | ✅ | ✅ |
| EventBridge | ✅ | ✅ | ✅ |
| Kinesis | ✅ | ✅ | ✅ |
| SES | ✅ | ✅ | ✅ |
| Step Functions | ✅ | ✅ | ✅ |
| RDS (real DB containers) | ✅ | ❌ | ✅ |
| ElastiCache (real Redis) | ✅ | ❌ | ✅ |
| ECS (real Docker containers) | ✅ | ❌ | ✅ |
| Athena (real SQL via DuckDB) | ✅ | ❌ | ✅ |
| Glue Data Catalog + Jobs | ✅ | ❌ | ✅ |
| API Gateway v2 (HTTP API) | ✅ | ✅ | ✅ |
| API Gateway v1 (REST API) | ✅ | ✅ | ✅ |
| Firehose | ✅ | ✅ | ✅ |
| Cognito | ❌ | ✅ | ✅ |
| CloudFormation | ❌ | partial | ✅ |
| Cost | Free | Was free, now paid | $35+/mo |
| Docker image size | ~150MB | ~1GB | ~1GB |
| Memory at idle | ~30MB | ~500MB | ~500MB |
| Startup time | ~2s | ~15-30s | ~15-30s |
| License | MIT | BSL (restricted) | Proprietary |
Contributing
PRs welcome. The codebase is intentionally simple — each service is a single self-contained Python file in services/. Adding a new service means:
- Create
services/myservice.pywith anasync def handle_request(...)function - Add it to
SERVICE_HANDLERSinapp.py - Add detection patterns to
core/router.py - Add tests to
tests/test_services.py
License
MIT — free to use, modify, and distribute. No restrictions.
Copyright (c) 2024 MiniStack Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ministack-1.0.7.tar.gz.
File metadata
- Download URL: ministack-1.0.7.tar.gz
- Upload date:
- Size: 253.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69abfaff0750e346b14e23f54489bc1f5ab00cf308d5c49d5e581900920cb3e3
|
|
| MD5 |
eed4f98b3cdf02c67707d42eb882ebb1
|
|
| BLAKE2b-256 |
b905b887ca7bd24b189162bbb124ab3a2922d24fefe3a7e934651b34d3836967
|
File details
Details for the file ministack-1.0.7-py3-none-any.whl.
File metadata
- Download URL: ministack-1.0.7-py3-none-any.whl
- Upload date:
- Size: 206.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e06690b58c413cd080df017f959ef978f85da1da97e5953d7b56c8ffa8120be
|
|
| MD5 |
95faec5359f3f182e6edfe22526a6a81
|
|
| BLAKE2b-256 |
3855ed8cc01804b6059a6b79031bb703f203d0a6d82efc4b91dcdd7cf50d38c1
|