Skip to content

CLI Commands

Thunder is invoked through the thunder binary. All commands follow the same top-level syntax.

Terminal window
thunder [GLOBAL_OPTIONS] <COMMAND> [COMMAND_OPTIONS]

These options apply to every subcommand.

FlagDefaultEnvDescription
-v, --verboseoffRUST_LOGEnable debug logging
--log-format <pretty|json>prettyEDGE_RUNTIME_LOG_FORMATRuntime log output format
--otel-enabledfalseEDGE_RUNTIME_OTEL_ENABLEDEnable OpenTelemetry export
--otel-protocol <http-protobuf>http-protobufEDGE_RUNTIME_OTEL_PROTOCOLOTLP transport protocol
--otel-endpoint <URL>http://127.0.0.1:4318EDGE_RUNTIME_OTEL_ENDPOINTOTLP collector base URL
--otel-service-name <NAME>thunderEDGE_RUNTIME_OTEL_SERVICE_NAMEOTEL resource service.name
--otel-export-interval-ms <MS>5000EDGE_RUNTIME_OTEL_EXPORT_INTERVAL_MSPeriodic export interval
--otel-export-timeout-ms <MS>10000EDGE_RUNTIME_OTEL_EXPORT_TIMEOUT_MSOTEL export timeout
--otel-enable-tracestrueEDGE_RUNTIME_OTEL_ENABLE_TRACESEnable OTEL trace signal
--otel-enable-metricstrueEDGE_RUNTIME_OTEL_ENABLE_METRICSEnable OTEL metrics signal
--otel-enable-logstrueEDGE_RUNTIME_OTEL_ENABLE_LOGSEnable OTEL logs signal
--otel-export-isolate-logstrueEDGE_RUNTIME_OTEL_EXPORT_ISOLATE_LOGSExport isolate logs to OTEL
--otel-isolate-log-batch-size <N>256EDGE_RUNTIME_OTEL_ISOLATE_LOG_BATCH_SIZEMax isolate logs per drain tick

Start the edge runtime server with a dual-listener architecture.

  • Admin listener (default port 9000): serves /_internal/* management endpoints with optional API key authentication.
  • Ingress listener (default port 8080 or Unix socket): serves function invocation requests at /{function_name}/* without authentication.
Terminal window
thunder start [OPTIONS]
FlagDefaultEnvDescription
--admin-host <HOST>0.0.0.0EDGE_RUNTIME_ADMIN_HOSTAdmin listener bind address
--admin-port <PORT>9000EDGE_RUNTIME_ADMIN_PORTAdmin listener port
--api-key <KEY>noneEDGE_RUNTIME_API_KEYAPI key for /_internal/* endpoints
--admin-tls-cert <PATH>noneEDGE_RUNTIME_ADMIN_TLS_CERTTLS certificate for admin listener
--admin-tls-key <PATH>noneEDGE_RUNTIME_ADMIN_TLS_KEYTLS private key for admin listener
FlagDefaultEnvDescription
--host <HOST>0.0.0.0EDGE_RUNTIME_HOSTIngress listener bind address
-p, --port <PORT>8080EDGE_RUNTIME_PORTIngress listener TCP port
--unix-socket <PATH>noneEDGE_RUNTIME_UNIX_SOCKETUnix socket path (mutually exclusive with --port)
--tls-cert <PATH>noneEDGE_RUNTIME_TLS_CERTTLS certificate for ingress (TCP only)
--tls-key <PATH>noneEDGE_RUNTIME_TLS_KEYTLS private key for ingress (TCP only)
FlagDefaultEnvDescription
--max-heap-mib <MIB>128EDGE_RUNTIME_MAX_HEAP_MIBPer-isolate heap limit. 0 = unlimited
--cpu-time-limit-ms <MS>50000EDGE_RUNTIME_CPU_TIME_LIMIT_MSPer-request CPU time limit. 0 = unlimited
--wall-clock-timeout-ms <MS>60000EDGE_RUNTIME_WALL_CLOCK_TIMEOUT_MSPer-request wall clock timeout. 0 = unlimited
--vfs-total-quota-bytes <BYTES>10485760EDGE_RUNTIME_VFS_TOTAL_QUOTA_BYTESWritable VFS quota per isolate (10 MiB)
--vfs-max-file-bytes <BYTES>5242880EDGE_RUNTIME_VFS_MAX_FILE_BYTESMax writable file size in VFS (5 MiB)
FlagDefaultEnvDescription
--pool-enabledfalseEDGE_RUNTIME_POOL_ENABLEDEnable isolate pooling
--pool-global-max-isolates <N>256EDGE_RUNTIME_POOL_GLOBAL_MAX_ISOLATESGlobal isolate cap
--pool-min-free-memory-mib <MIB>256EDGE_RUNTIME_POOL_MIN_FREE_MEMORY_MIBMin free memory for scale-up
--context-pool-enabledfalseEDGE_RUNTIME_CONTEXT_POOL_ENABLEDEnable context-first scheduling
--max-contexts-per-isolate <N>8EDGE_RUNTIME_MAX_CONTEXTS_PER_ISOLATEMax contexts per isolate
--max-active-requests-per-context <N>1EDGE_RUNTIME_MAX_ACTIVE_REQUESTS_PER_CONTEXTMax active requests per context
FlagDefaultEnvDescription
--max-connections <N>10000EDGE_RUNTIME_MAX_CONNECTIONSMax concurrent connections across listeners
--rate-limit <RPS>0EDGE_RUNTIME_RATE_LIMITRequests per second. 0 = unlimited
--max-request-body-size <BYTES>5242880EDGE_RUNTIME_MAX_REQUEST_BODY_SIZEMax request body (5 MiB)
--max-response-body-size <BYTES>10485760EDGE_RUNTIME_MAX_RESPONSE_BODY_SIZEMax response body (10 MiB)
FlagDefaultEnvDescription
--disable-ssrf-protectionfalseEDGE_RUNTIME_DISABLE_SSRF_PROTECTIONAllow fetch() to private IPs
--allow-private-net <CIDR,...>noneEDGE_RUNTIME_ALLOW_PRIVATE_NETAllow specific private subnets
--require-bundle-signaturefalseEDGE_RUNTIME_REQUIRE_BUNDLE_SIGNATURERequire Ed25519 bundle signatures
--bundle-public-key-path <PATH>noneEDGE_RUNTIME_BUNDLE_PUBLIC_KEY_PATHEd25519 public key for verification

SSRF protection blocks fetch() requests to private IP ranges by default:

RangeDescription
127.0.0.0/8Loopback
10.0.0.0/8RFC 1918 private
172.16.0.0/12RFC 1918 private
192.168.0.0/16RFC 1918 private
169.254.0.0/16Link-local / cloud metadata
0.0.0.0/8Reserved
::1/128IPv6 loopback
fc00::/7IPv6 unique local
fe80::/10IPv6 link-local
FlagEnvDescription
--http-outgoing-proxy <URL>EDGE_RUNTIME_HTTP_OUTGOING_PROXYOutgoing HTTP proxy
--https-outgoing-proxy <URL>EDGE_RUNTIME_HTTPS_OUTGOING_PROXYOutgoing HTTPS proxy
--tcp-outgoing-proxy <HOST:PORT>EDGE_RUNTIME_TCP_OUTGOING_PROXYGeneric TCP proxy
--http-no-proxy <HOSTS>EDGE_RUNTIME_HTTP_NO_PROXYHTTP proxy bypass list
--https-no-proxy <HOSTS>EDGE_RUNTIME_HTTPS_NO_PROXYHTTPS proxy bypass list
--tcp-no-proxy <HOSTS>EDGE_RUNTIME_TCP_NO_PROXYTCP proxy bypass list
FlagDefaultEnvDescription
--graceful-exit-timeout <SECS>30Graceful shutdown deadline
--print-isolate-logstrueEDGE_RUNTIME_PRINT_ISOLATE_LOGSPrint isolate console.* to stdout
--sourcemap <none|inline>noneEDGE_RUNTIME_SOURCE_MAPSource map handling for ESZIP modules
--dns-doh-endpoint <URL>https://1.1.1.1/dns-queryEDGE_RUNTIME_DNS_DOH_ENDPOINTDoH endpoint for node:dns
--dns-max-answers <N>16EDGE_RUNTIME_DNS_MAX_ANSWERSMax DNS answers per query
--dns-timeout-ms <MS>2000EDGE_RUNTIME_DNS_TIMEOUT_MSDNS resolver timeout

Basic development server:

Terminal window
thunder start

Production with authentication:

Terminal window
thunder start \
--api-key "$(cat /run/secrets/api-key)" \
--port 8080 \
--max-heap-mib 256

Unix socket ingress:

Terminal window
thunder start \
--api-key "super-secret" \
--unix-socket /var/run/thunder.sock

TLS on both listeners:

Terminal window
thunder start \
--api-key "secret" \
--admin-tls-cert /certs/admin.crt \
--admin-tls-key /certs/admin.key \
--tls-cert /certs/ingress.crt \
--tls-key /certs/ingress.key \
--port 8443

Environment variable configuration:

Terminal window
export EDGE_RUNTIME_API_KEY="my-secret-key"
export EDGE_RUNTIME_PORT=8080
export EDGE_RUNTIME_ADMIN_PORT=9000
thunder start

Security hardened deployment:

Terminal window
thunder start \
--api-key "$(cat /run/secrets/api-key)" \
--port 8080 \
--max-heap-mib 256 \
--max-request-body-size 1048576 \
--max-response-body-size 5242880 \
--max-connections 5000

Corporate network with internal service access:

Terminal window
thunder start \
--api-key "secret" \
--allow-private-net "10.0.0.0/8"

Bundle a JavaScript/TypeScript entrypoint and its dependencies into a deployable package.

Terminal window
thunder bundle --entrypoint <FILE> --output <FILE> [OPTIONS]
FlagDefaultDescription
-e, --entrypoint <FILE>requiredEntry JS/TS file
-o, --output <FILE>requiredOutput bundle file path
--format <eszip|snapshot>eszipPackage format
--manifest <PATH>nonePath to function manifest (v2)

The snapshot format packages a V8 snapshot envelope with an embedded ESZIP fallback. If the snapshot is incompatible at runtime (V8 version mismatch), startup falls back to ESZIP automatically.

When --manifest is provided with flavor: "routed-app" and an empty routes array, the CLI auto-scans a functions/ directory and populates routes. If a sibling public/ directory exists, static asset routes are generated per file.

For TypeScript entrypoints, the bundler runs deno check first when deno is in PATH. Otherwise it falls back to syntax/module-graph validation.

Bundle a single function:

Terminal window
thunder bundle \
--entrypoint ./examples/hello/hello.ts \
--output ./hello.eszip

Bundle with snapshot format:

Terminal window
thunder bundle \
--entrypoint ./examples/hello/hello.ts \
--output ./hello.snapshot.bundle \
--format snapshot

Bundle a routed app with manifest:

Terminal window
thunder bundle \
--entrypoint ./functions/index.ts \
--manifest ./function.manifest.json \
--output ./app.eszip

Watch a directory for file changes, automatically bundle discovered JS/TS files, and deploy or update functions live.

Terminal window
thunder watch [OPTIONS]
FlagDefaultEnvDescription
--path <PATH>.Directory to scan and watch
--host <HOST>0.0.0.0EDGE_RUNTIME_HOSTBind address
-p, --port <PORT>9000EDGE_RUNTIME_PORTListener port
--interval <MS>1000Debounce interval after file changes
--format <eszip|snapshot>snapshotBundle format for auto-deploy
--max-heap-mib <MIB>128EDGE_RUNTIME_MAX_HEAP_MIBPer-isolate heap limit
--cpu-time-limit-ms <MS>50000EDGE_RUNTIME_CPU_TIME_LIMIT_MSCPU time limit
--wall-clock-timeout-ms <MS>60000EDGE_RUNTIME_WALL_CLOCK_TIMEOUT_MSWall clock timeout
--inspect [PORT]9229V8 inspector base port
--inspect-brkoffBreak on first statement
--inspect-allow-remoteoffBind inspector to 0.0.0.0

The watcher scans recursively for .ts and .js files, skipping node_modules, dist, build, .next, .deno, and target directories. File paths are converted to function names by joining path segments with - and removing the extension.

SSRF protection is disabled in watch mode. Functions are deployed without a manifest.

For multiple functions, inspector ports auto-increment from the base port.

Terminal window
thunder watch \
--path ./examples \
--port 9000 \
--interval 500 \
--inspect 9230

Run JavaScript/TypeScript test files in an isolated runtime.

Terminal window
thunder test [OPTIONS]
FlagDefaultDescription
-p, --path <PATH>./tests/js/**/*.tsPath, directory, or glob pattern
-i, --ignore <PATTERN>noneIgnore pattern (repeatable)
--inspect [PORT]9229Enable V8 inspector
--inspect-allow-remoteoffBind inspector to 0.0.0.0

Supported extensions: .ts, .js, .mts, .mjs.

When --inspect is used, exactly one test file must be selected.

Run all tests except helpers:

Terminal window
thunder test \
--path "./tests/js/**/*.ts" \
--ignore "./tests/js/lib/**"

Debug a single test file:

Terminal window
thunder test --path ./tests/js/my_test.ts --inspect 9229

Typecheck source files using deno check, or fall back to syntax/module validation when Deno is unavailable.

Terminal window
thunder check [OPTIONS]
FlagDefaultDescription
-p, --path <PATH>./**/*.{ts,js,mts,mjs,tsx,jsx,cjs,cts}Path, directory, or glob pattern
-i, --ignore <PATTERN>noneIgnore pattern (repeatable)

Supported extensions: .ts, .js, .mts, .mjs, .tsx, .jsx, .cjs, .cts.

Check a directory:

Terminal window
thunder check --path ./examples

Check with exclusions:

Terminal window
thunder check \
--path "./**/*.{ts,tsx}" \
--ignore "./target/**" \
--ignore "./node_modules/**"

All commands return a non-zero exit code on failure. Common failure causes:

  • Invalid or missing paths
  • Module graph or build failures
  • Bundling or deployment errors
  • Test failures
  • deno check type errors