Skip to content
Pyrula

Deployment Modes

Two deployment modes:

EMBEDDED runs execution in the same process that submits the run. Fine for local dev, tests, and small deployments. InlineWorkflowRunner is the in-process driver:

from pyrula.workflows import InlineWorkflowRunner, MemoryStore
runner = InlineWorkflowRunner(store=MemoryStore())
run_id = runner.submit("my_workflow", params={"x": 1})
events = await runner.run("my_workflow", run_id)

SEPARATE splits the two halves. One tier submits and follows runs. Worker processes claim and execute them against the same shared store. Use it when you want to scale execution independently or isolate it from the request path.

Either way, durability comes from the run’s event stream in the shared store. The mode only changes where execution happens, not the recovery guarantees.

pyrula.workflows ships WorkflowWorker for the worker tier, with no agent runtime required. Point it at a durable store and call run():

import asyncio
from pyrula.workflows import WorkflowWorker, ValkeyStore
worker = WorkflowWorker(store=ValkeyStore(url="redis://localhost:6379"))
asyncio.run(worker.run())

The worker discovers all @workflow-decorated functions automatically. Pass workflows=[my_workflow] to restrict it to a specific set.

MemoryStore is in-process only — for dev and tests. The two production stores differ in exactly one way that matters for durability:

  • ValkeyStore holds the entire live run in Valkey: the event stream plus run lifecycle/owner, idempotency keys, signals, timers, and KV. Everything is Valkey-durable, and completed runs expire after completed_ttl. If you lose Valkey, you lose runs — so run Valkey with persistence (AOF) and/or replication.

  • PostgresStore wraps a ValkeyStore and adds one thing: when a run reaches a terminal event, its full event stream is archived to Postgres, so a completed run survives past completed_ttl and beyond Valkey. Everything else still lives only in Valkey. Concretely: while a run is in flight — and for all of its lifecycle/owner state, idempotency keys, signals, timers, and KV even after completion — Postgres has nothing; that state is Valkey-durable only and expires with completed_ttl. Postgres is a completed-run archive, not a second live copy.

So “durable” means Valkey-durable for live execution and all auxiliary state, plus a Postgres archive of completed event logs. Size Valkey’s durability (persistence/replication) for your in-flight runs accordingly; Postgres protects history, not work in progress. Archival is reconciled in the background, so a transient Postgres outage during completion is retried rather than lost.

Serving runs over HTTP is part of the Agents runtime.