Skip to content
Get Started for Free

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.

Terminal window
lstk doctor

Doctor 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.

Terminal window
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.
Terminal window
# Run every check
lstk doctor
# Only the network lane, with full detail
lstk doctor network --verbose
# Only the container and system lanes
lstk doctor container system
# Machine-readable output for scripts, CI, and agents
lstk --json doctor

--json works in either position — lstk --json doctor and lstk doctor --json produce the same envelope.

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-sync
container: container.engine
system: 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.

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.cloud doesn’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>.

Terminal window
lstk --endpoint-url http://localhost:4566 doctor network

This 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.

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:

Terminal window
lstk doctor network --target internal-mirror.corp.example:443

--target is repeatable; the DNS, HTTPS, and certificate checks run against the first target given.

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:

Output
✔︎ All checks passed

When 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.

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).

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 in data. status: "error" means doctor couldn’t produce a report at all — a rejected invocation or an internal failure — and data is null. status answers “did doctor run?”; data.verdict.result answers “can LocalStack run?”.
  • Findings are always complete, whether --verbose was passed or not — every check is listed with its pass/fail/skip/error status. --verbose only changes the text rendering; --json never 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:

Terminal window
lstk --json doctor > doctor.json
jq -r '.data.findings[] | select(.status=="fail") | "\(.severity)\t\(.code)"' doctor.json
[ "$(jq -r '.data.verdict.result' doctor.json)" != "blocking" ]

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 --verbose or --json were used, and how many --target values were given (not their values).
  • Your machine ID (the same hashed ID lstk and LocalStack report), OS and architecture, and your auth token.
  • A session ID, so the run can be matched to the lstk invocation 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.

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.*, network.certificate.* How do I configure LocalStack to use my corporate HTTP and HTTPS proxy?, 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* The snowflake.localhost.localstack.cloud hostname doesn’t resolve on my machine, what can I do?
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.

Was this page helpful?