Ministack Aws Emulator

作者 reason-machines2384a003145a无许可证83 个星标收录于 2026年10月8日更新于 2026年10月8日仓库3个月前更新

MiniStack is a free, open-source local AWS emulator (LocalStack replacement) that emulates 25+ AWS services on a single port with no account or license required.

仅含说明DevOps & Cloud
AI 生成的概览

介绍本地 AWS 模拟器 MiniStack 的安装、配置以及 boto3 和 AWS CLI 使用示例。

功能
该技能提供运行 MiniStack 的参考说明,MiniStack 是一个在单一端口上模拟 25 种以上 AWS 服务的本地 AWS 模拟器。内容涵盖通过 PyPI、Docker 或 Docker Compose 安装、环境变量配置,以及使用 AWS CLI 和 boto3 操作 S3、SQS、DynamoDB、Lambda、SNS、SecretsManager、SSM、Kinesis、EventBridge 和 CloudWatch Logs 等服务的示例。还包括基于 pytest fixture 的测试模式、GitHub Actions CI 集成以及故障排查说明。
适用场景
当你希望在本地运行 AWS 服务进行开发或测试、而不使用真实 AWS 账户时使用。适用于本地测试 boto3 代码、在 CI 中模拟 AWS 服务,或从 LocalStack 迁移到免费替代方案等场景。
运行要求
需要安装并运行 MiniStack,可通过 pip install ministack 或 Docker 镜像 nahuelnucera/ministack 启动,默认监听 4566 端口。客户端使用需要 boto3 或 AWS CLI,以及非空的 AWS 凭证环境变量;无需真实 AWS 账户或 API 密钥。该技能不包含脚本,仅为说明文档。

MiniStack AWS Emulator

Skill by ara.so — Daily 2026 Skills collection.

MiniStack is a free, MIT-licensed drop-in replacement for LocalStack that emulates 25+ AWS services (S3, SQS, DynamoDB, Lambda, SNS, IAM, STS, Kinesis, EventBridge, SecretsManager, SSM, CloudWatch, SES, and more) on a single port (4566). No account, no API key, no telemetry. Works with boto3, AWS CLI, Terraform, CDK, and any SDK.


Installation

Option 1: PyPI (simplest)

bash
pip install ministackministack# Server runs at http://localhost:4566# Change port: GATEWAY_PORT=5000 ministack

Option 2: Docker Hub

bash
docker run -p 4566:4566 nahuelnucera/ministack

Option 3: Docker Compose (from source)

bash
git clone https://github.com/Nahuel990/ministackcd ministackdocker compose up -d

Verify it's running

bash
curl http://localhost:4566/_localstack/health

Configuration

Environment VariableDefaultDescription
GATEWAY_PORT4566Port to listen on
S3_PERSIST0Set to 1 to persist S3 data to disk

AWS CLI Usage

bash
# Set credentials (any non-empty values work)export AWS_ACCESS_KEY_ID=testexport AWS_SECRET_ACCESS_KEY=testexport AWS_DEFAULT_REGION=us-east-1
# S3aws --endpoint-url=http://localhost:4566 s3 mb s3://my-bucketaws --endpoint-url=http://localhost:4566 s3 cp ./file.txt s3://my-bucket/aws --endpoint-url=http://localhost:4566 s3 ls s3://my-bucket
# SQSaws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name my-queueaws --endpoint-url=http://localhost:4566 sqs list-queues
# DynamoDBaws --endpoint-url=http://localhost:4566 dynamodb list-tablesaws --endpoint-url=http://localhost:4566 dynamodb create-table \  --table-name Users \  --attribute-definitions AttributeName=userId,AttributeType=S \  --key-schema AttributeName=userId,KeyType=HASH \  --billing-mode PAY_PER_REQUEST
# STS (identity check)aws --endpoint-url=http://localhost:4566 sts get-caller-identity
# Use a named profile insteadaws configure --profile local# Enter: test / test / us-east-1 / jsonaws --profile local --endpoint-url=http://localhost:4566 s3 ls

awslocal wrapper (from source)

bash
chmod +x bin/awslocal./bin/awslocal s3 ls./bin/awslocal dynamodb list-tables

boto3 Usage Patterns

Universal client factory

python
import boto3
ENDPOINT = "http://localhost:4566"
def aws_client(service: str):    return boto3.client(        service,        endpoint_url=ENDPOINT,        aws_access_key_id="test",        aws_secret_access_key="test",        region_name="us-east-1",    )
def aws_resource(service: str):    return boto3.resource(        service,        endpoint_url=ENDPOINT,        aws_access_key_id="test",        aws_secret_access_key="test",        region_name="us-east-1",    )

S3

python
s3 = aws_client("s3")
# Create bucket and uploads3.create_bucket(Bucket="my-bucket")s3.put_object(Bucket="my-bucket", Key="hello.txt", Body=b"Hello, MiniStack!")
# Downloadobj = s3.get_object(Bucket="my-bucket", Key="hello.txt")print(obj["Body"].read())  # b'Hello, MiniStack!'
# List objectsresponse = s3.list_objects_v2(Bucket="my-bucket")for item in response.get("Contents", []):    print(item["Key"])
# Copy objects3.copy_object(    Bucket="my-bucket",    CopySource={"Bucket": "my-bucket", "Key": "hello.txt"},    Key="hello-copy.txt",)
# Enable versionings3.put_bucket_versioning(    Bucket="my-bucket",    VersioningConfiguration={"Status": "Enabled"},)
# Presigned URL (works locally)url = s3.generate_presigned_url(    "get_object",    Params={"Bucket": "my-bucket", "Key": "hello.txt"},    ExpiresIn=3600,)

SQS

python
sqs = aws_client("sqs")
# Standard queuequeue = sqs.create_queue(QueueName="my-queue")queue_url = queue["QueueUrl"]
sqs.send_message(QueueUrl=queue_url, MessageBody='{"event": "user_signup"}')
messages = sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=10)for msg in messages.get("Messages", []):    print(msg["Body"])    sqs.delete_message(QueueUrl=queue_url, ReceiptHandle=msg["ReceiptHandle"])
# FIFO queuefifo = sqs.create_queue(    QueueName="my-queue.fifo",    Attributes={"FifoQueue": "true", "ContentBasedDeduplication": "true"},)
# Dead-letter queue setupdlq = sqs.create_queue(QueueName="my-dlq")dlq_attrs = sqs.get_queue_attributes(    QueueUrl=dlq["QueueUrl"], AttributeNames=["QueueArn"])sqs.set_queue_attributes(    QueueUrl=queue_url,    Attributes={        "RedrivePolicy": json.dumps({            "deadLetterTargetArn": dlq_attrs["Attributes"]["QueueArn"],            "maxReceiveCount": "3",        })    },)

DynamoDB

python
import jsonddb = aws_client("dynamodb")
# Create tableddb.create_table(    TableName="Users",    KeySchema=[        {"AttributeName": "userId", "KeyType": "HASH"},        {"AttributeName": "createdAt", "KeyType": "RANGE"},    ],    AttributeDefinitions=[        {"AttributeName": "userId", "AttributeType": "S"},        {"AttributeName": "createdAt", "AttributeType": "N"},    ],    BillingMode="PAY_PER_REQUEST",)
# Put / Get / Deleteddb.put_item(    TableName="Users",    Item={        "userId": {"S": "u1"},        "createdAt": {"N": "1700000000"},        "name": {"S": "Alice"},        "active": {"BOOL": True},    },)
item = ddb.get_item(    TableName="Users",    Key={"userId": {"S": "u1"}, "createdAt": {"N": "1700000000"}},)print(item["Item"]["name"]["S"])  # Alice
# Queryresult = ddb.query(    TableName="Users",    KeyConditionExpression="userId = :uid",    ExpressionAttributeValues={":uid": {"S": "u1"}},)
# Batch writeddb.batch_write_item(    RequestItems={        "Users": [            {"PutRequest": {"Item": {"userId": {"S": "u2"}, "createdAt": {"N": "1700000001"}, "name": {"S": "Bob"}}}},        ]    })
# TTLddb.update_time_to_live(    TableName="Users",    TimeToLiveSpecification={"Enabled": True, "AttributeName": "expiresAt"},)

SNS + SQS Fanout

python
sns = aws_client("sns")sqs = aws_client("sqs")
topic = sns.create_topic(Name="my-topic")topic_arn = topic["TopicArn"]
queue = sqs.create_queue(QueueName="fan-queue")queue_attrs = sqs.get_queue_attributes(    QueueUrl=queue["QueueUrl"], AttributeNames=["QueueArn"])queue_arn = queue_attrs["Attributes"]["QueueArn"]
sns.subscribe(TopicArn=topic_arn, Protocol="sqs", Endpoint=queue_arn)
# Publish — message is fanned out to subscribed SQS queuessns.publish(TopicArn=topic_arn, Message="hello fanout", Subject="test")

Lambda

python
import zipfile, io
# Create a zip with handler codebuf = io.BytesIO()with zipfile.ZipFile(buf, "w") as zf:    zf.writestr("handler.py", """def handler(event, context):    print("event:", event)    return {"statusCode": 200, "body": "ok"}""")buf.seek(0)
lam = aws_client("lambda")
lam.create_function(    FunctionName="my-function",    Runtime="python3.12",    Role="arn:aws:iam::000000000000:role/role",    Handler="handler.handler",    Code={"ZipFile": buf.read()},)
# Invoke synchronouslyimport jsonresponse = lam.invoke(    FunctionName="my-function",    InvocationType="RequestResponse",    Payload=json.dumps({"key": "value"}),)result = json.loads(response["Payload"].read())print(result)  # {"statusCode": 200, "body": "ok"}
# SQS event source mappinglam.create_event_source_mapping(    EventSourceArn=queue_arn,    FunctionName="my-function",    BatchSize=10,    Enabled=True,)

SecretsManager

python
sm = aws_client("secretsmanager")
sm.create_secret(Name="db-password", SecretString='{"password":"s3cr3t"}')secret = sm.get_secret_value(SecretId="db-password")print(secret["SecretString"])  # {"password":"s3cr3t"}
sm.update_secret(SecretId="db-password", SecretString='{"password":"newpass"}')sm.delete_secret(SecretId="db-password", ForceDeleteWithoutRecovery=True)

SSM Parameter Store

python
ssm = aws_client("ssm")
ssm.put_parameter(Name="/app/db/host", Value="localhost", Type="String")ssm.put_parameter(Name="/app/db/password", Value="secret", Type="SecureString")
param = ssm.get_parameter(Name="/app/db/host")print(param["Parameter"]["Value"])  # localhost
# Fetch all params under a pathparams = ssm.get_parameters_by_path(Path="/app/", Recursive=True)for p in params["Parameters"]:    print(p["Name"], p["Value"])

Kinesis

python
import base64
kin = aws_client("kinesis")
kin.create_stream(StreamName="events", ShardCount=1)kin.put_record(StreamName="events", Data=b'{"event":"click"}', PartitionKey="user1")
# Get recordsshards = kin.list_shards(StreamName="events")shard_id = shards["Shards"][0]["ShardId"]
iterator = kin.get_shard_iterator(    StreamName="events",    ShardId=shard_id,    ShardIteratorType="TRIM_HORIZON",)records = kin.get_records(ShardIterator=iterator["ShardIterator"])for r in records["Records"]:    print(base64.b64decode(r["Data"]))

EventBridge

python
eb = aws_client("events")
# Create a custom buseb.create_event_bus(Name="my-bus")
# Put a rule targeting a Lambdaeb.put_rule(    Name="my-rule",    EventBusName="my-bus",    EventPattern='{"source": ["myapp"]}',    State="ENABLED",)eb.put_targets(    Rule="my-rule",    EventBusName="my-bus",    Targets=[{"Id": "1", "Arn": "arn:aws:lambda:us-east-1:000000000000:function:my-function"}],)
# Emit an event (triggers Lambda target)eb.put_events(Entries=[{    "Source": "myapp",    "DetailType": "UserSignup",    "Detail": '{"userId": "123"}',    "EventBusName": "my-bus",}])

CloudWatch Logs

python
import time
logs = aws_client("logs")
logs.create_log_group(logGroupName="/app/service")logs.create_log_stream(logGroupName="/app/service", logStreamName="stream-1")
logs.put_log_events(    logGroupName="/app/service",    logStreamName="stream-1",    logEvents=[        {"timestamp": int(time.time() * 1000), "message": "App started"},        {"timestamp": int(time.time() * 1000), "message": "Request received"},    ],)
events = logs.get_log_events(    logGroupName="/app/service",    logStreamName="stream-1",)for e in events["events"]:    print(e["message"])
# Filter with glob patterns (* and ?), AND terms, -exclusionsfiltered = logs.filter_log_events(    logGroupName="/app/service",    filterPattern="Request*",)

Testing Patterns

pytest fixture (recommended)

python
import pytestimport boto3
MINISTACK_ENDPOINT = "http://localhost:4566"
@pytest.fixture(scope="session")def aws_endpoint():    return MINISTACK_ENDPOINT
@pytest.fixturedef s3_client(aws_endpoint):    return boto3.client(        "s3",        endpoint_url=aws_endpoint,        aws_access_key_id="test",        aws_secret_access_key="test",        region_name="us-east-1",    )
@pytest.fixturedef test_bucket(s3_client):    bucket = "test-bucket"    s3_client.create_bucket(Bucket=bucket)    yield bucket    # Cleanup    objs = s3_client.list_objects_v2(Bucket=bucket).get("Contents", [])    for obj in objs:        s3_client.delete_object(Bucket=bucket, Key=obj["Key"])    s3_client.delete_bucket(Bucket=bucket)
def test_upload_download(s3_client, test_bucket):    s3_client.put_object(Bucket=test_bucket, Key="test.txt", Body=b"hello")    resp = s3_client.get_object(Bucket=test_bucket, Key="test.txt")    assert resp["Body"].read() == b"hello"

GitHub Actions CI integration

yaml
# .github/workflows/test.ymlname: Test
on: [push, pull_request]
jobs:  test:    runs-on: ubuntu-latest    services:      ministack:        image: nahuelnucera/ministack        ports:          - 4566:4566    steps:      - uses: actions/checkout@v4      - uses: actions/setup-python@v5        with:          python-version: "3.12"      - run: pip install -r requirements.txt      - run: pytest        env:          AWS_ACCESS_KEY_ID: test          AWS_SECRET_ACCESS_KEY: test          AWS_DEFAULT_REGION: us-east-1          AWS_ENDPOINT_URL: http://localhost:4566

Using AWS_ENDPOINT_URL env var (boto3 >= 1.28)

python
import osimport boto3
# If AWS_ENDPOINT_URL is set, boto3 uses it automatically — no endpoint_url kwarg needed# export AWS_ENDPOINT_URL=http://localhost:4566s3 = boto3.client("s3")  # picks up AWS_ENDPOINT_URL automatically

Supported Services (25+)

ServiceKey Operations
S3CRUD, multipart, versioning, encryption, lifecycle, CORS, ACL, notifications
SQSStandard & FIFO queues, DLQ, batch ops
SNSTopics, subscriptions, fanout to SQS/Lambda, platform endpoints
DynamoDBTables, CRUD, Query, Scan, TTL, transactions, batch ops
LambdaPython runtimes, invoke, SQS event sources, Function URLs
IAMUsers, roles, policies, groups, instance profiles, OIDC
STSGetCallerIdentity, AssumeRole, GetSessionToken
SecretsManagerFull CRUD, rotation, versioning
SSM Parameter StoreString, SecureString, StringList, path queries
EventBridgeBuses, rules, targets, Lambda dispatch
KinesisStreams, shards, records, iterators
CloudWatch MetricsPutMetricData, alarms, dashboards, CBOR protocol
CloudWatch LogsLog groups/streams, filter with globs, metric filters
SESSend email, templates, configuration sets
Step FunctionsState machine CRUD
RDSSpins up real Postgres/MySQL containers
ElastiCacheSpins up real Redis containers
AthenaReal SQL via DuckDB
ECSReal Docker containers

Troubleshooting

Connection refused on port 4566

bash
# Check if ministack is runningcurl http://localhost:4566/_localstack/health# Start itministack# ordocker run -p 4566:4566 nahuelnucera/ministack

NoCredentialsError from boto3

bash
export AWS_ACCESS_KEY_ID=testexport AWS_SECRET_ACCESS_KEY=testexport AWS_DEFAULT_REGION=us-east-1# Any non-empty values work — MiniStack doesn't validate credentials

InvalidSignatureException

  • This is usually a region mismatch. Ensure region_name="us-east-1" matches across all clients.

Lambda function not found after create

  • MiniStack executes Python runtimes with a warm worker pool. Wait briefly or invoke with InvocationType="Event" for async.

S3 data lost on restart

bash
# Enable persistenceS3_PERSIST=1 ministack# or in Dockerdocker run -p 4566:4566 -e S3_PERSIST=1 -v $(pwd)/data:/data nahuelnucera/ministack

Port conflict

bash
GATEWAY_PORT=5000 ministack# Then use http://localhost:5000 as endpoint

Migrating from LocalStack

  • Replace all http://localhost:4566 endpoint URLs — they stay the same.
  • Remove LOCALSTACK_AUTH_TOKEN / LOCALSTACK_API_KEY env vars (not needed).
  • Replace localstack/localstack Docker image with nahuelnucera/ministack.
  • All boto3 client code works without modification.

来源与署名

来源:reason-machines/trending-skills位于skills/ministack-aws-emulator提交2384a00

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架