lstk Doctor
lstk doctor checks your machine and network for the problems that most commonly stop LocalStack from starting or activating: DNS or HTTPS to the LocalStack API being blocked, a TLS-intercepting proxy whose certificate LocalStack does not trust, no reachable container engine, or too little memory or disk.
Every check either passes or produces a finding with a concrete fix, so you can resolve the cause before it shows up mid-run as an opaque error.
Run it on a new machine before your first lstk start, before opening a support ticket, or any time LocalStack fails to start behind a corporate network.
lstk doctorDoctor does not require a running emulator and does not change anything on your system — it only reads the environment, opens test connections, and reports what it finds.
doctor
Section titled “doctor”lstk doctor [category...] [options]| Argument / Option | Description |
|---|---|
network, container, system |
Optional positional categories. Run only the listed lanes; default is all three. See What it checks. |
--target <host:port> |
Probe this host instead of the default api.localstack.cloud:443. Repeatable. |
-v, --verbose |
Show passing and skipped checks with their evidence, not only failures. |
--json |
Emit one machine-readable JSON object instead of human-oriented text. See JSON output. |
-h, --help |
Show the built-in help. |
# Run every checklstk doctor
# Only the network lane, with full detaillstk doctor network --verbose
# Only the container and system laneslstk doctor container system
# Machine-readable output for scripts, CI, and agentslstk --json doctor--json works in either position — lstk --json doctor and lstk doctor --json produce the same envelope.
What it checks
Section titled “What it checks”Doctor runs 11 checks across three lanes — network, container, and system. Within a lane, dependent checks only run once their prerequisite has passed; a failed prerequisite skips its dependents and collapses them into a single note rather than a wall of noise. The lanes themselves, and every independent check within a lane, run concurrently, so one pass surfaces every root cause instead of stopping at the first one.
network: network.proxy, network.dns ──▶ network.https ──▶ network.certificate network.localstack network.local-dns, network.local-dns-s3, network.local-dns-synccontainer: container.enginesystem: system.memory, system.disk| Check | Asks | On failure |
|---|---|---|
network.proxy |
Is an egress proxy configured (OUTBOUND_HTTPS_PROXY / HTTPS_PROXY)? |
Informational when detected — later checks route through it. A proxy value that fails to parse is fixable. |
network.dns |
Does the target host (api.localstack.cloud by default) resolve? |
Blocking |
network.https |
Can an HTTPS/TLS connection be opened to it, through the proxy if one is set? | Blocking |
network.certificate |
Does the certificate chain verify against LocalStack’s own trust store, and pass the stricter validation LocalStack itself applies? | Fixable for an untrusted, self-signed, expired, or mismatched certificate. Blocking only when the untrusted CA itself fails strict validation. |
network.localstack |
If an emulator is running, does its /_localstack/health endpoint respond? |
Informational. Skipped when nothing is running. |
network.local-dns |
Does localhost.localstack.cloud resolve to a loopback address? |
Informational — LocalStack stays reachable via localhost / 127.0.0.1. |
network.local-dns-s3 |
Does the S3 virtual-host shape bucket.s3.localhost.localstack.cloud resolve to loopback? |
Informational — path-style S3 URLs still work. |
network.local-dns-sync |
Does sync-localhost.localstack.cloud resolve to loopback? AWS SDKs dial this name for Step Functions’ StartSyncExecution. |
Informational — only that one API is affected. |
container.engine |
Is a container engine (Docker, Podman, Colima, Rancher Desktop, …) reachable? | Fixable |
system.memory |
Is enough memory available? Doctor reads the container engine’s VM allocation where one exists (Docker Desktop, Colima, …), otherwise host RAM. | Blocking below 2 GB. Fixable between 2 GB and the recommended 4 GB. |
system.disk |
Is there enough free space on the volume backing LocalStack’s data directory? | Blocking below 2 GB. Fixable between 2 GB and the recommended 10 GB. |
Severity levels
Section titled “Severity levels”Every failed check carries one of three severities, which drive both the final verdict and the exit code:
- Blocking — LocalStack cannot start or activate here until it’s resolved. For example:
api.localstack.clouddoesn’t resolve, outbound HTTPS is dropped, or available memory is under 2 GB. - Fixable — LocalStack can run once a configuration change is made; doctor prints the change, typically an environment variable and value. For example: an untrusted corporate CA, no container engine reachable, memory between 2 GB and 4 GB.
- Informational — a heads-up about one specific feature; LocalStack itself will run, but that feature won’t work until the finding is addressed. All
network.local-dns*findings are informational.
Doctor is deliberately cautious: when it can’t be sure a setup is clean, it reports a finding rather than a pass.
Certificate trust doesn’t follow the OS trust store
Section titled “Certificate trust doesn’t follow the OS trust store”network.certificate validates the chain the same way LocalStack’s own Python client does — against an embedded CA bundle plus REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE — not against your operating system’s trust store.
Adding a corporate CA to the OS trust store (or running something like update-ca-certificates) does not clear this finding.
Point REQUESTS_CA_BUNDLE (or CURL_CA_BUNDLE) at a file that concatenates the default CA bundle with your corporate root instead; doctor’s fix output gives you the exact value.
Checking the network for an external emulator
Section titled “Checking the network for an external emulator”When lstk targets an externally managed emulator via --endpoint-url or LSTK_ENDPOINT_URL (see Targeting an external emulator), the three network.local-dns* checks additionally probe the names AWS SDKs derive from that endpoint’s host: <host>, bucket.s3.<host>, and sync-<host>.
lstk --endpoint-url http://localhost:4566 doctor networkThis is how a plain localhost endpoint gets flagged as breaking Step Functions’ StartSyncExecution — sync-localhost resolves nowhere.
These derived names only need to resolve at all (not to loopback), since the endpoint may legitimately be remote; an IP-literal endpoint derives no names, since SDKs address it path-style instead.
The check is skipped entirely when no endpoint was conveyed or the endpoint host is already localhost.localstack.cloud.
Probing a different host
Section titled “Probing a different host”By default the network lane probes api.localstack.cloud:443, the host LocalStack contacts to license itself.
Pass --target to probe a different host:port instead — an internal mirror, for example:
lstk doctor network --target internal-mirror.corp.example:443--target is repeatable; the DNS, HTTPS, and certificate checks run against the first target given.
Output
Section titled “Output”By default doctor prints only failures and informational notes, each with its evidence and fix, closed by a one-line verdict. On a healthy machine, that verdict is the entire report:
✔︎ All checks passedWhen something’s wrong, the verdict counts the issues and names the worst severity — for example 1 issue found - fixable with config changes or 2 blocking issues found - LocalStack cannot start here.
With --verbose, every check is shown, passes and skips included.
In an interactive terminal this renders as one CHECK | STATUS | SUMMARY overview table, with failures still expanded below into full detail (evidence and fixes).
Piped or run with --non-interactive, it renders as one line per check instead, so the output stays stable for logs and CI.
Each finding carries a stable dotted code, such as network.certificate.untrusted or system.memory.low — this is the identity used in JSON and telemetry.
In human-readable text the same code is shown upper-cased with hyphens, NETWORK-CERTIFICATE-UNTRUSTED, to read more like a symbolic name.
Exit codes
Section titled “Exit codes”0 All checks passed.1 Only fixable issues were found. LocalStack can run once they're addressed.2 A blocking issue was found — LocalStack cannot start here.3 Doctor itself could not complete a check.Informational findings never affect the exit code.
The whole run has a 90-second deadline. Every individual probe bounds itself well inside that, so the deadline should only fire when a check hangs; when it does, the run is still reported with whatever findings it collected, flagged as incomplete (see JSON output).
JSON output
Section titled “JSON output”With --json, doctor writes exactly one JSON object to stdout and nothing else, so scripts, CI jobs, and coding agents can act on a diagnosis without parsing prose.
It’s the same result envelope every JSON-capable lstk command uses:
{ "schemaVersion": 1, "command": "doctor", "status": "ok", "data": { "verdict": { "result": "fixable", "headline": "1 issue found - fixable with config changes", "exitCode": 1, "counts": {"total": 6, "pass": 2, "fail": 1, "skip": 3, "error": 0, "blocking": 0, "fixable": 1} }, "categories": ["network"], "findings": [ { "code": "network.certificate.untrusted", "category": "network", "status": "fail", "severity": "fixable", "summary": "the certificate presented for api.localstack.cloud:443 is not trusted", "evidence": {"host": "api.localstack.cloud:443", "issuer": "CN=corp-proxy"}, "fixes": [ { "envVar": "REQUESTS_CA_BUNDLE", "value": "/path/to/corp-ca.pem", "note": "concatenate your corporate CA with the default CA bundle", "docUrl": "https://docs.localstack.cloud/aws/customization/networking/" } ] } ] }, "warnings": [], "error": null}| Question | Field |
|---|---|
| Did doctor work? | status: "ok" or "error". |
| Can LocalStack run here? | data.verdict.result: healthy, fixable, blocking, or incomplete. |
| What exactly is wrong? | data.findings[].code — the stable dotted identifier. |
| How do I fix it? | data.findings[].fixes[] — an environment variable and value where one applies, a note, and a docs URL. |
| Which lanes ran? | data.categories — a scoped invocation only lists the lanes it actually ran. |
A few properties make the envelope safe to automate against:
- A diagnosed problem is still a successful diagnosis. Finding a blocking issue is
status: "ok", with the problem described indata.status: "error"means doctor couldn’t produce a report at all — a rejected invocation or an internal failure — anddataisnull.statusanswers “did doctor run?”;data.verdict.resultanswers “can LocalStack run?”. - Findings are always complete, whether
--verbosewas passed or not — every check is listed with itspass/fail/skip/errorstatus.--verboseonly changes the text rendering;--jsonnever omits a finding for brevity. - Exit codes keep their meaning — the 0/1/2/3 table applies unchanged under
--json.
data.verdict.result values, worsening in order:
| Result | Meaning |
|---|---|
healthy |
Everything checked passed. |
fixable |
LocalStack fails today, but a configuration change resolves it. |
blocking |
LocalStack cannot start here. |
incomplete |
A check errored, or the run hit its deadline, so the diagnosis has a hole. Still status: "ok". |
warnings is always an array. The one entry defined today is RUN_INCOMPLETE, set when the run hit its overall 90-second deadline before finishing — a signal that findings may be incomplete or misattributed, which plain-text output doesn’t otherwise surface.
When status is "error", error.code is USAGE_ERROR for a rejected invocation (pointing you at lstk doctor --help) or INTERNAL_ERROR for an engine failure.
Even lstk doctor --json --help keeps the one-object contract: the usage text comes back in data.usage rather than printed as prose.
Fail a CI job only on blocking issues, and print the failing codes:
lstk --json doctor > doctor.jsonjq -r '.data.findings[] | select(.status=="fail") | "\(.severity)\t\(.code)"' doctor.json[ "$(jq -r '.data.verdict.result' doctor.json)" != "blocking" ]Telemetry
Section titled “Telemetry”Each completed run reports its outcome to LocalStack’s analytics endpoint, so the diagnoses that fire most often in the field get prioritized for fixes.
Opt out with LOCALSTACK_DISABLE_EVENTS=1, the same variable that disables telemetry for lstk and for LocalStack itself — nothing is collected or sent.
What’s sent, per run:
- The code, category, status, and severity of every check — for example
network.certificate.untrusted,network,fail,fixable. - The overall verdict, exit code, and how long the checks took.
- Which categories were selected, whether
--verboseor--jsonwere used, and how many--targetvalues were given (not their values). - Your machine ID (the same hashed ID
lstkand LocalStack report), OS and architecture, and your auth token. - A session ID, so the run can be matched to the
lstkinvocation that started it.
Never sent: the evidence behind a finding. Hostnames, certificate subjects and issuers, proxy URLs, file paths, and every --target value stay on your machine — a finding travels as its code alone.
Delivery happens in the background after doctor exits, so a slow or unreachable analytics endpoint never delays the command or changes its exit code.
Runs that produce no diagnosis — --help, or a rejected invocation — send nothing.
Acting on common findings
Section titled “Acting on common findings”Doctor prints a fix with every failure. These guides cover the same problems in more depth:
| Finding | Where to go next |
|---|---|
network.dns.*, network.https.* |
What hostnames must LocalStack be able to reach during startup? and How do I configure LocalStack to use my corporate HTTP and HTTPS proxy? |
network.certificate.* |
How do I trust my corporate TLS interceptor certificate (Zscaler, Netskope, and similar) inside LocalStack? and How do I provide a corporate or updated CA bundle to LocalStack? |
network.local-dns* |
Is using localhost.localstack.cloud:4566 as the endpoint recommended? and the DNS Server guide |
network.localstack.* |
Accessing LocalStack via the endpoint URL |
container.engine.* |
Docker is not running and Container runtime discovery |
system.memory.*, system.disk.* |
Raise the memory allocation of your container engine’s VM, or free disk space with docker system prune. |
If a fix doesn’t resolve your issue, attach the output of lstk --json doctor when you contact support — it contains no credentials, and its evidence fields give the support team the same view of your environment that doctor had.