lstk Automation & CI
Interactive and non-interactive mode
Section titled “Interactive and non-interactive mode”lstk automatically selects its output mode:
- Interactive mode (TUI): used when both stdin and stdout are connected to a terminal.
Commands like
start,stop,restart,status,login,update, and the confirmation prompt ofvolume cleardisplay a Bubble Tea-powered terminal UI. - Non-interactive mode (plain text): used when stdin or stdout isn’t a terminal, for example when you pipe or redirect the output, or in a typical CI job.
Force this in a terminal with
--non-interactive.
# Force plain output even in an interactive terminallstk --non-interactive startTargeting an external emulator
Section titled “Targeting an external emulator”By default lstk discovers the emulator it manages through local Docker.
The --endpoint-url <url> global flag (or the LSTK_ENDPOINT_URL environment variable) instead points a command at an emulator lstk did not start: a Docker Compose or host-network deployment, or one running in CI or on another machine.
# Run against an emulator reachable at a custom URLlstk --endpoint-url http://localhost:4566 az group list
# Equivalent via the environmentLSTK_ENDPOINT_URL=http://localhost:4566 lstk statusPut --endpoint-url before az; anything after az goes to the Azure CLI, which doesn’t know the flag.
The endpoint is resolved from, in order of precedence: the --endpoint-url flag, LSTK_ENDPOINT_URL, then AWS_ENDPOINT_URL (a full synonym for LSTK_ENDPOINT_URL, one tier lower).
Both http:// and https:// URLs are accepted (any other scheme is rejected), and the scheme is preserved end-to-end, so lstk --endpoint-url https://localhost.localstack.cloud:4566 status talks to the emulator over HTTPS.
The commands that accept an external endpoint are the ones that only talk to an already-running emulator: az and status.
doctor also uses it, to check the host names derived from the endpoint.
Commands that manage the emulator’s lifecycle or on-disk state have no remote equivalent and reject any endpoint source: start, the bare lstk, stop, restart, logs, and volume.
For example, lstk --endpoint-url http://localhost:4566 start fails with start does not support --endpoint-url.
lstk detects the emulator’s type (AWS, Azure, or Snowflake) by probing the endpoint’s health API.
There is no flag or config setting to override the detected type, and a failed probe stops the command, for example with could not reach LocalStack emulator at <url> when the endpoint isn’t a LocalStack emulator.
The AWS-only tools (aws, terraform, cdk, sam) reject an endpoint whose detected type is not AWS.
Structured output
Section titled “Structured output”The global --json flag makes a command emit a single, machine-readable JSON object on stdout instead of human-oriented text, for scripting and CI.
JSON support is available per command: the bare lstk, start, stop, status, update, and doctor accept --json.
Commands without JSON support, such as logs and config path, reject it with an error envelope (error.code: NOT_JSON_CAPABLE) rather than silently printing plain text.
Every JSON-capable command writes exactly one JSON object with the following envelope shape:
{ "schemaVersion": 1, "command": "stop", "status": "ok", "data": { "emulators": [ { "type": "azure", "name": "localstack-azure", "wasRunning": true } ] }, "warnings": [], "error": null}| Field | Type | Description |
|---|---|---|
schemaVersion |
integer | Wire-format version of the envelope, always 1 for this schema. Check it once before parsing. |
command |
string | The command that produced the envelope (e.g. "stop", "status"). |
status |
string | "ok" or "error" — branch on this first. |
data |
object or null |
Command-specific result. Non-null when status is "ok", null when it is "error". |
warnings |
array | Non-fatal notices, always present (empty array when there are none). Each entry is { "code", "message" }. |
error |
object or null |
The machine-readable failure. Non-null when status is "error", null otherwise. |
When status is "error", the error object carries a stable code (e.g. EMULATOR_NOT_RUNNING, AUTH_REQUIRED, RUNTIME_UNAVAILABLE), a coarse category, a human-readable message (informational only — branch on code, not message), and a retryable boolean:
{ "schemaVersion": 1, "command": "status", "status": "error", "data": null, "warnings": [], "error": { "code": "EMULATOR_NOT_RUNNING", "category": "EMULATOR", "message": "LocalStack Azure Emulator is not running", "retryable": false }}Exit codes
Section titled “Exit codes”With --json, the process exit code tells you whether the command succeeded and flags a missing auth token.
For every other failure, read error.code from the envelope:
| Exit code | Meaning |
|---|---|
0 |
status: "ok". |
1 |
status: "error" for any code other than AUTH_REQUIRED. Usage errors, such as an unknown flag, also exit with 1. |
4 |
error.code == "AUTH_REQUIRED": no auth token was found (run lstk login or set LOCALSTACK_AUTH_TOKEN). |
lstk also has exit code 3 for CONFIRMATION_REQUIRED, but only the AWS-only reset command returns it.
Without --json, lstk exits with 0 on success and 1 on any failure, including a missing auth token.
A proxied tool passes its own exit code through: when lstk az group show fails because the resource group doesn’t exist, lstk exits with the Azure CLI’s code 3.
Environment variables
Section titled “Environment variables”The following environment variables configure lstk itself (not the LocalStack container):
| Variable | Description |
|---|---|
LOCALSTACK_AUTH_TOKEN |
Auth token for non-interactive runs or to skip browser login. Takes precedence over a token stored in the keyring. |
LSTK_ENDPOINT_URL |
Target an existing, externally-managed emulator at this URL (equivalent to --endpoint-url). AWS_ENDPOINT_URL is a lower-precedence synonym. See Targeting an external emulator. |
LOCALSTACK_HOST |
Override the host (and optional port) used when resolving and printing the emulator endpoint. Bypasses the localhost.localstack.cloud DNS probe. lstk setup azure and lstk az start-interception register https://azure.<host> as the Azure CLI endpoint, so for Azure the value must be a host name whose azure. subdomain resolves to the emulator. |
LOCALSTACK_DISABLE_EVENTS |
Set to 1 to stop lstk, and extensions such as doctor, from sending usage events. |
DOCKER_HOST |
Override the Docker daemon socket (e.g. unix:///home/user/.colima/default/docker.sock). |
LSTK_KEYRING |
Set to file to force file-based token storage instead of the system keyring. |
LSTK_STARTUP_TIMEOUT |
Startup readiness deadline for lstk start, as a Go duration (e.g. 90s, 2m). Zero/unset uses the per-mode default (20s interactive, 60s non-interactive). See start. |
LSTK_OTEL |
Set to 1 to enable OpenTelemetry trace export (disabled by default). See OpenTelemetry tracing. |
LSTK_GITHUB_TOKEN |
Optional GitHub token used when checking for or downloading lstk updates (raises GitHub API rate limits). |
LSTK_CHECK_FOR_UPDATE_ON_STARTUP |
Set to false to skip the update check when the emulator starts, or true to force it. Overrides the check_for_update_on_startup key under [cli] in config.toml. |
LSTK_API_ENDPOINT |
Override the LocalStack platform API base URL. Default: https://api.localstack.cloud. |
LSTK_WEB_APP_URL |
Override the LocalStack Web Application URL used for browser login. Default: https://app.localstack.cloud. |
When LSTK_OTEL is enabled, the standard OTEL_EXPORTER_OTLP_* environment variables are honored by the OpenTelemetry SDK.
Container runtime discovery
Section titled “Container runtime discovery”lstk talks to a Docker-compatible runtime and works with Docker Desktop, Rancher Desktop, Colima, OrbStack, Lima, and Podman.
It resolves the daemon endpoint in this order:
DOCKER_HOST, if set, always wins.DOCKER_CONTEXTor the active Docker CLI context, when it isn’tdefaultand its local socket (or named pipe, on Windows) is reachable. A stale or unreachable context is skipped rather than failing.- On Linux, a live
/var/run/docker.sock: a running Docker daemon is preferred over a co-installed runtime such as Podman. - A probe of known runtime sockets (Docker Desktop, Rancher Desktop, Colima, OrbStack, Podman, Lima). Each candidate is dialed, not just checked for existence, so a leftover socket file never shadows a live daemon.
- The Docker SDK’s own default.
If no runtime is reachable, the error tailors its suggested start command (rdctl start, colima start, podman machine start, …) to the runtime it detects. Set DOCKER_HOST to point at a specific socket to bypass discovery entirely.
Container-injected variables
Section titled “Container-injected variables”lstk injects several environment variables into the LocalStack container on every start, in addition to any profiles you configure:
| Variable | Default value | Description |
|---|---|---|
LOCALSTACK_AUTH_TOKEN |
(your resolved token) | Passed from the CLI to activate the license. |
GATEWAY_LISTEN |
:4566,:443 |
Ports the emulator binds inside the container. |
MAIN_CONTAINER_NAME |
localstack-azure |
Container name for internal references. |
LOCALSTACK_HOST |
localhost.localstack.cloud:<host port> |
Hostname/port the emulator advertises. |
LOCALSTACK_CLIENT_NAME |
lstk |
Identifies the client that started the emulator. |
LOCALSTACK_CLIENT_VERSION |
(the lstk version) |
Version of the client that started the emulator. |
When a Docker socket is detected it is bind-mounted into the container and DOCKER_HOST=unix:///var/run/docker.sock is injected so the emulator can spawn its own containers.
lstk also forwards host environment variables matching CI and LOCALSTACK_* (the host LOCALSTACK_AUTH_TOKEN is dropped so it cannot override the token resolved by lstk).
It skips, with a warning, any LOCALSTACK_* variable whose value spans several lines or that would replace a critical variable inside the emulator, such as LOCALSTACK_PATH or LOCALSTACK_HOME.
LOCALSTACK_* variables keep their prefix, and the Azure emulator reads them as if it weren’t there: LOCALSTACK_LS_AZURE_PORTAL=1 works like LS_AZURE_PORTAL=1.
The container also gets port mappings for 4566, 443, and the service port range 4510-4559.
OpenTelemetry tracing
Section titled “OpenTelemetry tracing”lstk can export traces of its own command execution over OTLP/HTTP.
Tracing is disabled by default.
Enable it with:
LSTK_OTEL=1 lstk startWhen enabled, every command is wrapped in a span (e.g. lstk.start) recording the exit code and any error.
lstk does not hardcode an export target, so the OpenTelemetry Go SDK reads the standard OTEL_EXPORTER_OTLP_* environment variables automatically (default target: OTLP/HTTP at localhost:4318).
You need an OTLP-compatible backend running to receive the traces.
Logging
Section titled “Logging”lstk writes its own diagnostic logs to lstk.log in its config directory: the directory of the config.toml found through the search order.
This is separate from the LocalStack container logs (which you view with lstk logs).
- The log file is created automatically and appended to across runs.
- When the file exceeds 1 MB, it is cleared on the next run.
- Run
lstk config path(without--config) to find the config directory;lstk.logsits alongside thatconfig.toml. --configdoesn’t move the log: with--config ./other.toml,lstkstill writes to thelstk.login its config directory.