1. Architecture Overview
LocalStack is an AWS cloud service emulator built as a Python application that runs inside a Docker container. It provides a single gateway endpoint (default port 4566) that accepts AWS API requests via any standard SDK, CLI, or IaC tool, routes them to service-specific provider implementations, and returns AWS-compatible responses — all without connecting to real AWS infrastructure.
The system is designed around three core architectural principles: a plugin-based service model that loads services on demand, a gateway with a handler chain pattern for request processing, and a runtime lifecycle with hook points for extensibility.
Architecture Diagram
┌─────────────────────────────────────────┐
│ Docker Container │
│ │
AWS SDK/CLI ─────┤──► Gateway (:4566) │
Terraform │ │ │
CDK │ ├── Request Handlers (20+) │
Pulumi │ │ ├── AccountID Extraction │
│ │ ├── Service Router │
│ │ └── Integration Handlers │
│ │ │
│ ├── Service Plugin Manager │
│ │ ├── S3 Provider │
│ │ ├── Lambda Provider │
│ │ ├── DynamoDB Provider │
│ │ ├── SQS Provider │
│ │ └── ... (30+ services) │
│ │ │
│ ├── Exception Handlers (4) │
│ ├── Response Handlers (8) │
│ └── Finalizers (2) │
│ │
│ ┌── DNS Server (:53) ──────────────┐ │
│ │ Resolves *.amazonaws.com → GW │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌── Init Scripts ──────────────────┐ │
│ │ boot.d → start.d → ready.d │ │
│ │ shutdown.d (on exit) │ │
│ └──────────────────────────────────┘ │
│ │
│ /var/lib/localstack/ │
│ ├── cache/ (downloaded deps) │
│ ├── data/ (persistence snapshots) │
│ ├── tmp/ (transient state) │
│ └── logs/ (service logs) │
└─────────────────────────────────────────┘
Component Inventory
| Component | Path | Role |
|---|---|---|
| Gateway Server | localstack/aws/app.py |
LocalstackAwsGateway — HTTP entry point, handler chain orchestration |
| Runtime | localstack/runtime/main.py, runtime.py |
LocalstackRuntime — lifecycle management, startup, shutdown |
| Service Plugin Manager | localstack/services/plugins.py |
Lazy-loading service providers via plux plugin framework |
| DNS Server | localstack/dns/server.py |
Built-in DNS for AWS domain resolution |
| Init Script Manager | localstack/runtime/init.py |
InitScriptManager — discovers and runs lifecycle scripts |
| Configuration | localstack/config.py |
153 environment variables, platform detection, directory management |
| Edge/Service Router | localstack/services/edge.py |
Gateway listener startup, privileged port handling |
| Extensions API | localstack/extensions/api.py |
Plugin lifecycle hooks for custom extensions |
| CLI / LPM | localstack/cli/lpm.py |
LocalStack Package Manager CLI commands |
| Scaffold | localstack/aws/scaffold.py |
ASF API stub generator and upgrader |
| Health Endpoint | localstack/services/edge.py |
HealthResource — /health and /_localstack/health |
| Moto Integration | localstack/services/moto.py |
Delegation to moto library for service emulation |
Technology Stack
| Layer | Technology |
|---|---|
| Language | Python 3.10+ |
| Web Server | Twisted (default), Hypercorn (alternative) |
| HTTP Framework | Werkzeug/WSGI-based request routing |
| Plugin System | plux (entry-point based plugin discovery) |
| Service Emulation | Moto (community), custom ASF providers |
| Container Runtime | Docker (for Lambda, sub-services) |
| DNS | Custom UDP/TCP DNS protocol implementation |
| Persistence | Snapshot-based serialization to filesystem |
Note
The gateway server type is configurable via
GATEWAY_SERVER. The defaulttwistedserver provides stable HTTP/1.1 handling. Thehypercornalternative supports HTTP/2 but has known stability issues with Lambda creation (GitHub issue #13020) and connection reuse (GitHub issue #13039).
Request Processing Flow
- Client sends request to
http://localhost:4566with AWS SigV4 headers - Gateway receives via Twisted/Hypercorn server
- Request handler chain processes sequentially:
- Account ID extraction from access key (AKIA/ASIA/LKIA/LSIA prefix decoding)
- Service identification from request headers/path
- Request parsing and validation
- Route matching to service provider
- Service plugin loaded on demand (lazy initialization via
plux) - Provider processes request (moto delegation or custom ASF implementation)
- Response handler chain transforms the response
- Exception handlers catch and format errors as AWS-compatible XML/JSON
- Finalizers complete the request lifecycle
2. System Requirements
Minimum Hardware
| Resource | Minimum | Recommended | Notes |
|---|---|---|---|
| CPU | 2 cores | 4+ cores | Lambda and parallel service loading benefit from more cores |
| RAM | 2 GB | 4-8 GB | JVM-based services (OpenSearch, DynamoDB, Kinesis) each add 256 MB-1 GB |
| Disk | 1 GB | 10+ GB | Persistence, Lambda layers, and cached downloads consume space |
| Network | Localhost only | Bridge network | Required for inter-container communication |
Software Dependencies
| Dependency | Minimum Version | Purpose |
|---|---|---|
| Docker | 20.10+ | Container runtime |
| Python | 3.8+ | CLI and runtime |
| Docker Compose | 2.0+ | Multi-service deployment (optional) |
Platform Support
| Platform | Status | Notes |
|---|---|---|
| Linux (x86_64) | Full support | Primary development platform |
| Linux (ARM64) | Full support | Native ARM images available |
| macOS (Intel) | Full support | Via Docker Desktop |
| macOS (Apple Silicon) | Full support | Via Docker Desktop with Rosetta or native ARM |
| Windows (WSL2) | Full support | Docker Desktop with WSL2 backend required |
| Windows (Hyper-V) | Limited | Networking issues with DNS server |
Important
Platform detection is handled by
localstack/config.pywhich checksin_docker(),is_linux(),is_macos(),is_windows(), andis_wsl(). TheWINDOWS_DOCKER_MOUNT_PREFIXvariable (default:/host_mnt) handles Windows path translation for Docker volume mounts.
3. Configuration Reference
LocalStack exposes 153 environment variables for configuration. All are set via environment variables — there is no configuration file format. Variables are read at startup from localstack/config.py and localstack/constants.py.
Configuration Loading Order
- Defaults defined in
config.py - Profile file
~/.localstack/{CONFIG_PROFILE}.envloaded via dotenv - Shell environment variables (highest priority)
Core Configuration
| Variable | Type | Default | Description | Required |
|---|---|---|---|---|
CONFIG_PROFILE |
string | (unset) | Name of profile to load from ~/.localstack/ |
No |
CONFIG_DIR |
string | (unset) | Override configuration directory path | No |
RUNTIME_COMPONENTS |
string | (unset) | Component plugins to load at startup | No |
SERVICES |
string | (all) | Comma-separated list of services to enable | No |
MAIN_CONTAINER_NAME |
string | localstack-main |
Docker container name | No |
FILESYSTEM_ROOT |
string | (auto) | Root filesystem path | No |
Gateway Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
GATEWAY_LISTEN |
string | 0.0.0.0:4566 |
Listen address(es), supports IPv4 and IPv6. Multiple addresses separated by commas |
GATEWAY_SERVER |
string | twisted |
Gateway server implementation (twisted or alternatives) |
GATEWAY_WORKER_COUNT |
int | 1000 |
Maximum concurrent worker connections |
LOCALSTACK_HOST |
string | (auto) | Hostname used in service response URLs |
Warning
Setting
LOCALSTACK_HOSTincorrectly can break internal healthchecks (GitHub issue #13883). The value must be resolvable from within the container. For external access, configure your client's endpoint URL separately.
Persistence & Snapshot Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
PERSISTENCE |
bool | 0 |
Enable state persistence |
DATA_DIR |
string | (unset) | Data directory for persistence (setting this also enables persistence) |
SNAPSHOT_LOAD_STRATEGY |
string | (unset) | on_startup, on_request, manual |
SNAPSHOT_SAVE_STRATEGY |
string | (unset) | on_request, scheduled, manual |
SNAPSHOT_FLUSH_INTERVAL |
int | 15 |
Seconds between scheduled snapshot flushes |
LOCALSTACK_VOLUME_DIR |
string | (unset) | Override volume directory path |
STATE_SERIALIZATION_BACKEND |
string | (unset) | Serialization backend for state persistence |
Docker Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
DOCKER_SOCK |
string | (auto) | Path to Docker socket |
DOCKER_CMD |
string | (auto) | Docker command to use |
DOCKER_FLAGS |
string | (unset) | Additional flags for all Docker commands |
DOCKER_BRIDGE_IP |
string | (auto) | Docker bridge network IP address |
DOCKER_GLOBAL_IMAGE_PREFIX |
string | (unset) | Prefix for all Docker image names (for private registries) |
DOCKER_SDK_DEFAULT_TIMEOUT_SECONDS |
int | (default) | Timeout for Docker SDK operations |
DOCKER_SDK_DEFAULT_RETRIES |
int | (default) | Retry count for Docker SDK operations |
CONTAINER_RUNTIME |
string | (auto) | Container runtime (docker/podman) |
IMAGE_NAME |
string | (auto) | LocalStack Docker image name |
PORTS_CHECK_DOCKER_IMAGE |
string | (unset) | Image used for port availability checks |
MAIN_DOCKER_NETWORK |
string | (unset) | Docker network for LocalStack containers |
DNS Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
DNS_ADDRESS |
string | (auto) | DNS server bind address |
DNS_PORT |
int | 53 |
DNS server port |
DNS_SERVER |
string | (unset) | Upstream DNS server |
DNS_RESOLVE_IP |
string | (unset) | IP to resolve AWS domains to |
DNS_NAME_PATTERNS_TO_RESOLVE_UPSTREAM |
string | (unset) | Patterns for upstream DNS resolution |
DNS_LOCAL_NAME_PATTERNS |
string | (unset) | Patterns for local DNS resolution |
DNS_VERIFICATION_DOMAIN |
string | (unset) | Domain for DNS verification |
Lambda Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
LAMBDA_KEEPALIVE_MS |
int | 600000 |
Container keepalive time (ms) |
LAMBDA_RUNTIME_EXECUTOR |
string | (default) | Execution backend |
LAMBDA_DOCKER_NETWORK |
string | (unset) | Docker network for Lambda containers |
LAMBDA_DOCKER_DNS |
string | (unset) | DNS server for Lambda containers |
LAMBDA_DOCKER_FLAGS |
string | (unset) | Additional Docker flags for Lambda containers |
LAMBDA_REMOVE_CONTAINERS |
string | (default) | Remove containers after execution |
LAMBDA_RUNTIME_IMAGE_MAPPING |
JSON string | (unset) | Custom runtime-to-image mapping |
LAMBDA_RUNTIME_VALIDATION |
string | (unset) | Runtime validation mode |
LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT |
int | (default) | Environment initialization timeout |
LAMBDA_DEBUG_MODE |
bool | (unset) | Enable debugging support |
LAMBDA_DEBUG_MODE_CONFIG_PATH |
string | (unset) | Path to debug mode configuration file |
LAMBDA_JAVA_OPTS |
string | (unset) | JVM options for Java Lambda runtimes |
LAMBDA_TRUNCATE_STDOUT |
int | (default) | Max stdout bytes to capture |
LAMBDA_RETRY_BASE_DELAY |
int | (default) | Base delay for Lambda retry backoff |
HOSTNAME_FROM_LAMBDA |
string | (unset) | Hostname Lambdas use to reach LocalStack |
BUCKET_MARKER_LOCAL |
string | (unset) | S3 bucket marker for local development |
Lambda Limits
| Variable | Type | Default | Description |
|---|---|---|---|
LAMBDA_LIMITS_CONCURRENT_EXECUTIONS |
int | 1000 |
Max concurrent executions |
LAMBDA_LIMITS_MINIMUM_UNRESERVED_CONCURRENCY |
int | 100 |
Minimum unreserved concurrency |
LAMBDA_LIMITS_TOTAL_CODE_SIZE |
int | 80530636800 |
Total code storage limit (bytes) |
LAMBDA_LIMITS_CODE_SIZE_ZIPPED |
int | 52428800 |
Max zipped code size (50 MB) |
LAMBDA_LIMITS_CODE_SIZE_UNZIPPED |
int | 262144000 |
Max unzipped code size (250 MB) |
LAMBDA_LIMITS_CREATE_FUNCTION_REQUEST_SIZE |
int | 70167211 |
Max create-function request size |
LAMBDA_LIMITS_MAX_FUNCTION_ENVVAR_SIZE_BYTES |
int | (AWS default) | Max environment variable size |
LAMBDA_LIMITS_MAX_FUNCTION_PAYLOAD_SIZE_BYTES |
int | (AWS default) | Max invocation payload size |
Lambda Init Binary Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
LAMBDA_INIT_RELEASE_VERSION |
string | (unset) | Lambda init binary version |
LAMBDA_INIT_BIN_PATH |
string | (unset) | Path to Lambda init binary |
LAMBDA_INIT_BOOTSTRAP_PATH |
string | (unset) | Path to Lambda bootstrap script |
LAMBDA_INIT_DELVE_PATH |
string | (unset) | Path to Delve debugger binary |
LAMBDA_INIT_DELVE_PORT |
int | (unset) | Delve debugger port |
LAMBDA_INIT_POST_INVOKE_WAIT_MS |
int | (unset) | Post-invocation wait time (ms) |
LAMBDA_INIT_USER |
string | (unset) | User for Lambda init process |
Lambda Event Source Mapping
| Variable | Type | Default | Description |
|---|---|---|---|
LAMBDA_EVENT_SOURCE_MAPPING_POLL_INTERVAL_SEC |
int | (default) | Polling interval for event source mappings |
LAMBDA_EVENT_SOURCE_MAPPING_MAX_BACKOFF_ON_ERROR_SEC |
int | (default) | Max backoff after error |
LAMBDA_EVENT_SOURCE_MAPPING_MAX_BACKOFF_ON_EMPTY_POLL_SEC |
int | (default) | Max backoff on empty poll |
DynamoDB Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
DYNAMODB_SHARE_DB |
bool | (unset) | Share single DB across all tables |
DYNAMODB_LOCAL_PORT |
int | (unset) | Custom DynamoDB Local port |
DYNAMODB_HEAP_SIZE |
string | (unset) | JVM heap size for DynamoDB Local |
DYNAMODB_ERROR_PROBABILITY |
float | (unset) | Probability of random errors (0.0-1.0) |
DYNAMODB_READ_ERROR_PROBABILITY |
float | (unset) | Probability of read errors |
DYNAMODB_WRITE_ERROR_PROBABILITY |
float | (unset) | Probability of write errors |
Tip
Error probability settings are useful for chaos testing. Set
DYNAMODB_ERROR_PROBABILITY=0.05to simulate a 5% failure rate in development environments.
Kinesis Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
KINESIS_SHARD_LIMIT |
int | (AWS default) | Maximum shards per stream |
KINESIS_ON_DEMAND_STREAM_COUNT_LIMIT |
int | (AWS default) | Max on-demand streams |
KINESIS_LATENCY |
int | (unset) | Simulated put latency (ms) |
KINESIS_MOCK_PERSIST_INTERVAL |
int | (unset) | Persistence interval (ms) |
KINESIS_MOCK_LOG_LEVEL |
string | (unset) | Mock engine log level |
KINESIS_MOCK_PROVIDER_ENGINE |
string | (default) | Mock provider engine |
KINESIS_MOCK_MAXIMUM_HEAP_SIZE |
string | (unset) | JVM max heap for Kinesis mock |
KINESIS_MOCK_INITIAL_HEAP_SIZE |
string | (unset) | JVM initial heap for Kinesis mock |
KINESIS_ERROR_PROBABILITY |
float | (unset) | Random error probability |
CORS Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
DISABLE_CORS_HEADERS |
bool | (unset) | Disable CORS headers entirely |
DISABLE_CORS_CHECKS |
bool | (unset) | Disable CORS validation |
EXTRA_CORS_ALLOWED_HEADERS |
string | (unset) | Additional allowed CORS headers |
EXTRA_CORS_EXPOSE_HEADERS |
string | (unset) | Additional exposed CORS headers |
EXTRA_CORS_ALLOWED_ORIGINS |
string | (unset) | Additional allowed CORS origins |
Network & Proxy Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
OUTBOUND_HTTP_PROXY |
string | (unset) | HTTP proxy for outbound requests |
OUTBOUND_HTTPS_PROXY |
string | (unset) | HTTPS proxy for outbound requests |
NO_PROXY / no_proxy |
string | (unset) | Addresses that bypass proxy |
CUSTOM_SSL_CERT_PATH |
string | (unset) | Path to custom SSL certificate |
REQUESTS_CA_BUNDLE |
string | (unset) | CA bundle for outbound HTTPS |
SKIP_INFRA_DOWNLOADS |
bool | (unset) | Skip runtime dependency downloads |
EXTERNAL_SERVICE_PORTS_START |
int | (default) | Start of external service port range |
EXTERNAL_SERVICE_PORTS_END |
int | (default) | End of external service port range |
SERVICE_INSTANCES_PORTS_START |
int | (default) | Start of service instance port range |
SERVICE_INSTANCES_PORTS_END |
int | (default) | End of service instance port range |
Logging & Debug Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
DEBUG |
bool | 0 |
Enable debug mode |
LS_LOG |
string | (unset) | Log level override (trace, debug, info, warning, error) |
LOG_LEVEL_OVERRIDES |
string | (unset) | Per-module log level overrides |
DEVELOP |
bool | (unset) | Enable development mode |
DEVELOP_PORT |
int | (unset) | Remote debugger port for development |
Note
LOG_LEVEL_OVERRIDESaccepts a JSON string mapping logger names to levels, e.g.,{"botocore":"WARNING","urllib3":"WARNING"}. This reduces log noise from verbose libraries.
Service-Specific Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
SQS_ENDPOINT_STRATEGY |
string | (default) | SQS URL generation strategy |
SQS_CLOUDWATCH_METRICS_REPORT_INTERVAL |
int | (unset) | CloudWatch metrics reporting interval |
OPENSEARCH_CUSTOM_BACKEND |
string | (unset) | Custom OpenSearch backend URL |
OPENSEARCH_ENDPOINT_STRATEGY |
string | (default) | OpenSearch endpoint strategy |
SFN_MOCK_CONFIG |
string | (unset) | Step Functions mock configuration file path |
SNS_SES_SENDER_ADDRESS |
string | (unset) | SES sender address for SNS email notifications |
SNS_CERT_URL_HOST |
string | (unset) | SNS certificate URL host |
CFN_PER_RESOURCE_TIMEOUT |
int | (default) | CloudFormation per-resource timeout (seconds) |
CFN_NO_WAIT_ITERATIONS |
int | (unset) | CloudFormation polling iterations |
CFN_STRING_REPLACEMENT_DENY_LIST |
string | (unset) | Strings to exclude from CFN template replacements |
BOTO_WAITER_DELAY |
int | (default) | Boto3 waiter polling delay |
BOTO_WAITER_MAX_ATTEMPTS |
int | (default) | Boto3 waiter max attempts |
INTERNAL_RESOURCE_ACCOUNT |
string | (unset) | Internal resource account ID |
Provider Override Configuration
| Variable | Type | Description |
|---|---|---|
PROVIDER_OVERRIDE_APIGATEWAY |
string | Override API Gateway provider implementation |
PROVIDER_OVERRIDE_DYNAMODB |
string | Override DynamoDB provider |
PROVIDER_OVERRIDE_DYNAMODBSTREAMS |
string | Override DynamoDB Streams provider |
PROVIDER_OVERRIDE_SNS |
string | Override SNS provider |
PROVIDER_OVERRIDE_EVENTS |
string | Override EventBridge provider (deprecated) |
Build & Version Information
| Variable | Type | Description |
|---|---|---|
LOCALSTACK_BUILD_GIT_HASH |
string | Git hash of the build |
LOCALSTACK_BUILD_DATE |
string | Build date |
LOCALSTACK_BUILD_VERSION |
string | Build version string |
LOCALSTACK_AUTH_TOKEN |
string | Pro authentication token |
LOCALSTACK_API_KEY |
string | Legacy API key (deprecated) |
Annotated Production Configuration
# Production-grade Docker Compose for CI/CD
docker run --rm -d \
--name localstack-ci \
-p 4566:4566 \
-p 4510-4559:4510-4559 \
-e SERVICES=s3,sqs,sns,lambda,dynamodb,cloudformation,iam \
-e GATEWAY_SERVER=twisted \
-e GATEWAY_WORKER_COUNT=1000 \
-e LAMBDA_KEEPALIVE_MS=300000 \
-e LAMBDA_DOCKER_NETWORK=ci-network \
-e LAMBDA_REMOVE_CONTAINERS=1 \
-e PERSISTENCE=0 \
-e DNS_ADDRESS=0.0.0.0 \
-e SKIP_INFRA_DOWNLOADS=0 \
-e DEBUG=0 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ./init:/etc/localstack/init \
localstack/localstack
4. Deployment & Scaling
Docker Deployment
Standalone Container
docker run --rm -d \
--name localstack \
-p 4566:4566 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ./data:/var/lib/localstack \
localstack/localstack
Docker Compose
version: "3.8"
services:
localstack:
image: localstack/localstack
container_name: localstack
ports:
- "4566:4566"
- "4510-4559:4510-4559"
environment:
SERVICES: s3,sqs,sns,lambda,dynamodb
GATEWAY_WORKER_COUNT: 1000
LAMBDA_DOCKER_NETWORK: ${COMPOSE_PROJECT_NAME}_default
PERSISTENCE: 1
volumes:
- localstack-data:/var/lib/localstack
- /var/run/docker.sock:/var/run/docker.sock
- ./init:/etc/localstack/init
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"]
interval: 10s
timeout: 5s
retries: 3
volumes:
localstack-data:
Tip
Set
LAMBDA_DOCKER_NETWORKto${COMPOSE_PROJECT_NAME}_defaultso Lambda containers automatically join the compose network and can reach other services.
Kubernetes Deployment
LocalStack includes development tooling for Kubernetes via k3d and Helm:
# Development setup (from localstack/dev/kubernetes/)
python -m localstack.dev.kubernetes
Warning
The Kubernetes tooling in this repository is for development and testing of LocalStack itself, not for deploying LocalStack as a service in Kubernetes. For production K8s deployment, use the official Helm chart.
CI/CD Integration
GitHub Actions
jobs:
test:
runs-on: ubuntu-latest
services:
localstack:
image: localstack/localstack
ports:
- 4566:4566
env:
SERVICES: s3,sqs,lambda,dynamodb
steps:
- uses: actions/checkout@v4
- run: |
pip install awscli-local
awslocal s3 mb s3://test-bucket
# Run your tests
GitLab CI
test:
services:
- name: localstack/localstack
alias: localstack
variables:
AWS_ENDPOINT_URL: http://localstack:4566
script:
- pip install awscli-local
- awslocal s3 mb s3://test-bucket
Volume Mount Architecture
LocalStack uses a standardized directory structure inside the container:
| Directory | Purpose | Persistence |
|---|---|---|
/usr/lib/localstack |
Static libraries (installed packages) | Ephemeral |
/var/lib/localstack/cache |
Downloaded runtime dependencies | Can be persisted |
/var/lib/localstack/data |
State snapshots | Persist for data retention |
/var/lib/localstack/tmp |
Temporary files | Ephemeral |
/var/lib/localstack/logs |
Service logs | Ephemeral |
/etc/localstack/init |
Init hook scripts | Bind-mount from host |
The Directories class in config.py manages these paths with properties: static_libs, var_libs, cache, tmp, mounted_tmp, functions, data, config, init, logs.
5. Operations
Health Monitoring
The health endpoint provides service status:
# Basic health check
curl http://localhost:4566/_localstack/health
# Also available at legacy path
curl http://localhost:4566/health
{
"services": {
"s3": "available",
"sqs": "available",
"lambda": "running",
"dynamodb": "available"
},
"features": {}
}
Service states:
| State | Meaning |
|---|---|
available |
Service is loaded and ready |
running |
Service is actively processing |
disabled |
Service is explicitly disabled |
| (absent) | Service not yet loaded (lazy loading) |
Note
The health endpoint supports POST requests with
actionfield for service control:restartto restart a service,killto stop one.
Service Management via Health Endpoint
# Restart a specific service
curl -X POST http://localhost:4566/health \
-H "Content-Type: application/json" \
-d '{"action": "restart", "services": ["s3"]}'
# Kill a service
curl -X POST http://localhost:4566/health \
-H "Content-Type: application/json" \
-d '{"action": "kill", "services": ["lambda"]}'
Container Lifecycle Management
# Start in background
localstack start -d
# Check status
localstack status
# View logs
docker logs -f localstack-main
# Stop gracefully
localstack stop
# Force stop
docker rm -f localstack-main
Init Hook Lifecycle
Init hooks execute at specific points in the LocalStack lifecycle:
| Directory | When | Use Case |
|---|---|---|
boot.d/ |
Before services start | System configuration, environment setup |
start.d/ |
During service initialization | Early resource creation |
ready.d/ |
After all services are ready | Resource provisioning, test data |
shutdown.d/ |
On container shutdown | Cleanup, state export |
Scripts are discovered by InitScriptManager and executed by ShellScriptRunner (.sh) or PythonScriptRunner (.py).
Important
Init scripts that exit with a non-zero code raise an
OSErrorwith the messageScript {path} returned a non-zero exit code {exit_code}. This does not halt LocalStack startup but is logged as an error.
Snapshot Operations
# Manual snapshot save (via internal API)
curl -X POST http://localhost:4566/_localstack/state/save
# Manual snapshot load
curl -X POST http://localhost:4566/_localstack/state/load
# Reset all state
curl -X POST http://localhost:4566/_localstack/state/reset
Log Management
Configure logging granularity:
# Global log level
LS_LOG=debug
# Per-module overrides (JSON string)
LOG_LEVEL_OVERRIDES='{"botocore":"WARNING","urllib3":"WARNING","docker":"INFO"}'
Tip
In production CI runs, set
LS_LOG=warningand selectively enable debug for specific modules viaLOG_LEVEL_OVERRIDESto reduce log volume while retaining diagnostic capability.
6. Performance
Gateway Server Tuning
| Parameter | Default | Tuning Guidance |
|---|---|---|
GATEWAY_SERVER |
twisted |
Use twisted for stability; alternatives may offer HTTP/2 but with stability tradeoffs |
GATEWAY_WORKER_COUNT |
1000 |
Increase for high-concurrency test suites; decrease on memory-constrained runners |
Lambda Performance
| Parameter | Impact | Recommendation |
|---|---|---|
LAMBDA_KEEPALIVE_MS |
600000 (10 min) |
Increase for test suites with frequent same-function invocations; decrease for memory-constrained environments |
LAMBDA_REMOVE_CONTAINERS |
Default | Set to 1 in CI to prevent container accumulation |
LAMBDA_DOCKER_NETWORK |
Network routing | Use host network for lowest latency |
JVM Service Tuning
Services backed by JVM processes (DynamoDB Local, Kinesis mock, OpenSearch) accept heap configuration:
# DynamoDB
DYNAMODB_HEAP_SIZE=512m
# Kinesis
KINESIS_MOCK_INITIAL_HEAP_SIZE=256m
KINESIS_MOCK_MAXIMUM_HEAP_SIZE=512m
Note
Each JVM-backed service starts a separate Java process. On a 4 GB container, running DynamoDB + Kinesis + OpenSearch simultaneously can consume 1.5-2 GB of heap alone. Size your container accordingly.
Service Loading Strategy
LocalStack uses lazy loading: services initialize on first API request, not at startup. This means:
- First request to a service incurs initialization latency (1-10 seconds depending on service)
- Subsequent requests are fast
- Use init hooks in
ready.d/to pre-warm services by making initial API calls
Port Range Management
The PortRange class manages dynamic port allocation for sub-services:
EXTERNAL_SERVICE_PORTS_START/EXTERNAL_SERVICE_PORTS_END: External-facing ports (default: 4510-4559)SERVICE_INSTANCES_PORTS_START/SERVICE_INSTANCES_PORTS_END: Internal service instance ports
If you see PortNotAvailableException, expand the port range or reduce concurrent service instances.
7. Security
Authentication Model
LocalStack accepts any AWS credentials by default. There is no authentication enforcement in the Community Edition. The access key ID is used for account identification (multi-account isolation), not for access control.
Access key prefixes determine account routing:
| Prefix | Source |
|---|---|
AKIA |
Standard IAM access key |
ASIA |
STS temporary credentials |
LKIA |
LocalStack-specific permanent key |
LSIA |
LocalStack-specific temporary key |
Caution
LocalStack does not enforce IAM policies by default in the Community Edition. Do not rely on LocalStack for security testing of IAM permissions. Production IAM policy validation requires testing against real AWS.
Network Security
- Default bind:
0.0.0.0:4566— accessible from all network interfaces - DNS server: Binds to port 53 (privileged port, requires root or capabilities)
- Docker socket: Required for Lambda — grants full Docker API access
- Inter-container traffic: Lambda containers communicate via Docker networking
Hardening recommendations:
- Bind to localhost only:
GATEWAY_LISTEN=127.0.0.1:4566 - Use Docker user namespaces to limit socket access
- Run in an isolated Docker network:
--network isolated-net - Never expose LocalStack to the public internet
SSL/TLS
Custom SSL certificates can be provided via CUSTOM_SSL_CERT_PATH. The CA bundle for outbound HTTPS requests is configurable via REQUESTS_CA_BUNDLE.
Privileged Port Handling
When GATEWAY_LISTEN specifies a port below 1024, LocalStack attempts to start a TCP proxy via sudo. If running in a non-TTY environment, this fails with cannot get sudo password from non-tty input. Solutions:
- Use ports above 1024 (default 4566)
- Run the container as root
- Use
--cap-add NET_BIND_SERVICE
Secrets in Environment
No sensitive secrets are required for the Community Edition. Pro features require:
LOCALSTACK_AUTH_TOKEN— authentication token for Pro servicesLOCALSTACK_API_KEY— legacy API key (deprecated)GITHUB_API_TOKEN— optional, for higher GitHub API rate limits
Warning
Never expose
LOCALSTACK_AUTH_TOKENin public CI logs. Use secret variables in your CI/CD system.
8. Developer Internals
Repository Structure
localstack-core/localstack/
├── aws/
│ ├── app.py # LocalstackAwsGateway — main entry point
│ ├── scaffold.py # ASF API stub generator
│ ├── client.py # Internal AWS client
│ ├── connect.py # AWS connection management
│ ├── mocking.py # Response shape generation
│ └── handlers/ # Gateway request/response handlers
│ └── service_plugin.py # Service lazy-loading handler
├── cli/
│ └── lpm.py # LocalStack Package Manager CLI
├── config.py # All 153 config variables
├── constants.py # Constants (API_ENDPOINT, etc.)
├── deprecations.py # Deprecation warnings
├── dns/
│ └── server.py # Built-in DNS server
├── extensions/
│ └── api.py # Extension lifecycle API
├── logging/
│ └── setup.py # Logging configuration
├── runtime/
│ ├── main.py # Runtime entry point
│ ├── runtime.py # LocalstackRuntime class
│ ├── init.py # InitScriptManager
│ ├── components.py # Gateway server factory
│ ├── current.py # Runtime singleton
│ └── legacy.py # Legacy supervisor support
├── services/
│ ├── edge.py # Gateway listener, HealthResource
│ ├── plugins.py # Service plugin manager
│ ├── stores.py # Multi-account/region store
│ ├── moto.py # Moto integration layer
│ └── apigateway/ # API Gateway provider (example)
│ └── next_gen/
│ └── execute_api/
│ └── handlers/ # Request/response handlers
└── utils/
├── archives.py # Archive download/extraction
├── batching.py # Batcher/AsyncBatcher
├── bootstrap.py # Container bootstrap utilities
├── checksum.py # Download checksum verification
├── collections.py # Dict navigation utilities
├── container_networking.py # Container network resolution
├── diagnose.py # Diagnostic information
├── docker_utils.py # Docker volume/mount utilities
├── files.py # File system utilities
├── functions.py # Function utilities (recursion guard)
├── http.py # HTTP download utilities
├── iputils.py # IP address utilities
├── net.py # Port management (PortRange)
├── objects.py # Object utilities
├── patch.py # Monkey-patching framework
├── platform.py # OS detection
├── scheduler.py # Task scheduling (Deadline)
├── serving.py # Server lifecycle management
├── testutil.py # Test utilities
├── time.py # Timestamp parsing
└── venv.py # Virtual environment management
Architecture Deep Dive
Gateway Handler Chain
The LocalstackAwsGateway in aws/app.py orchestrates request processing through a chain of handlers:
Request Handlers (20+):
- Account ID extraction from SigV4 headers
- Service identification and routing
- Request parsing and validation
- API key validation (for API Gateway)
- Integration request transformation
Exception Handlers (4):
- AWS error response formatting
- Internal server error handling
- Unsupported media type errors
- Authentication errors
Response Handlers (8):
- CORS header injection
- Response transformation
- Content type negotiation
Finalizers (2):
- Metrics collection
- Connection cleanup
Service Plugin System
Services are loaded via the plux plugin framework using Python entry points. The ServicePluginManager in services/plugins.py handles:
- Discovery: Scans entry points in the
localstack.servicesnamespace - Lazy loading: Services initialize only on first API request
- State tracking: Services transition through states:
disabled→starting→available→running→stopping - Error handling: Failed services raise
ServiceStateException
# Service lifecycle
ServiceDisabled → ServiceStarting → ServiceAvailable → ServiceRunning
→ ServiceStopping
Multi-Account/Region Store
The stores.py module provides AccountRegionBundle for service state isolation:
- Each service maintains state per (account_id, region) tuple
- Account IDs validated as 12-digit strings
- Region names validated against known AWS regions
- Cross-account and cross-region attributes supported via
CrossAccountAttributeandCrossRegionAttribute
Moto Integration
Many services delegate to the moto library via services/moto.py:
- Request routing maps to moto backends
CommonServiceExceptionwraps moto errors as AWS-compatible responses- Custom providers can override moto behavior per service
Code Patterns & Conventions
Monkey-Patching Framework
utils/patch.py provides a structured patching system:
from localstack.utils.patch import patch
@patch(target=SomeClass.some_method)
def custom_behavior(fn, self, *args, **kwargs):
# pre-processing
result = fn(self, *args, **kwargs)
# post-processing
return result
Restrictions: cannot patch classes implementing __getattr__.
Batcher Utilities
utils/batching.py provides Batcher and AsyncBatcher for collecting items and processing in batches. Raises ValueError if items are added after the batcher is stopped.
Safe Dict Navigation
utils/collections.py provides safe navigation into nested dicts using JSON Path-like syntax ($.key.subkey). Root node must start with $.
Port Management
utils/net.py provides PortRange for managing dynamic port allocation with reservation tracking. Raises PortNotAvailableException when no ports are available.
Testing Strategy
The codebase uses a comprehensive testing framework:
Test Types
| Type | Location | Purpose |
|---|---|---|
| Unit tests | tests/unit/ |
Isolated component testing |
| Integration tests | tests/aws/ |
Full AWS API emulation testing |
| Parity tests | tests/aws/ (snapshot) |
Compare LocalStack vs real AWS responses |
Testing Rules (from CONTRIBUTING.md)
- R01: Tests must be reproducible and deterministic
- R02: Each test must clean up after itself
- R03: Tests should not depend on execution order
- R04: Use pytest fixtures for resource setup
- R05-R14: Additional rules covering naming conventions, assertions, timeouts, and resource management
Parity/Snapshot Testing
The parity testing framework captures responses from both LocalStack and real AWS, then compares them via snapshot files. This ensures behavioral compatibility:
@markers.aws.validated
def test_s3_put_get(self, aws_client, s3_bucket):
aws_client.s3.put_object(Bucket=s3_bucket, Key="test", Body=b"data")
result = aws_client.s3.get_object(Bucket=s3_bucket, Key="test")
snapshot.match("get_object", result)
Running Tests
# Unit tests
pytest tests/unit/
# Integration tests (requires running LocalStack)
pytest tests/aws/services/s3/
# With verbose output
pytest -v tests/unit/utils/
Extension API
The extensions system in extensions/api.py provides lifecycle hooks for custom plugins:
from localstack.extensions.api import Extension
class MyExtension(Extension):
name = "my-extension"
def on_extension_load(self):
"""Called when the extension is loaded."""
pass
def on_platform_start(self):
"""Called when LocalStack platform starts."""
pass
def update_gateway_routes(self, router):
"""Add custom routes to the gateway."""
pass
def update_request_handlers(self, handlers):
"""Add custom request handlers."""
pass
def update_response_handlers(self, handlers):
"""Add custom response handlers."""
pass
def on_platform_ready(self):
"""Called when all services are ready."""
pass
def on_platform_shutdown(self):
"""Called on shutdown."""
pass
Scaffold Tool
The scaffold tool generates AWS Service Framework (ASF) API stubs from AWS service models:
# Generate stubs for a new service
localstack scaffold generate ec2 --doc --save --path ./localstack-core/localstack/aws/api
# Upgrade existing stubs to match latest service model
localstack scaffold upgrade --doc --path ./localstack-core/localstack/aws/api
| Flag | Default | Description |
|---|---|---|
--doc/--no-doc |
--no-doc |
Generate docstrings from service model |
--save/--print |
--print |
Save to disk or print to stdout |
--path |
./localstack-core/localstack/aws/api |
Output directory |
Key Classes Reference
| Class | Module | Purpose |
|---|---|---|
LocalstackRuntime |
runtime/runtime.py |
Main runtime lifecycle (run, shutdown, exit, is_ready) |
LocalstackAwsGateway |
aws/app.py |
Gateway with handler chain |
InitScriptManager |
runtime/init.py |
Init hook discovery and execution |
ShellScriptRunner |
runtime/init.py |
Executes .sh init scripts |
PythonScriptRunner |
runtime/init.py |
Executes .py init scripts |
ShutdownHandlers |
runtime/runtime.py |
Priority-based shutdown handler registry |
Directories |
config.py |
10 directory path properties |
HostAndPort |
config.py |
IPv4/IPv6 address parsing |
UniqueHostAndPortList |
config.py |
Deduplicating listen address list |
ServiceProviderConfig |
services/plugins.py |
Service provider configuration |
DnsServerProtocol |
dns/server.py |
DNS record management (add_host, delete_host, add_alias) |
RecordType |
dns/server.py |
DNS record types (A, AAAA, CNAME, TXT, MX, SOA, NS, SRV) |
PortRange |
utils/net.py |
Dynamic port allocation with reservation |
Batcher / AsyncBatcher |
utils/batching.py |
Batch processing utilities |
Extension |
extensions/api.py |
Plugin lifecycle interface |
Container / RunningContainer |
utils/bootstrap.py |
Docker container management |
LocalstackContainerServer |
utils/bootstrap.py |
Container server wrapper |
HealthResource |
services/edge.py |
Health endpoint handler |
Contributing
From CONTRIBUTING.md:
- Pull requests: Fork, create feature branch, submit PR against
main - Coding style: Follow existing patterns; use
blackfor formatting - CLA: Contributors must sign the Contributor License Agreement
- Tests: All changes must include appropriate test coverage
- Documentation: Update relevant docs for user-facing changes
9. Integration Points
AWS SDK Integration
Any AWS SDK can be configured to use LocalStack by setting the endpoint URL:
| SDK | Configuration Method |
|---|---|
| Python (boto3) | endpoint_url="http://localhost:4566" |
| JavaScript (v3) | endpoint: "http://localhost:4566" |
| Java | .endpointOverride(URI.create("http://localhost:4566")) |
| Go | EndpointResolver configuration |
| .NET | ServiceURL = "http://localhost:4566" |
| Rust | endpoint_url("http://localhost:4566") |
IaC Tool Integration
| Tool | Integration Method |
|---|---|
| Terraform | tflocal wrapper or manual provider endpoints |
| CDK | cdklocal wrapper |
| Pulumi | AWS provider endpoint configuration |
| CloudFormation | Direct via awslocal cloudformation |
| Serverless Framework | serverless-localstack plugin |
| SAM CLI | --endpoint-url flag |
Docker Integration
LocalStack interacts with Docker in multiple ways:
- Docker socket: Required for Lambda container management
- Docker networks: Configurable for inter-container communication
- Docker images: Custom images via
LAMBDA_RUNTIME_IMAGE_MAPPING - Docker flags: Passthrough via
DOCKER_FLAGSandLAMBDA_DOCKER_FLAGS
DNS Integration
The built-in DNS server supports:
| Record Type | Support |
|---|---|
| A | Full |
| AAAA | Full |
| CNAME | Full |
| TXT | Full |
| MX | Full |
| SOA | Full |
| NS | Full |
| SRV | Full |
Operations available via DnsServerProtocol:
add_host(name, ip)— Add A/AAAA recorddelete_host(name)— Remove recordadd_host_pointing_to_localstack(name)— Add record pointing to LocalStack gatewayadd_alias(source, target)— Add CNAME aliasadd_skip(name)— Skip resolution for nameclear()— Remove all records
The DNS server starts at priority 20 (before Pro activation) and shuts down at priority -30 (after all services).
Runtime Hooks
| Hook | When | Use Case |
|---|---|---|
on_runtime_create |
Runtime object created | Early initialization |
on_infra_start |
Infrastructure starting | Pre-service setup |
on_infra_ready |
All services ready | Integration triggers |
on_infra_shutdown |
Shutdown initiated | Cleanup |
Analytics & Telemetry
LocalStack tracks environment variable presence for analytics:
- TRACKED_ENV_VAR: 60+ variables whose values are reported
- PRESENCE_ENV_VAR: 25+ variables where only presence/absence is reported
This data helps the LocalStack team understand feature usage. No service data or request content is transmitted.
Known Compatibility Issues
| Issue | Service | Description | Workaround |
|---|---|---|---|
| #13858 | Cognito | MFA challenge not issued for SOFTWARE_TOKEN_MFA |
Skip MFA in local testing |
| #13899 | S3 | Healthcheck broken in S3-only image | Use full localstack/localstack image |
| #13580 | CDK | Bootstrap fails on free tier | Wait for full initialization |
| #13882 | Route53 | DNS stops after pod state load | Restart LocalStack |
| #13883 | Core | LOCALSTACK_HOST breaks healthcheck |
Leave unset unless needed |
| #13886 | KMS | ECC_NIST_EDWARDS25519 validation error | Use RSA or standard ECC |
| #13898 | EventBridge | Binary type serialization failure in Pipes | Use string-encoded binary |
| #13867 | ECR | list-images throws ImageNotFoundException |
Use describe-images |
| #13585 | DynamoDB | Composite key limitations | Use simple key schemas |
| #13488 | EC2/Terraform | Burstable instance regression | Update LocalStack |
| #13039 | Core | HTTP/2 protocol errors | Use GATEWAY_SERVER=twisted |
| #13020 | Lambda | Hypercorn 500 errors on creation | Use GATEWAY_SERVER=twisted |
| #13138 | Kinesis | Mock engine returns latest record only | Switch to kinesalite engine |
| #13157 | CloudFront | Routing fails with IP/URL access | Use localhost |