Skip to main content

walkerOS Runner

The walkerOS runner is the walkeros/flow Docker image in self-bundling mode. Point it at a flow config — local file or API — and it bundles, runs, and keeps itself up to date.

Loading...

Runner vs Docker runtime

Runner (this page)Docker runtime
InputFlow config (JSON)Pre-built bundle (.mjs)
BundlingSelf-bundles internallyNone — expects pre-built
API integrationHeartbeat, config polling, hot-swapNone
Use caseSelf-hosted production, managed deploySimple deploy, CI/CD pipelines

Use the runner when you want API visibility, remote config, or hot-swap. Use the Docker runtime when you pre-build bundles in CI and want minimal images.

Deployment modes

Same image, same config format. Each mode adds one env var:

ModeWhat you setWhat happens
A. LocalBUNDLE onlyBundles config, runs. No API.
B. Registered+ WALKEROS_TOKEN + PROJECT_IDSame as A, plus heartbeat. Visible in dashboard.
C. Remote config+ FLOW_IDFetches config from API. Polls for updates. Hot-swaps on change.
D. Managed(set by walkerOS)Same as C, on walkerOS infrastructure.

Mode A: fully local

No signup, no token, no API:

Loading...

Mode B: local config + dashboard

Adds heartbeat registration. The runner appears in your project dashboard:

Loading...

Mode C: remote config with hot-swap

Config is fetched from the API. Polls for updates, hot-swaps on new versions:

Loading...

Environment variables

VariableDefaultDescription
MODEcollectcollect (HTTP event server) or serve (static file server)
PORT8080Server port
BUNDLE/app/flow/bundle.mjsLocal config/bundle path or URL
WALKEROS_TOKENAPI token for registration and config fetch
PROJECT_IDProject ID (required with WALKEROS_TOKEN)
FLOW_IDFlow ID for remote config (requires token + project)
FLOW_NAMEFlow name for multi-flow configs (selects which flow to run)
CACHE_DIR/app/cacheDirectory for last-known-good bundle cache
POLL_INTERVAL30Seconds between config polls (mode C/D)
HEARTBEAT_INTERVAL60Seconds between heartbeats (mode B/C/D)
WALKEROS_APP_URLhttps://app.walkeros.ioAPI base URL

Validation rules

Invalid combinations fail fast with actionable error messages:

Vars setResult
Nothing or BUNDLE onlyMode A — local bundle, no API
WALKEROS_TOKEN + PROJECT_IDMode B — local config + heartbeat
WALKEROS_TOKEN + PROJECT_ID + FLOW_IDMode C/D — remote config + polling
FLOW_ID without tokenError: FLOW_ID requires WALKEROS_TOKEN and PROJECT_ID
WALKEROS_TOKEN without PROJECT_IDError: WALKEROS_TOKEN requires PROJECT_ID
BUNDLE + FLOW_IDStarts with local bundle, polls for remote. Hot-swaps when remote version is newer.

Pipeline

After config is resolved, all modes converge into the same pipeline:

1. Validate env ─────────── all modes ──────────
2. Fetch secrets ──────────── if flow_id set ───
3. Resolve config local | API fetch

┌──────────────────┐
│ config (Flow.Config) │
└──────────────────┘

4. Bundle ───────────────── esbuild ────────────
5. Cache ────────────── write last-known-good ──
6. Run ──────────────────── all modes ──────────
7. Heartbeat ────────────── if token set ───────
8. Poll + hot-swap ──────── if flow_id set ─────

Secrets

When FLOW_ID is set, the runner fetches secrets from the walkerOS cloud API and injects them into process.env before the flow starts. This lets you use $env.SECRET_NAME references in your flow config without manually setting environment variables on the container.

Secrets are managed per-flow in the walkerOS dashboard. The runner handles them automatically:

  1. At startup — secrets are fetched and injected into the environment before config resolution and bundling. The $env.* references in your flow config resolve to the fetched values.
  2. On hot-swap — when a new config version is detected, secrets are re-fetched before re-bundling. This ensures updated secrets take effect alongside the new config.

Error handling

The runner treats secret fetch failures differently depending on the cause:

ErrorBehavior
401/403 (auth failure)Fatal — the runner exits. An invalid token means config fetch will also fail.
404 (no secrets configured)Warning — the runner continues. The flow may not use $env.* references.
500 (server error)Warning — the runner continues without secrets.
Failure during hot-swapThe swap is skipped entirely. The runner keeps running the current flow with existing secrets and retries on the next poll.
note

Secrets only apply in mode C/D (remote config). In local modes, set environment variables directly on the container with -e or in your Docker Compose file.

Polling and hot-swap

When FLOW_ID is set, the runner polls for config changes using ETags:

  1. Every POLL_INTERVAL seconds, checks for a new config version
  2. 304 Not Modified — no action
  3. New version detected — re-fetches secrets, downloads config, bundles locally, atomically swaps the running flow, updates cache, reports version via heartbeat

If the new bundle fails to load, the current flow stays active (safe swap). If secret refresh fails during a poll, the entire swap is skipped and the runner retries on the next poll cycle.

Caching and resilience

The runner writes the last working bundle to CACHE_DIR (/app/cache). This provides cold-start resilience:

  • API unreachable at startup — falls back to cached bundle
  • Bundling fails — uses cached bundle instead
  • Mount as a Docker volume to persist across restarts
Loading...

Health checks

The runner provides its own health server on the configured PORT, independent of flow sources. Health endpoints are always available, even during flow hot-swaps or when no flow handler is loaded.

EndpointDescription
GET /healthLiveness check — always returns 200
GET /readyReadiness check — returns 200 when a flow handler is mounted, 503 otherwise
Loading...
Loading...

All other requests are delegated to the flow's HTTP handler (e.g., Express source routes).

The Docker image includes a built-in HEALTHCHECK that polls /health every 30 seconds with a 30-second start period.

Docker Compose examples

Mode A: local config

Loading...

Mode C: remote config with cache

Loading...
Heartbeat details

When WALKEROS_TOKEN and PROJECT_ID are set, the runner sends periodic heartbeats:

  • Endpoint: POST /api/projects/:projectId/runners/heartbeat
  • Interval: Every HEARTBEAT_INTERVAL seconds (default 60)
  • Payload: instance ID, flow ID, config version, CLI version, uptime, mode, event counters (delta since last successful heartbeat)
  • Fire-and-forget: Failures are logged, never crash the runner. Counter deltas accumulate on failure and are included in the next successful heartbeat.

Runners appear in the project dashboard after their first heartbeat.

Building a custom image
Loading...

Pin a specific CLI version:

Loading...

File paths at runtime

When the runner loads a bundle, it sets the working directory (process.cwd()) to the bundle's directory. All file paths in your flow config settings resolve relative to the bundle, not your project root.

For example, if your bundle is at dist/bundle.mjs and a transformer reads a file:

{
"settings": {
"filePath": "./data.json"
}
}

At runtime, this resolves to dist/data.json — not ./data.json from the project root. Use the include field in your flow config to copy files alongside the bundle so they're accessible at runtime.

Troubleshooting

Runner won't start — Check the error message. Invalid env var combinations produce specific errors like FLOW_ID requires WALKEROS_TOKEN and PROJECT_ID.

Runner exits with "Secrets fetch failed (auth)" — The API token is invalid or expired. Generate a new token in the walkerOS dashboard. Auth failures (401/403) are fatal because config fetch would also fail.

"Warning: Could not fetch secrets" but runner continues — A non-auth error (404, 500) occurred. The runner continues without secrets. If your flow uses $env.* references, those values will be missing or fall back to their defaults.

API fetch fails at startup — The runner falls back to the cached bundle. If no cache exists, it exits. Mount CACHE_DIR as a volume for resilience.

Hot-swap not working — Verify FLOW_ID is set (polling only runs in mode C/D). Check logs for Config unchanged or Poll error. Confirm the flow was updated in the walkerOS UI.

Hot-swap skipped with "Failed to refresh secrets" — A transient secrets fetch error during polling skips the swap. The runner keeps the current flow and retries on the next poll. Check API connectivity and token validity.

Bundle fails after config fetch — The runner continues with the current flow and logs the error. The cached bundle is not overwritten.

Next steps

💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)