The published image bundles everything the server shells out to — the LocalStack
CLI, awslocal, Terraform + tflocal, AWS CDK + cdklocal, AWS SAM + samlocal,
the Snowflake snow CLI, and the Docker CLI — so the only dependency on your
machine is Docker itself.
The image is multi-arch (linux/amd64 and linux/arm64).
The container talks to your host Docker daemon through the bind-mounted
/var/run/docker.sock. When you ask the server to start LocalStack, localstack start launches a sibling localstack-main container on the host (not nested
inside the MCP container). The MCP server and the IaC CLIs reach that sibling over
the host gateway.
MCP client ── stdio ──► docker run … (MCP server)
│ /var/run/docker.sock (mounted)
▼
host Docker daemon
└─ localstack-main (sibling, publishes :4566 on the host)
Because LocalStack is a sibling container, two things must be configured at run time:
- Reachability — set
LOCALSTACK_HOSTNAME=host.docker.internalso the server and the IaC CLIs target the sibling's published port instead of the container's ownlocalhost. - Host-resolvable mounts —
localstack startasks the host daemon to bind-mount its license/state files intolocalstack-main. Those mount sources must exist at an identical path on the host and inside the MCP container, so point LocalStack's cache at a directory you bind-mount one-to-one (viaXDG_CACHE_HOME). Without this you getMounts denied: … is not shared from the host.
mkdir -p "$HOME/.localstack-mcp"
docker run -i --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$HOME/.localstack-mcp:$HOME/.localstack-mcp" \
-e XDG_CACHE_HOME="$HOME/.localstack-mcp" \
--add-host host.docker.internal:host-gateway \
--add-host s3.host.docker.internal:host-gateway \
--add-host snowflake.localhost.localstack.cloud:host-gateway \
-e LOCALSTACK_AUTH_TOKEN="<YOUR_TOKEN>" \
-e LOCALSTACK_HOSTNAME=host.docker.internal \
localstack/localstack-mcp-server:latest| Flag | Why it's needed |
|---|---|
-v /var/run/docker.sock:/var/run/docker.sock |
Lets the bundled LocalStack CLI drive the host Docker daemon (start/stop the sibling, awslocal exec). |
-v "$HOME/.localstack-mcp:$HOME/.localstack-mcp" + -e XDG_CACHE_HOME=… |
Puts LocalStack's license/machine/volume files on an identically-pathed host directory so the host daemon can bind-mount them into localstack-main. |
--add-host host.docker.internal:host-gateway |
Resolves host.docker.internal on Linux. Harmless on Docker Desktop (Mac/Windows), where it already resolves. |
--add-host s3.host.docker.internal:host-gateway |
Lets CDK's virtual-hosted S3 endpoint resolve when cdklocal uses AWS_ENDPOINT_URL_S3=http://s3.host.docker.internal:4566. |
--add-host snowflake.localhost.localstack.cloud:host-gateway |
Lets the Snowflake CLI reach the sibling Snowflake emulator through the hostname the emulator expects for routing. |
-e LOCALSTACK_AUTH_TOKEN |
Required by every tool in this server. |
-e LOCALSTACK_HOSTNAME=host.docker.internal |
Tells the server + IaC CLIs where the sibling LocalStack lives. |
MCP clients launch the server over stdio. Note that client config files do not
expand $HOME/$PWD — use absolute paths.
Deploys run inside the MCP container, so your project directory must be visible
there. Mount it and pass the in-container path to the localstack-deployer tool.
The simplest convention is to mount it at the same absolute path:
-v "/Users/you/projects/my-infra:/Users/you/projects/my-infra"
Then tell the tool directory: /Users/you/projects/my-infra.
Terraform, SAM, and CDK receive the LocalStack endpoint automatically when
LOCALSTACK_HOSTNAME=host.docker.internal is set. CDK asset publishing is forced
to path-style S3 inside the Docker image, so the single s3.host.docker.internal
alias covers bootstrap asset uploads.
- Extra host aliases. Include the aliases shown in the quick-start command.
- First cold start of LocalStack can take up to ~2 minutes while the runtime
initializes; subsequent starts reuse the persisted volume under
$XDG_CACHE_HOME. - Persistence across MCP restarts. The sibling
localstack-mainkeeps running on the host even if your editor restarts the MCP container — reconnecting finds your stack still up. State persists in$XDG_CACHE_HOME/localstack/volume.
| Symptom | Cause / fix |
|---|---|
Mounts denied: … is not shared from the host |
The cache bind mount / XDG_CACHE_HOME is missing or not under a Docker-shared root. Use a path under your home directory and mount it one-to-one. |
Tools report LocalStack Not Running after start |
Check LOCALSTACK_HOSTNAME=host.docker.internal is set and --add-host is present (Linux). |
Auth Token Required |
LOCALSTACK_AUTH_TOKEN must be passed through (every tool requires it). |
Could not find a running LocalStack container named "localstack-main" |
Set MAIN_CONTAINER_NAME if you renamed it. |
tests/docker/validate-image.mjs is a dependency-free MCP stdio client that drives
the image through real tool calls.
LOCALSTACK_AUTH_TOKEN="<YOUR_TOKEN>" \
HARNESS_TOKEN_REAL=1 \
node tests/docker/validate-image.mjs -- \
docker run -i --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$HOME/.localstack-mcp:$HOME/.localstack-mcp" \
-e XDG_CACHE_HOME="$HOME/.localstack-mcp" \
--add-host host.docker.internal:host-gateway \
--add-host s3.host.docker.internal:host-gateway \
--add-host snowflake.localhost.localstack.cloud:host-gateway \
-e LOCALSTACK_AUTH_TOKEN \
-e LOCALSTACK_HOSTNAME=host.docker.internal \
-v "$PWD/data:/work/data" \
localstack/localstack-mcp-server:latestUse HARNESS_SKIP to skip scenarios, for example:
HARNESS_SKIP=docs,cloudpods,ephemeral,replicator
{ "mcpServers": { "localstack-mcp-server": { "command": "docker", "args": [ "run", "-i", "--rm", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", "/Users/you/.localstack-mcp:/Users/you/.localstack-mcp", "-e", "XDG_CACHE_HOME=/Users/you/.localstack-mcp", "--add-host", "host.docker.internal:host-gateway", "--add-host", "s3.host.docker.internal:host-gateway", "--add-host", "snowflake.localhost.localstack.cloud:host-gateway", "-e", "LOCALSTACK_AUTH_TOKEN", "-e", "LOCALSTACK_HOSTNAME=host.docker.internal", "-v", "/Users/you/projects:/Users/you/projects", "localstack/localstack-mcp-server:latest" ], "env": { "LOCALSTACK_AUTH_TOKEN": "<YOUR_TOKEN>" } } } }