Node execution worker
Run one generated Runtime program in a durable, separately supervised Node process.
Use the execution worker when Runtime work must continue outside the process
that accepted it. An API process can commit a durable task, timer, or suspended
Flow to PostgreSQL; crux runtime worker then discovers and executes that work.
This is the supported long-lived Node topology when work must survive an app
restart or deployment. For tests and process-local development, node() with
its in-memory store is simpler and does not use the worker command.
Configure PostgreSQL
Install @use-crux/postgres and its pg peer, then configure the in-process
Node composer in crux.config.ts:
import { config } from "@use-crux/core";
import { node } from "@use-crux/core/runtime";
import { postgres } from "@use-crux/postgres/runtime";
import { postgresRecordStore } from "@use-crux/postgres";
export default config({
// Thread RecordStore is required for durable Agent Sessions and Thread owners.
storage: { records: postgresRecordStore() },
runtime: node({
store: postgres(),
}),
});postgres() and postgresRecordStore() read DATABASE_URL by default and
should target the same database. Runtime storage alone is enough for Flow Work;
durable Agent Sessions need both the Runtime store and the Session-owned
Thread RecordStore. Apply additive schema setup for both before starting
application or worker processes:
crux setup --applyGenerate the program
Export every Flow and durable task that the worker may execute as a top-level, named export. Then generate the immutable program:
crux runtime generateCrux writes .crux/generated/runtime/manifest.json and
.crux/generated/runtime/program.ts. The program contains static imports for
the discovered targets. Do not edit either file. crux dev refreshes them
during development; build integrations and explicit generation cover builds
and deployments.
Package the generated files with the same application source that the worker
runs. Generate before building the deployable image, not on each replica at
startup. .crux is normally gitignored, so include it from the build context
without treating generated output as authored source.
Start and stop the worker
From the project root, run:
crux runtime workerUse --cwd <project-root> in a monorepo. The command accepts no positional
arguments. It imports the configured node({ store: postgres() }) host, loads
the generated program, acquires durable ownership, and starts an immediate
serial maintenance loop.
Stop it with SIGINT or SIGTERM. The worker stops future maintenance ticks,
waits up to 10 seconds for ownership acquisition or the active tick, disposes
the Runtime, and releases ownership. The Go supervisor allows 11 seconds before
forcibly terminating the worker process group.
One worker per namespace
Run exactly one execution worker for each PostgreSQL store and Runtime namespace. A second worker fails ownership acquisition before it executes maintenance. Other namespaces may have their own workers; a namespace isolates Runtime records but is not an authorization boundary.
Do not configure a deployment controller to overlap old and new worker replicas. Use a stop-then-start replacement strategy and wait for the old process to exit before starting its replacement.
What survives a restart
PostgreSQL stores work records, Flow snapshots, timers, waiters, leases, outbox rows, idempotency records, and scoped-idle counters. After a worker restart, maintenance can rediscover queued work, reclaim expired leases, fire due timers, expire waiters, and resume suspended Flows.
Durability is committed at Runtime step boundaries. Deferred child work, timers, suspension state, and terminal progress become durable together at a suspension or completion barrier. If a process stops between boundaries, Crux replays from the last committed snapshot. Runtime effects use recorded identities so replay does not schedule a second committed child or overwrite a committed terminal result.
This does not make arbitrary side effects exactly-once. Keep external effects idempotent, or use an application idempotency key derived from the work or effect identity.
Keep generated programs fresh
The manifest is activated last during generation. At startup the worker checks that both files exist, the manifest decodes, the generated format is supported, the program was generated from the exact manifest bytes, and its target list matches the manifest. It does not run a partial or mixed artifact set.
Regenerate after adding, removing, renaming, or moving an exported Runtime target. Before removing a target from a deployment, make sure no non-terminal work still names it. See the execution worker recipes for rollout and recovery procedures.
Deployment topology
Run the application and execution worker as separate processes built from the same source and generated artifacts. Both use the same PostgreSQL database and namespace. Supervise the worker as a long-lived service with restart-on-failure and a shutdown grace period longer than 11 seconds.
The worker's generated target imports execute trusted application code. Give the worker the application environment and credentials its targets need, plus database access. It does not need an HTTP listener, and PostgreSQL notification is not required for correctness; maintenance polling discovers durable work.
Failure recovery
If a target or store operation makes a maintenance tick fail, the worker closes and rejects its lifecycle promise. Let the process supervisor restart it after the underlying failure is corrected. Committed work remains in PostgreSQL; expired leases make interrupted work eligible for recovery.
Do not delete .crux or Runtime database rows as a routine fix. Regenerate
artifacts for artifact errors, repair PostgreSQL connectivity or setup for store
errors, and replace a duplicate owner cleanly for ownership errors.