LogoZonai
zonai.dev

zonai serve

Start the HTTP server with optional hot-reload and file watching.

Start the Zonai HTTP server.

zonai serve [flags]

Run it from the project root (where zonai.yaml lives). With the installed zonai binary, operations and rules run in their compiled worker executables alongside the other workers. Only a project that itself depends on package:zonai gets a generated project entry (JIT .dart_tool/zonai/project_main.dart, or AOT .zonai/zonai with --release) with operations and rules running in-process. Both behave the same. In production, run ./zonai serve --release from a build/ bundle.

Flags#

FlagDescriptionDefault
--host <address> Bind address (see Server Binding) zonai.yaml value, then localhost
--port <number> HTTP port zonai.yaml value, then 8080
--flavor <name> Config flavor — picks the config file and .env.<name> (none)
--release Production mode — no watchers, no recompiling, no shortcuts false
--no-auto-migrate Dev only: don't generate migrations from schema changes false
--allow-stale-workers With --release: serve workers older than their sources instead of refusing to start false
-c, --config <path> Path to a custom zonai.yaml Auto-detected

Pending migration SQL is always applied when the database opens, with or without --no-auto-migrate and --release. The flag only stops dev mode from watching schemasPath and generating new migrations.

Dev Mode#

In dev mode (the default), Zonai watches worker source directories and recompiles affected worker binaries. At startup it also compiles any worker whose sources changed since its last compile, so an edit made while the server was down is not served from the old build. When ops/rules are linked into the project entry (see above), restart serve after editing them so the new code loads.

Keyboard shortcuts in dev mode:

KeyAction
cManually recompile all workers / regenerate entry
mGenerate and apply database migrations
pPing all workers and print their health status
rRestart the database connection
qGraceful shutdown

Release Mode#

With --release, Zonai does not watch sources, recompile, generate migrations, or read keyboard shortcuts — it serves the binaries that already exist. Build them first with zonai build --release (typical: cd build && ./zonai serve --release) or zonai compile --release. The full list of differences is under Release mode.

Because nothing is recompiled, serve --release refuses to start when a worker's executable is older than its sources, naming the workers. Otherwise it would serve the old build, and enforce the old rules, with nothing to say so. Run zonai compile, then serve again, or pass --allow-stale-workers to serve them as they are.

What counts as a worker's sources: its own directory (rulesPath, configPath, and so on, plus schemasPath for operations), the .env file in use, and pubspec.lock. Like the dev watchers, it does not follow imports, so a helper elsewhere in lib/ that only one worker imports is not tracked. A build/ bundle carries no sources, so it is never refused.

Examples#

# Dev mode with defaults (JIT project entry)
zonai serve

# Dev mode on a custom port
zonai serve --flavor dev --port 9000

# IPv4 loopback only (e.g. behind a reverse proxy)
zonai serve --host 127.0.0.1

# Production mode from a build/ bundle
cd build && ./zonai serve --release

The default host: localhost binds 127.0.0.1: reachable from this machine (and the Android emulator via 10.0.2.2) but not from the network. Pass --host 0.0.0.0 to expose it. See Server Binding.

Set ZONAI_FORCE_WORKERS=1 to run ops/rules via workers even when the project entry is linked.