- CLI Reference
- Commands
- ironflow serve
ironflow serve
Start the Ironflow server with embedded NATS JetStream, database, and web dashboard.
ironflow serve [flags]Flags:
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--config | -f | string | Path to ironflow.yaml configuration file | |
--port | -p | int | 9123 | HTTP server port. Use 0 to let the OS assign a free port (pair with --port-file so a supervisor can discover it). |
--host | string | HTTP bind host. Empty (default) binds all interfaces; pass 127.0.0.1 for loopback-only (desktop/dev must not LAN-expose the engine). | ||
--port-file | string | Path to write the bound HTTP port as JSON {"http_port":N} (mode 0600). Pair with --port 0 so a supervising parent can discover the OS-assigned port. | ||
--db | -d | string | .ironflow/ironflow.db | SQLite database path |
--open | -o | bool | false | Open dashboard in browser after startup |
--nats-store-dir | string | (auto-derived from --db) | Embedded NATS JetStream storage directory. Default: {dirname(--db)}/ironflow-nats. Pass empty (--nats-store-dir="") for memory mode opt-out. With Postgres or :memory: SQLite, must be set explicitly or ironflow serve fails fast. (Or set NATS_STORE_DIR; explicit flag wins over env var.) | |
--nats-url | string | (none) | External NATS URL — nats://host:4222, tls://host:4222, or comma-separated seeds (or set NATS_URL). Requires PostgreSQL (IRONFLOW_DATABASE_URL). | |
--nats-creds | string | (none) | Path to NATS .creds file for JWT/NKey authentication (or set NATS_CREDS_FILE). | |
--nats-port | int | 4222 | Embedded NATS bind port. Use -1 for an ephemeral/random port so multiple ironflow serve instances don’t collide on the NATS socket. This only de-conflicts the NATS port — to run instances side-by-side also give each a distinct --db and --nats-store-dir (they otherwise share .ironflow/ironflow.db and .ironflow/ironflow-nats). CLI-only (no YAML equivalent). Ignored when --nats-url is set (external NATS). | |
--node-id | string | (random UUID) | Stable node identifier for cluster coordination (or set IRONFLOW_NODE_ID). | |
--dev | bool | false | Enable dev mode: bypasses all authentication (API + dashboard) | |
--pprof | bool | false | Enable pprof debug endpoints on a separate listener at :6060 | |
--bootstrap-key-file | string | (auto-derived) | Path to write the first-boot admin API key (mode 0400). Default: <db-dir>/.ironflow_bootstrap_key.json. Production meta-cluster systemd sets /run/ironflow/bootstrap-key.json. | |
--reset | bool | false | Delete local dev state (SQLite db, NATS store, blobs, JWT secret, bootstrap key) before starting, then boot fresh. Requires --dev; SQLite only (refused under PostgreSQL). The single-node SQLite tier is a production mode, so the --dev gate is what makes this dev-only. |
Env vars apply to the flags-only path only. The (or set ...) fallbacks above — NATS_URL, NATS_CREDS_FILE, NATS_STORE_DIR, IRONFLOW_NODE_ID, and IRONFLOW_DATABASE_URL — are read when you start serve with flags. With -f file.yaml the file is the source of truth and none of them are applied (IRONFLOW_DATABASE_URL only as the URL source under driver: postgres); IRONFLOW_STALE_CLAIM_THRESHOLD is dropped the same way. Set the YAML field, or reference the variable as ${VAR}. serve logs a warning for each one set but ignored, and ironflow validate -f lists them in its summary.
What it starts:
- Embedded NATS JetStream (messaging)
- Database — SQLite by default, PostgreSQL if
IRONFLOW_DATABASE_URLis set - Workflow engine (step execution, memoization, retry)
- PubSub bridge (real-time WebSocket event delivery)
- Consumer group manager (load-balanced event delivery)
- HTTP server with ConnectRPC API and web dashboard
Examples:
# Start with defaults (SQLite, port 9123)ironflow serve
# Custom port, open dashboardironflow serve --port 8080 --open
# PostgreSQL backendIRONFLOW_DATABASE_URL="postgres://user:pass@localhost:5432/ironflow" ironflow serve
# Persistent NATS storage (default since #614 — derived from --db path)# To override the auto-derived path:ironflow serve --nats-store-dir ./data/nats
# Memory-mode opt-out (tests, demos, ephemeral CI)ironflow serve --nats-store-dir=""
# Multi-node cluster (external NATS + PostgreSQL required)IRONFLOW_DATABASE_URL="postgres://user:pass@localhost:5432/ironflow" \ ironflow serve --nats-url nats://nats:4222 --node-id node-1
# Cluster with NATS authenticationIRONFLOW_DATABASE_URL="postgres://..." \ ironflow serve --nats-url nats://nats:4222 --nats-creds /etc/ironflow/node.creds
# Auth is always on — API key from bootstrap is printed on first boot
# Custom bootstrap key file (production meta-cluster)ironflow serve --bootstrap-key-file /run/ironflow/bootstrap-key.json
# Dev mode — skip auth for local developmentironflow serve --dev
# Fresh start — delete local dev state (db, NATS, blobs, secrets), then boot# Requires --dev; SQLite only. Refused under PostgreSQL and without --dev so it# can never wipe the single-node SQLite production ("Hobby") tier.# Note: with a custom --db, --reset also removes derived siblings in that dir# (ironflow-nats/, blobs/, .ironflow_jwt_secret, .ironflow_bootstrap_key.json).ironflow serve --dev --resetDev Mode
Section titled “Dev Mode”The --dev flag disables all authentication for local development:
ironflow serve --devIn dev mode:
- API requests are granted admin-level access without an API key
- Dashboard loads without requiring login
- All requests use
org_default,env_default, and theadminrole - A prominent warning is shown in the startup banner
- CORS is restricted to loopback origins. Because auth is bypassed, a browser
request is only answered cross-origin when its
Originislocalhost,127.0.0.1, or::1— so a local app onhttp://localhost:3000works, while a remote site the developer happens to visit cannot read the local engine. Outside dev mode the API staysAccess-Control-Allow-Origin: *(no credentials are ever allowed, so a caller still needs an API key)
Warning: Never use
--devin production. It is a CLI flag only (no env var) to ensure it is an intentional choice on every startup.