Configuration
This page documents all environment variables and default settings for the Ironflow server and its SDKs.
Environment Variables
Section titled “Environment Variables”| Variable | Description | Default |
|---|---|---|
IRONFLOW_SERVER_URL |
The URL SDKs and CLI use to connect. The Go agent SDK also honors IRONFLOW_URL as a fallback alias (checked first). |
http://localhost:9123 |
IRONFLOW_API_KEY |
The key used for authentication. | (None) |
IRONFLOW_DATABASE_URL |
Postgres connection string (triggers PG mode). | (None, uses SQLite) |
IRONFLOW_BLOB_URL |
Blob overflow backend for large run/step outputs. Empty uses local filesystem next to the SQLite DB; s3://bucket?endpoint=...®ion=... selects an S3-compatible backend. Multi-node deployments require an s3:// value. See Artifact overflow to blob storage. |
(None, local filesystem) |
IRONFLOW_ARTIFACT_THRESHOLD |
Size above which run/step outputs offload to the blob backend (1MB, 512KiB, etc.). Empty or 0 disables new offloads; reads still inflate existing blobs. |
(None, off) |
AWS_ACCESS_KEY_ID |
Access key used by the S3-compatible blob backend when IRONFLOW_BLOB_URL is set. |
(None) |
AWS_SECRET_ACCESS_KEY |
Secret key used by the S3-compatible blob backend when IRONFLOW_BLOB_URL is set. |
(None) |
AWS_SESSION_TOKEN |
Optional session token for temporary S3 credentials. | (None) |
IRONFLOW_MASTER_KEY |
32-byte hex key for secrets encryption. | (None, Dev Mode) |
IRONFLOW_JWT_SECRET |
32-byte hex key for dashboard JWT signing. Auto-generated and persisted to .ironflow_jwt_secret if not set. |
(Auto-generated) |
IRONFLOW_SIGNING_KEY |
Key for verifying push-mode webhooks. | (None) |
IRONFLOW_ENV |
Default environment for CLI and SDK operations. | default |
IRONFLOW_CONFIG_DIR |
Directory for storing platform credentials (credentials.json). Used by ironflow platform login. |
~/.config/ironflow |
NATS_STORE_DIR |
Disk path for embedded NATS JetStream storage. | (Auto-derived from --db path: {db_dir}/ironflow-nats. See Embedded NATS storage below.) |
LOG_LEVEL |
trace, debug, info, warn, error. Overrides per-command defaults. |
warn (CLI), info (serve) |
LOG_FORMAT |
text for human-readable output. Only affects serve command (JSON by default). CLI commands always use text. |
text (CLI), JSON (serve) |
S3 Usage at a Glance
Section titled “S3 Usage at a Glance”Ironflow uses S3-compatible storage in a few different places. These settings are intentionally separate because they protect different data.
| Surface | What goes to S3 | Dev / single-node default | Production setting | Configure with |
|---|---|---|---|---|
| Runtime blob overflow | Large run and step outputs after they exceed the configured threshold. Reads inflate the payload transparently. | Local filesystem next to the SQLite DB; offload is disabled until a threshold is set. | Multi-node deployments require a shared s3:// backend, even when new offloads are disabled. |
IRONFLOW_ARTIFACT_THRESHOLD, IRONFLOW_BLOB_URL, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, optional AWS_SESSION_TOKEN; YAML: spec.blobs.artifactThreshold, spec.blobs.url. See Artifact overflow to blob storage. |
| Kubernetes PostgreSQL backups | CNPG/Barman WAL archives and database backups. | Disabled unless the Helm object store settings are enabled. | Enable postgresql.objectStore and point it at an S3-compatible bucket. |
Helm values: postgresql.objectStore.enabled, destinationPath, endpointURL, retentionPolicy, s3Credentials. See Kubernetes deployment. |
| Cloud operator break-glass and snapshots | Encrypted break-glass payloads and deprovision snapshot exports for managed-cloud workflows. | Not used by normal ironflow serve; only used by ironflow-cloud workflows. |
Set the cloud operator S3 endpoint, bucket, credentials, region, and TLS flag. | IRONFLOW_CLOUD_BREAK_GLASS_ENDPOINT, IRONFLOW_CLOUD_BREAK_GLASS_REGION, IRONFLOW_CLOUD_BREAK_GLASS_BUCKET, IRONFLOW_CLOUD_BREAK_GLASS_ACCESS_KEY, IRONFLOW_CLOUD_BREAK_GLASS_SECRET_KEY, IRONFLOW_CLOUD_BREAK_GLASS_SECURE. |
The broader object/file storage API described in ADR-0035 is separate design
work and is not configured by IRONFLOW_STORAGE_* variables in the shipped
server path.
Embedded NATS storage
Section titled “Embedded NATS storage”ironflow serve runs an embedded NATS JetStream server when NATS_URL is not
set. JetStream needs a storage directory for streams and consumer state.
Default behavior (since #614): the store directory is auto-derived from the
SQLite database path. For the default .ironflow/ironflow.db it becomes .ironflow/ironflow-nats/;
for /data/ironflow.db it becomes /data/ironflow-nats/. The directory is
created at startup with 0700 permissions (owner read/write/exec only) because
JetStream stream files contain workflow event payloads that may include PII or
secrets. Existing directories are left as-is so operators can pre-create with
their preferred ownership and permissions.
Memory-mode opt-out: pass an explicit empty value for testing, demos, or ephemeral CI:
ironflow serve --nats-store-dir=""The explicit empty CLI flag wins over a NATS_STORE_DIR environment variable
inherited from the shell or systemd unit.
YAML-only opt-out is not currently supported. YAML round-trip cannot
distinguish nats.storeDir: "" from an omitted field, so a YAML config alone
cannot request memory mode. YAML operators who want ephemeral embedded NATS
have two options today: (1) point at an external NATS server via nats.url,
or (2) override at startup with --nats-store-dir="" on the command line. A
discoverable :memory: sentinel for --nats-store-dir and NATS_STORE_DIR
is tracked in TODOS.md as a follow-up.
Postgres mode: when IRONFLOW_DATABASE_URL is set and NATS_URL is NOT set
(embedded NATS), ironflow serve and ironflow validate refuse to start
without an explicit --nats-store-dir or NATS_STORE_DIR. Auto-deriving from
the SQLite path is not safe — there is no SQLite path in Postgres mode, and
silently creating a directory in the current working directory is dangerous
under systemd units, container WorkingDirectory: /, and read-only cwd. Set
the directory explicitly when you want embedded NATS with Postgres, or use
--nats-url for an external NATS server.
:memory: SQLite: same fail-fast as Postgres mode. --db :memory: plus
embedded NATS without an explicit store dir is rejected at startup.
Security note — secrets KV plaintext on disk. The secrets KV bucket is
backed by JetStream file storage. When IRONFLOW_MASTER_KEY is unset, secret
values are stored without encryption (passthrough). Before #614 this was
masked by memory-mode embedded NATS; with persistent NATS as the default,
secrets without a master key persist plaintext to {store_dir}/secrets/....
The server emits a startup WARN when this configuration is detected and a
stronger WARN with the count of plaintext secrets when any exist. Set
IRONFLOW_MASTER_KEY to a 32-byte hex value (e.g. openssl rand -hex 32) for
AES-256-GCM encryption, then re-set existing secrets via ironflow secret set
so they get re-encrypted.
Storage profile. File-storage streams retain about 1.3 GB total
(internal/nats/streams.go), versus about 75 MB total in memory mode.
Operators on size-constrained disks can override per-stream limits via YAML
or roll back to memory mode with --nats-store-dir="".
Operator discipline. The embedded NATS server does not lock its store
directory, so two ironflow serve processes against the same DB directory
will silently corrupt JetStream state. Run a single process per directory.
Observability
Section titled “Observability”| Variable | Description | Default |
|---|---|---|
IRONFLOW_OTEL_ENDPOINT |
OTLP gRPC endpoint for OpenTelemetry tracing. | (None, disabled) |
IRONFLOW_OTEL_SAMPLE_RATE |
Trace sampling rate (0.0 to 1.0). | 1.0 |
IRONFLOW_OTEL_SERVICE_NAME |
OTel service name. | ironflow |
IRONFLOW_OTEL_INSECURE |
Use plaintext gRPC for OTLP export. | true |
IRONFLOW_METRICS_ENABLED |
Enable Prometheus metrics at /metrics. |
false |
Profiling
Section titled “Profiling”The --pprof CLI flag on ironflow serve starts a separate listener on :6060 with Go’s net/http/pprof handlers for CPU, heap, and goroutine profiling. Not configured via environment variable — CLI flag only. See Benchmarks for the load testing workflow.
Clustering
Section titled “Clustering”| Variable | Description | Default |
|---|---|---|
NATS_URL |
External NATS server URL. When set, connects to external NATS instead of embedded. Requires PostgreSQL. | (None, embedded) |
NATS_CREDS_FILE |
NATS credentials file for NKey/JWT auth. | (None) |
IRONFLOW_NODE_ID |
Unique node identifier for distributed coordination. Must be stable per node. | Random UUID v4 |
IRONFLOW_STALE_CLAIM_THRESHOLD |
Duration before stale scheduler claims are reclaimed. | 2m |
IRONFLOW_STALE_CLAIM_RECOVERY_INTERVAL |
How often the scheduler scans for orphaned claims. Lower values detect crashes faster at the cost of more frequent SQL sweeps. The --dev flag drops it to 5s. |
60s |
NATS_FILE_STORAGE |
Set to true to use file-backed JetStream streams instead of memory. |
false |
NATS_STREAM_REPLICAS |
JetStream stream replica count for clustered NATS. | 1 |
Security
Section titled “Security”| Variable | Description | Default |
|---|---|---|
IRONFLOW_WS_ALLOWED_ORIGINS |
Comma-separated allowed origins for WebSocket connections. When empty, all origins are allowed. | (None, all origins) |
IRONFLOW_AGENT_TOOLS_ALLOW_PRIVATE |
When true, agent tool callback URLs may target RFC1918/loopback addresses. The --dev flag forces this on regardless. |
false |
IRONFLOW_AGENT_TOOLS_URL_ALLOWLIST |
Comma-separated host allowlist for agent tool callback URLs. When set, only listed hosts are accepted. | (None, no allowlist) |
IRONFLOW_REQUIRE_ENCRYPTION |
When true/1/yes, the engine verifies the SQLite data path lives on a dm-crypt block device and fails fast at startup otherwise. Postgres backends skip the check. Belt-and-suspenders against misconfigured systemd BindsTo units. |
false (off) |
Engine
Section titled “Engine”| Variable | Description | Default |
|---|---|---|
IRONFLOW_CANCEL_ON_REPLAY_WINDOW_SECONDS |
Lookback window (seconds) for the cancel-on-replay buffer. Recent events newer than this are kept for cancellation matching. | 30 |
IRONFLOW_CANCEL_ON_REPLAY_BUFFER_SIZE |
Maximum entries retained in the cancel-on-replay buffer. Bounds memory under high event rates. | 10000 |
IRONFLOW_SQLITE_MAX_BYTES |
Caps total SQLite footprint (main + WAL + SHM). Writes that would exceed it return 507 Insufficient Storage. SQLite backends only. |
10737418240 (10 GiB) |
IRONFLOW_WRITE_QPS_MAX |
Caps REST write requests per second per node. Excess writes are throttled. Single-node SQLite sustained-write budget. | 500 |
Cloud Operator
Section titled “Cloud Operator”| Variable | Description | Default |
|---|---|---|
IRONFLOW_CAP_TOKEN_PRIVATE_KEY_B64 |
Base64-encoded PKCS8 DER Ed25519 private key for minting deprovision capability tokens (ironflow cloud cap-token mint). |
(None, required for mint) |
IRONFLOW_MASTER_KEY_B64 |
Base64-encoded 32-byte meta-cluster KEK for decrypting break-glass bearer blobs and snapshot exports (ironflow cloud break-glass, ironflow cloud snapshot decrypt). |
(None, required for decrypt) |
Auth Audit Batcher
Section titled “Auth Audit Batcher”| Variable | Description | Default |
|---|---|---|
IRONFLOW_AUDIT_BATCH_SIZE |
Maximum rows per batch insert into the policy_decisions audit table. |
100 |
IRONFLOW_AUDIT_BATCH_FLUSH_MS |
Milliseconds between batch flushes. Set lower for tighter audit latency; set higher for write throughput. | 1000 |
Projection Rebuild
Section titled “Projection Rebuild”| Variable | Description | Default |
|---|---|---|
IRONFLOW_REBUILD_BATCH_SIZE_SQLITE |
Events per batch when rebuilding a projection against a SQLite-backed event store. | 500 |
IRONFLOW_REBUILD_BATCH_SIZE_POSTGRES |
Events per batch when rebuilding a projection against a PostgreSQL-backed event store. | 1000 |
IRONFLOW_REBUILD_BATCH_PAUSE_MS |
Milliseconds to pause between FULL rebuild batches (PG only). Useful for throttling large rebuilds. | 0 |
Bootstrap Defaults
Section titled “Bootstrap Defaults”On the very first boot of a fresh database, Ironflow creates a default admin user with the email admin@ironflow.local and a randomly generated password. Both the admin API key and password are printed to the console and must be saved immediately – they will not be shown again.
SDK Configuration
Section titled “SDK Configuration”TypeScript (Browser)
Section titled “TypeScript (Browser)”import { ironflow } from "@ironflow/browser";
ironflow.configure({ serverUrl: "http://localhost:9123", auth: { apiKey: "ifkey_..." }, environment: "default",});Additional options: transport (“connectrpc” | “websocket”), reconnect, visibility, logger, timeout.
TypeScript (Node.js)
Section titled “TypeScript (Node.js)”import { createClient } from "@ironflow/node";
const client = createClient({ serverUrl: process.env.IRONFLOW_SERVER_URL || "http://localhost:9123", apiKey: process.env.IRONFLOW_API_KEY,});Additional options: timeout (ms, default 30000), onError (global error handler).
API Payload Limits
Section titled “API Payload Limits”| Limit | Value | Applies To |
|---|---|---|
| Max Event Data | 512 KB | Payload of an emitted event. |
| Max Step Output | 1 MB | Data returned by a step.run. |
| Max Batch Size | 100 | Max events in a TriggerBatch call. |
| Wait Timeout | 30s | Default timeout for emit --wait. |