Skip to content
local-cf
Edit this page

CLI reference

local-cf [command] [options]

Run it from a directory containing a wrangler.jsonc, wrangler.json or wrangler.toml. local-cf searches that directory and then walks up through parents until it finds one, or use --config to point at a specific file.

Requires Node.js 20.11 or newer.

Commands#

local-cf attach (default)#

npx local-cf
npx local-cf attach

Browse the persisted state of a dev server you do not control. Mode B. Read-only unless you pass --allow-write.

This is the default command — running local-cf with no command runs attach. It never runs your worker, so it never needs to bundle it, which makes it the command most likely to just work on any project.

It opens a copy of your persist directory (in .local-cf/attached/), not the original. See Modes for why that matters.

local-cf dev#

npx local-cf dev

Run your worker and the studio in one runtime, read/write. Mode A. This is the full studio and the only mode where Durable Objects and Queues work.

Requires a main entrypoint in your wrangler config — without one there is no worker to run, and local-cf will tell you to use attach instead.

local-cf remote#

npx local-cf remote

Browse the real resources in your Cloudflare account over the REST API. Mode C. Requires credentials; see --account-id below.

Covers D1, KV and R2. Durable Objects and Queues have no REST API equivalent.

Common options#

These apply to dev, attach and remote alike.

OptionDefaultDescription
-p, --port <port>8787Port to listen on
--host <host>127.0.0.1Host to bind to
-e, --env <environment>Wrangler environment to use, e.g. [env.staging]
-c, --config <path>auto-discoveredPath to wrangler.toml / wrangler.jsonc
--persist-to <path>.wrangler/stateOverride the persist directory
--no-watchwatching onDo not rebuild the worker on file changes (dev only)
-q, --quietoffSuppress worker logs in the terminal. They still appear in the Logs tab
--openoffOpen the dashboard in your browser on startup
--no-backupbackup onDo not copy the persist directory before opening it read/write
--allow-writeoffAllow writes in attach mode — only when the other dev server is stopped
-V, --versionPrint the version
-h, --helpPrint help for a command

Notes on the less obvious ones#

--env merges a named environment over the top level the way wrangler does: bindings are replaced wholesale by the environment, not deep-merged. If the named environment does not exist in your config, local-cf fails rather than silently using the top level.

--persist-to is the escape hatch when local-cf's workerd is newer than your project's. Pointing it somewhere else keeps local-cf's state entirely separate from your project's, at the cost of no longer sharing data with your dev server. See runtime version mismatch.

--no-watch only affects dev, since attach and remote never build your worker. Watching is scoped to the directory containing your entrypoint, not the project root — rooting it higher makes every D1 write under .wrangler/state look like a source change and the worker rebuilds in a loop.

--allow-write does two things at once: it accepts writes, and it makes attach open your real .wrangler/state instead of a copy. Only pass it when the other dev server is actually stopped.

--no-backup disables the pre-flight copy taken before any read/write open. There is no good reason to use it except a persist directory too large to copy comfortably — and above 2 GB local-cf skips the backup on its own anyway.

Remote-only options#

OptionEnvironment variableDescription
--account-id <id>CLOUDFLARE_ACCOUNT_IDCloudflare account ID
--api-token <token>CLOUDFLARE_API_TOKENCloudflare API token

The token needs D1 Edit, Workers KV Storage Edit and Workers R2 Storage Edit permissions. Flags take precedence over environment variables. The token stays in the Node process and is never sent to the browser.

Environment variables#

VariableUsed byEffect
CLOUDFLARE_ACCOUNT_IDremoteAccount ID, if not passed as a flag
CLOUDFLARE_API_TOKENremoteAPI token, if not passed as a flag
LOCAL_CF_BUNDLERdevSet to esbuild to skip your project's wrangler and always use the built-in bundler
LOCAL_CF_UI_DIRallPath to a built dashboard export, overriding the bundled one. Mainly for developing the dashboard against a live sidecar

Examples#

# Look at what your running dev server has written — safest first run
npx local-cf --port 8788

# Full studio, opens the browser for you
npx local-cf dev --open

# Full studio against your staging environment on a custom port
npx local-cf dev --env staging --port 8080

# Point at a project in another directory
npx local-cf --config ../api/wrangler.toml

# Edit the state your (stopped) dev server left behind
npx local-cf --allow-write

# Keep local-cf's state entirely separate from your project's
npx local-cf dev --persist-to .local-cf/state

# Quiet mode — worker logs go to the studio, not your terminal
npx local-cf dev --quiet

# Your real Cloudflare account
npx local-cf remote --account-id abc123 --api-token xyz789

URLs it serves#

PathWhat
/__local-cf/ui/The dashboard
/__local-cf/api/…The studio API
everything elseForwarded to your worker (dev mode only)

In attach and remote mode there is no worker to forward to, so anything outside /__local-cf returns a 404 with a JSON body explaining which mode you are in and where the studio lives.

Files it writes#

All inside your project, all safe to delete, all worth putting in .gitignore:

PathWhatRetention
.local-cf/attached/{0,1,2}/Point-in-time copies opened by attach mode2 kept, rotating
.local-cf/backups/<timestamp>/Pre-flight backups taken before a read/write open3 kept
.local-cf/snapshots/<name>/Snapshots you took from the studioKept until you delete them
.local-cf/audit.jsonlOne JSON line per dashboard writeAppend-only
.local-cf/

Shutdown behaviour#

local-cf handles SIGINT, SIGTERM, SIGBREAK (Ctrl+Break on Windows) and SIGHUP (your terminal closing). All four run the same clean shutdown, which matters more than it sounds: killing the process outright leaves the persist directory's SQLite files open mid-write.

Shutdown disposes the runtime — which checkpoints the write-ahead log — and then sweeps the -shm files it created, so the next process to open those databases (your own wrangler dev, possibly on a different workerd) builds its own. The -wal files are deliberately left alone: those are committed data.

If dispose hangs, it is abandoned after 5 seconds rather than leaving the process alive holding your files open.