> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Deploy

You have a working flow. Every path below runs on infrastructure you control; managed hosting is the optional alternative at the end.

## From config to running flow[​](#from-config-to-running-flow "Direct link to From config to running flow")

Every self-hosted path is the same two steps, with checks before them:

1. **Validate** the config: `walkeros validate flow.json`
2. **Simulate** an event through it locally, destinations mocked, see [bundled mode](/preview/pr-749/docs/getting-started/modes/bundled.md#see-your-event)
3. **Bundle** it into an artifact: `walkeros bundle flow.json -o dist/`
4. **Run** the artifact: `runneros start dist/flow.mjs`

Building and running are two tools. `walkeros` (the CLI) validates, simulates and bundles. `runneros` (the runtime) only starts a prebuilt artifact: it has no bundler, so a production host never needs a package manager or registry access. See the [runtime](/preview/pr-749/docs/apps/runtime.md) for why the stages are kept apart.

## Build once[​](#build-once "Direct link to Build once")

```bash
npx @walkeros/cli bundle flow.json -o dist/
```

For a server flow this writes `dist/flow.mjs`, `dist/package.json`, and `dist/node_modules/`. The step packages load from `node_modules/`, so ship the whole directory, not `flow.mjs` alone. A web flow builds to a single `dist/walker.js` that any static host can serve.

## Run it yourself[​](#run-it-yourself "Direct link to Run it yourself")

**Docker**, the standard path. Mount the bundle directory at `/app/flow`, where the image expects `flow.mjs`:

```bash
docker run -d -p 8080:8080 \
  -v "$PWD/dist:/app/flow:ro" \
  walkeros/flow:<version>
```

Use a `walkeros/flow` version that ships `runneros`, ideally the same version as the CLI that built the artifact, see [image tags](/preview/pr-749/docs/apps/runtime.md#image-tags). For a self-contained image, use the [canonical Dockerfile](/preview/pr-749/docs/apps/runtime.md#canonical-dockerfile).

**Node, from the bundle**. `flow.mjs` exports a start function and does not run on its own with `node`; `runneros` starts it, without Docker:

```bash
npx --package=@walkeros/runner runneros start dist/flow.mjs
```

Integrated mode needs none of this: the flow deploys with your application and needs nothing extra. See [integrated mode](/preview/pr-749/docs/getting-started/modes/integrated.md).

The same `flow.json` drives every path, so switching between them (or between providers) is a rebuild and a redeploy, not a migration. To change a running flow, bundle again and redeploy the new artifact.

## Before production[​](#before-production "Direct link to Before production")

* Test offline: run the event through the built flow with each destination mocked, see [bundled mode](/preview/pr-749/docs/getting-started/modes/bundled.md#see-your-event).
* Gate CI: `walkeros validate flow.json --strict` turns contract violations into errors, see [validate](/preview/pr-749/docs/getting-started/flow/validate.md).
* Wire consent before real traffic, see the [consent guide](/preview/pr-749/docs/guides/consent.md).
* Keep `flow.json` in git: deploys stay reviewable and reversible.

## Managed hosting[​](#managed-hosting "Direct link to Managed hosting")

## Next steps[​](#next-steps "Direct link to Next steps")

* [Runtime](/preview/pr-749/docs/apps/runtime.md): the image, inputs, environment variables, constraints
* [Docker quickstart](/preview/pr-749/docs/getting-started/quickstart/docker.md): a server flow running in five minutes
* [CLI reference](/preview/pr-749/docs/apps/cli.md): all commands and options
