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#
| Flag | Description | Default |
|---|---|---|
--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:
| Key | Action |
|---|---|
c | Manually recompile all workers / regenerate entry |
m | Generate and apply database migrations |
p | Ping all workers and print their health status |
r | Restart the database connection |
q | Graceful 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.