Local development
Three patterns for running Flux on a laptop — bare pip install, docker-compose for the full stack, and devcontainer / nix recipes.
Three patterns cover the laptop case. Pick the lightest one that fits the workflow you’re prototyping; switch up only when you hit its ceiling.
1. Bare pip install
The simplest path. Two terminals, no containers, SQLite on disk.
pip install flux-core
flux start server # terminal 1
flux start worker my-worker # terminal 2
Workers need a bootstrap token. The server auto-generates one on first start and writes it to <FLUX_HOME>/bootstrap-token (default ~/.flux/bootstrap-token, mode 0600). Either let the CLI read it back:
export FLUX_WORKERS__BOOTSTRAP_TOKEN="$(flux server bootstrap-token)"
flux start worker my-worker
…or print and copy:
flux server bootstrap-token
# 8a3f... (64 hex chars)
The worker crashes at startup if FLUX_WORKERS__BOOTSTRAP_TOKEN is missing or whitespace-only. There is no fallback.
Storage
Persistent state lives in .flux/flux.db (SQLite) under whatever directory you ran the server from, unless you’ve set FLUX_DATABASE_URL to a Postgres URL. Workflow definitions, execution history, schedules, and secrets all live in the database; the auto-generated bootstrap token sits next to it as .flux/bootstrap-token. The schema is Alembic-managed and migrates automatically whenever the server opens the database, so upgrading flux-core in place is safe — flux db current shows the revision if you’re curious.
SQLite is a single-node backend: one server plus one colocated worker is the supported shape (the server logs a warning if a second worker registers). That’s exactly the laptop topology — but it means the multi-worker and multi-replica behaviors need the docker-compose pattern with Postgres to reproduce locally.
Resetting state
Stop both processes and delete the directory:
rm -rf .flux/
Next flux start server regenerates schema, regenerates the bootstrap token, and starts fresh. Re-export FLUX_WORKERS__BOOTSTRAP_TOKEN for the worker — the previous value is gone.
For the actual walkthrough — defining a workflow, registering it, running it — see Quickstart.
2. docker-compose for the full stack
When you want the production shape on a laptop: server + workers + Postgres, networked, restartable. Useful for testing schema migrations, exercising the Postgres path before deploying, or running a quick load test against a representative topology.
The compose file lives on Docker; copy it and run docker compose up. From the developer’s seat, the difference vs the bare install is:
flux workflow registerruns from outside the container (pass--server-url http://localhost:8000).- Server logs come from
docker compose logs server. - Persistent state is in a named volume and survives
docker compose down. Rundocker compose down -vto clear it. - Two worker replicas vs one — useful for catching label-routing assumptions before they reach production.
The trade-off is rebuild latency. Edit a workflow, register it, and the running server picks it up over the catalog — no rebuild needed. Edit the server’s own code (rare for application developers) and you’re rebuilding the image. Most teams reserve compose for end-to-end testing and stay on the bare install for the inner loop.
3. Devcontainer / nix develop
For teams that want every developer to land in an identical environment, with no “works on my machine” debugging.
Flux 0.56.0 does not ship an official devcontainer or nix flake. The shape you’d author:
- Devcontainer: a
.devcontainer/devcontainer.jsonpinning Python 3.14, installingflux-core, and forwarding port 8000. ApostCreateCommandexportsFLUX_WORKERS__BOOTSTRAP_TOKENfrom the auto-generated file. VS Code or any devcontainer-aware editor picks it up. - Nix flake: a
flake.nixwith adevShellwhosebuildInputsincludepython314and apip install flux-coreshellHook. Same shape with reproducible Python — useful for CI parity.
Both are community-maintained where they exist. If you build one worth sharing, contribute it to flux-recipes.
The honest summary on this tier: it pays off when you have more than a handful of developers and “set up Flux” is taking real onboarding time. For a solo developer or a small team, the bare pip install is faster to live with.
Picking a pattern
Roughly:
- Inner loop on workflows you’re authoring: bare pip install. Fast restart, instant feedback.
- Pre-flight check before deploying: docker-compose. Same shape as prod, catches things the bare install hides (Postgres-only features, network boundaries between server and workers).
- Onboarding a team: devcontainer or nix flake, once one of those is available for your team’s tooling preference.
Switching between them is fine — the workflows you author work identically across all three.
What can go wrong
Worker crashes immediately
Symptom. flux start worker my-worker exits with RuntimeError: Worker bootstrap token is not configured.
Cause. FLUX_WORKERS__BOOTSTRAP_TOKEN is unset, or set to whitespace. Workers never auto-generate.
Fix. Export the token before launching the worker:
export FLUX_WORKERS__BOOTSTRAP_TOKEN="$(flux server bootstrap-token)"
flux start worker my-worker
If you’ve rotated the server or deleted .flux/, re-run the export — the old value is invalid.
Permission errors on .flux/flux.db
Symptom. Server crashes with sqlite3.OperationalError: unable to open database file, or workers can’t read state set by a different account.
Cause. You started the server as one user (or via sudo) and the worker as another. SQLite is owned by whoever wrote it first.
Fix. Run both processes as the same user. If you need to recover, chmod -R u+rw .flux/ and chown to your user.
Port 8000 already in use
Symptom. Server exits with OSError: [Errno 98] Address already in use.
Cause. Another process — a previous Flux server you forgot to stop, or an unrelated service — is bound to 8000.
Fix. Either stop the other process (lsof -i :8000, then kill) or pick a free port:
flux start server --port 8001
flux start worker my-worker --server-url http://localhost:8001
A worker started with a plain flux start worker reads its server URL from FLUX_WORKERS__SERVER_URL (or the --server-url flag) — there is no bare FLUX_SERVER_URL config field. The CLI subcommands derive their target from FLUX_SERVER_HOST / FLUX_SERVER_PORT, or accept --server-url per command.
Next
- Docker for the full-stack compose file.
- Quickstart for the runnable walkthrough.
- Production checklist when the laptop loop graduates to a real environment.