---
title: Local World
description: Zero-config world bundled with Workflow for local development. No external services required.
docs_index: /llms.txt
lastUpdated: 2026-10-13
type: integration
summary: Set up the Local World for zero-configuration workflow development on your machine.
prerequisites:
  - /docs/deploying
related:
  - /worlds/postgres
  - /worlds/vercel
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

The Local World is bundled with `workflow` and used automatically during local development. It requires no installation or configuration.

To explicitly use the Local World in any environment, set this environment variable:

```bash
WORKFLOW_TARGET_WORLD=local
```

## Observability

The Workflow CLI uses the Local World by default. Run these commands inside your workflow project to view your local development workflows:

```bash
# List recent workflow runs
npx workflow inspect runs

# Launch the web UI
npx workflow web
```

Learn more in the [Observability](/docs/observability) documentation.

## Testing & compatibility

## Configuration

The Local World requires no configuration, but you can customize its behavior through environment variables or programmatically through `createWorld()`.

### `WORKFLOW_LOCAL_DATA_DIR`

Directory for storing workflow data as JSON files. Default: `.workflow-data/`

### `PORT`

The application dev server port. Used to deliver workflow queue messages to the combined flow route. Default: auto-detected

### `WORKFLOW_LOCAL_BASE_URL`

Full base URL override for HTTPS or custom hostnames. Default: `http://localhost:{port}`

Port resolution priority: `baseUrl` > `port` > `PORT` > auto-detected

### `WORKFLOW_LOCAL_QUEUE_CONCURRENCY`

Maximum number of concurrent queue message handlers. Default: `1000`

### `WORKFLOW_LOCAL_QUEUE_MAX_VISIBILITY`

Maximum number of seconds a local queue message can stay hidden before the handler rechecks the run. Default: unlimited.

### `WORKFLOW_LOCAL_RUN_STATUS_POLL_INTERVAL_MS`

How often a wait for a terminal run status re-reads the run file, in milliseconds. Default: `100`.

`await run.returnValue` asks the World to wait for the run to finish. When the run and the caller share a process (the usual development server case), an in-process signal resolves the wait as soon as the run ends, and this interval never comes into play. The interval covers activity the signal cannot detect, primarily a second process awaiting a run over the same data directory, so it is set far below Workflow's own polling interval.

### `WORKFLOW_LOCAL_HEADERS_TIMEOUT_MS`

Maximum time in milliseconds to wait for a local queue handler to begin responding before redelivering the durable message. Default: `0` (no deadline).

A delivery executes inline steps before the handler responds, so a deadline shorter than your longest step redelivers a healthy delivery while it is still running and executes the same step again. Set this only if you would rather a hung handler be redelivered, and set it above the longest inline work you expect.

### `WORKFLOW_LOCAL_BODY_TIMEOUT_MS`

Maximum gap in milliseconds between response body chunks from a local queue handler before redelivering the durable message. Default: `0` (no deadline). The same caution as `WORKFLOW_LOCAL_HEADERS_TIMEOUT_MS` applies.

### `WORKFLOW_NODE_HTTP`

Whether queue deliveries go out through Node's built-in `node:http` and `node:https` modules instead of the HTTP client library this World normally uses. Default: disabled. Set to `1` to switch to Node's modules. Socket pooling, keep-alive, and both timeouts above apply either way. See [`WORKFLOW_NODE_HTTP`](/docs/configuration/runtime-tuning#workflow_node_http) for what else changes.

### `WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS`

Whether pending and running runs found in the data directory are re-enqueued when the World starts. Set to `0` or `false` to skip recovery and leave stale runs untouched. Default: `true`.

### `WORKFLOW_LOCAL_HOOK_RETENTION_LIMIT_DAYS`

Maximum [`experimental_minRetention`](/docs/api-reference/workflow/create-hook#keep-a-token-unavailable-after-the-run-ends) accepted by the Local World, in days. Default: `30`. Set this to the same limit as your production World so oversized values fail during local development.

### `WORKFLOW_STREAM_FLUSH_INTERVAL_MS`

Group-commit window, in milliseconds, for the leading chunk of an idle stream. Default: `0` (dispatch immediately). A positive value holds the first chunk up to that long to collect a group, trading first-chunk latency for fewer requests. Chunks arriving while a request is in flight always coalesce into the next group regardless.

### Programmatic configuration

Options passed to `createWorld()` take precedence over the environment variables above. Export the configured World from a module and point `WORKFLOW_TARGET_WORLD` at that module:

```typescript title="my-world.ts" lineNumbers
import { createWorld } from "@workflow/world-local";

export default createWorld({
  dataDir: "./custom-workflow-data",
  port: 5173,
  // baseUrl overrides port if set
  baseUrl: "https://local.example.com:3000",
  recoverActiveRuns: true, // overrides WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS
  streamFlushIntervalMs: 10, // WORKFLOW_STREAM_FLUSH_INTERVAL_MS, if set, overrides this
});
```

```bash title=".env"
WORKFLOW_TARGET_WORLD="./my-world.ts"
```

`createWorld()` also accepts `tag`, which scopes local storage files to a suffix. It is mainly used by test harnesses that share one `.workflow-data` directory.

### Queue consumers other than the Workflow SDK

The Local World delivers queue messages over HTTP to the app, and by default every message goes to the Workflow SDK's flow route, `/.well-known/workflow/v1/flow`. A library that consumes messages of its own on the same World, such as a durable agent harness, uses its own queue prefix (`__{namespace}_wkf_workflow_`) and tells the World where those go:

```typescript title="lib/agent-queue.ts" lineNumbers
import { getWorld } from "workflow/runtime";

const PREFIX = "__agent_wkf_workflow_";

export async function registerAgentQueue(
  handler: (req: Request) => Promise<Response>
) {
  const world = await getWorld();
  // Over HTTP: deliveries for this prefix go to the app's own route.
  world.registerRoute?.(PREFIX, "/api/agent/queue");
  // In this process: deliveries skip HTTP entirely.
  world.registerHandler?.(PREFIX, handler);
}
```

`registerHandler()` takes precedence over `registerRoute()` for the same prefix. A prefix with neither still goes to the flow route, which answers `400` for a prefix it does not handle, and the Local World retries the message every five seconds, so a route registered later takes the next attempt. When the World restarts, `start()` re-enqueues each active run to the queue namespace it recorded at creation (`executionContext.queueNamespace`), so register the route or handler before calling `start()`.

## Data directory layout

Each run's event and step files are stored in their own directory (`events/<runId>/`, `steps/<runId>/`). Data written by `@workflow/world-local` releases before 5.1.0 uses a flat layout and is not migrated: when the World starts on a data directory that holds only flat-layout files, it logs a warning, deletes the directory, and starts with an empty one. The CLI and web UI only read the data directory and never delete it. Older releases see no events or steps in the new layout, so clear `.workflow-data` by hand before downgrading.

A directory that mixes both layouts, or holds `.json` files in `events/` or `steps/` that the World did not write, is never deleted: starting fails with a `DataDirLayoutError` that lists the files.

## Limitations

The Local World is designed for development, not production:

- **In-memory queue**: Workflow messages, including queued step invocations, do not persist across server restarts.
- **Filesystem storage**: Data is stored in local JSON files.
- **Single instance**: The Local World cannot handle distributed deployments.
- **No authentication**: The Local World is suitable only for local development.

For production deployments, use the [Vercel World](/worlds/vercel), which handles execution, persistence, multi-tenancy, scale, observability, and security for you on Vercel, or check out the [Postgres World](/worlds/postgres) - a tested reference implementation that implements the complete World spec and can be used to deploy workflows anywhere.

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)