A peek behind the scenes at how LocalStack works: a single gateway on port 4566, a provider-based architecture, the difference between stateful emulation and mocking, and the emulator's core components.

In episode 1 we understood why LocalStack exists: solving the problems of cost, speed, and reproducibility by mimicking AWS APIs locally. Now the question shifts from "why" to "how" — how can a single emulator pretend to be dozens of AWS services at once?
Understanding this architecture matters because it will shape your mental model. When you later debug why S3 behaves slightly differently from DynamoDB, or why IAM isn't as strict as real AWS, the answer lies in the architecture we're about to discuss.
LocalStack's underlying principle is simple: all AWS requests come in through a single endpoint, then get routed to the right provider based on the requested service and action.
Historically, LocalStack listened on many ports numbered 457x — one port per service. The modern architecture replaces that with a single gateway endpoint at http://localhost:4566: one address, dozens of APIs.
How does the gateway know which service is being requested? It detects it from the request path and headers. For example, a request to a bucket path with the S3 service Host header is treated as an S3 operation, while a request to a DynamoDB path is routed to the DynamoDB provider. You don't need to care about the routing details — what matters is the consistency:
http://localhost:4566./_localstack/health to verify status.awslocal s3 ls
awslocal dynamodb list-tables
awslocal sts get-caller-identityAll three commands above hit the same endpoint (http://localhost:4566), yet each is executed by a different provider.
This is the most important concept in this episode. LocalStack is built with a provider-based pattern: each AWS service is implemented as a provider (plugin) that adheres to the AWS API contract. The gateway only handles routing — the service logic lives in each provider.
Think of it like extensions in VS Code: the core editor only provides the framework, and each extension adds capabilities. Likewise with LocalStack: the emulator framework handles routing, configuration, and lifecycle, while the s3, dynamodb, lambda, and other providers implement the API details of each service.
The consequences of this architecture show up in two ways:
SERVICES=s3,dynamodb, only two providers are loaded — the container is lighter and boots faster.You can easily inspect the service coverage via the CLI:
localstack servicesThe output shows the recognized service names — from s3, dynamodb, lambda, sqs, sns, to apigateway — along with indicators of their support level.
Because each provider is written separately, the implementation approaches vary too. The two poles are:
| Type | Example Services | Behavior |
|---|---|---|
| Stateful | S3, DynamoDB, SQS, Lambda | Stores real state; data persists and can be read back |
| Mock / simulated | IAM, Route 53, STS | Mimics responses and light validation; state isn't fully realistic |
This difference matters for setting expectations. A DynamoDB table created with create-table genuinely stores items — you can put-item and then get-item and get your data back. IAM, on the other hand, only validates policies superficially: it doesn't enforce permissions as strictly as real AWS. For development and integration testing, stateful services are the most valuable; for mock services, just be aware of their limitations.
LocalStack isn't a single program, but an interconnected ecosystem. Get to know its five core components.
The localstack CLI is the primary interface for controlling the emulator: localstack start, localstack status, localstack logs, and so on. This CLI is what starts the Docker container and waits until it's ready. Without the CLI, managing the emulator means holding onto Docker directly — the CLI makes it more convenient and consistent.
The entire emulator runs inside the localstack/localstack image. Since v2026.03.0 this image is a single one for Community and Pro — enabling Pro features is done through the LOCALSTACK_API_KEY variable, not a different image. Tags are also available for development workflows:
| Tag | Meaning |
|---|---|
stable | Latest stable release |
latest | Same as stable for the single image |
dev | Latest development build |
nightly | Nightly build, most cutting-edge, most risky |
For everyday work use stable; nightly is only for trying features before an official release. The latest stable release at the time this article was written is 2026.07.0.
awslocal is an AWS CLI wrapper that automatically adds --endpoint-url=http://localhost:4566. It saves you from typing the endpoint over and over and makes commands easier to read.
The LocalStack Web Application (at app.localstack.cloud) is a graphical dashboard for viewing emulator state: containers, active services, and logs. Advanced features like Cloud Pods and AWS Replicator (Pro) are operated through this interface. For Community, the terminal remains the main interface.
The emulator is configured almost entirely through environment variables. Some of the most commonly used:
| Variable | Function |
|---|---|
PERSISTENCE=1 | Saves state across restarts |
DEBUG=1 | Detailed logging for debugging |
SERVICES=s3,dynamodb | Limits which services are activated |
LOCALSTACK_API_KEY | Enables Pro features |
LAMBDA_EXECUTOR | Determines how Lambda executes |
All of these variables will be practiced in depth in episode 4.
To solidify your understanding, let's follow a single awslocal s3 mb s3://bucket request:
awslocal calls the AWS CLI with --endpoint-url=http://localhost:4566.CreateBucket) and service (s3).This whole flow happens in milliseconds on your local machine — no network to the cloud, no cost.
Warning
Don't confuse the gateway with the container. localhost:4566 is the gateway that receives requests from the host; behind it, the LocalStack container runs the provider logic. When you run docker ps, you'll see a single localstack/localstack container exposing port 4566 to the host.
Note
Thanks to API parity, your SDK code (boto3, aws-sdk) uses the exact same flow. The only difference is in one place: the value of endpoint_url or the AWS_ENDPOINT_URL variable.
http://localhost:4566 replaces the many 457x ports — one endpoint for all services.localstack CLI, the localstack/localstack image, awslocal, the Web Application, and env var configuration.In the next episode, episode 3, we start hands-on: running LocalStack for the first time with localstack start and Docker Compose, waiting for the running status, then verifying with localstack status, localstack services, and awslocal sts get-caller-identity. Get your Docker ready now!