Skip to content

When Ironflow is unavailable

Ironflow is a server, not a library. Almost nothing the SDK exposes is computed in your process — emit, kv(), config(), secrets, entity-stream appends and projection reads are all network calls to the engine. There is no client-side cache to fall back on: sdk/js/node/src/config-client.ts and sdk/js/node/src/kv.ts hold no cache or TTL logic at all.

So the honest answer to “what happens when Ironflow is down” is not one answer. It depends on which of three things is down, and — for the third — on whether the thing you lost comes back.

┌──────────────────────────────────────────────────────────────┐
│ 1. your worker process dies → run resumes, nothing lost │
│ 2. one node of a cluster dies → survivors take over │
│ 3. no engine is reachable → this page │
└──────────────────────────────────────────────────────────────┘

Cases 1 and 2 are Crash recovery. This page is case 3.

The useful axis is not “which feature” but “is this recoverable”.

┌─────────────────┬──────────────────────────────────────────────┐
│ lost for good │ cron slots · inbound webhooks past retry │
│ pauses, resumes │ in-flight runs · workers · accepted events │
│ fails at call │ everything else — no cache, no degraded mode │
└─────────────────┴──────────────────────────────────────────────┘

Scheduled runs that fall during the outage do not fire, and are not backfilled. On restart the scheduler computes the next slot from the current wall clock, so a two-hour outage of an hourly job simply loses two runs.

Skipping is a defensible policy — firing a burst of catch-up runs after an outage is often worse than firing none, and most cron bodies are not written to be idempotent across a thundering herd. The problem is that it is currently silent: there is no warning log and no metric for a skipped slot, so an hourly reconciliation job can miss a day of runs with every dashboard green. Tracked in #1639.

If a scheduled job matters, assert on its effect — a freshness check on whatever it produces — rather than trusting that it ran.

Inbound webhooks past the sender’s retry budget

Section titled “Inbound webhooks past the sender’s retry budget”

A third party posting to your Ironflow webhook endpoint gets a connection error. What happens next is entirely the sender’s policy, and it is not yours to configure: some providers retry for days, some give up after a handful of attempts, some never retry at all.

Once the sender stops, that event is gone. There is no backfill path, because the event was never in your system to queue.

Stripe ──POST──► ✗ engine down
└─ retries on ITS schedule, for ITS budget ──► gives up
permanently lost

This is the only failure on this page that no amount of Ironflow-side durability can fix. If a provider offers an event-replay API, that replay is your recovery mechanism — know before the outage whether yours does.

WhatBehaviour
In-flight runsResume from the last completed step once an engine returns. See Crash recovery.
Pull workersReconnect on a loop and never give up. No configuration needed; the worker logs each attempt.
Already-accepted eventsDurable in the outbox and JetStream. They flush when the engine returns — see the persistence caveat below.
SubscriptionsReconnect automatically — but whether they resume depends on how you subscribed. See below.

Subscriptions resume only if you named a consumer group

Section titled “Subscriptions resume only if you named a consumer group”

A subscription that passes a consumerGroup gets a stable durable consumer on the server, keyed by environment and group name. It reconnects to the same consumer and picks up its backlog.

A subscription with no consumer group — the default — is a per-connection fan-out subscription. It is not durable. On reconnect it is a new subscription, delivering from that moment on, with no memory of what it missed.

subscribe(pattern, { consumerGroup: 'billing' }) ──► durable, resumes
subscribe(pattern, { onEvent }) ──► fan-out, starts at "now"

If missing an event during a reconnect would be a correctness problem, name a consumer group.

That said, this matters less during a full outage than it looks: nothing can be published either, because publishing also goes through the engine. The backlog that survives is the work accepted in the moments before the engine went away.

Your application does not crash — it goes quiet

Section titled “Your application does not crash — it goes quiet”

Push-mode functions are HTTP endpoints that Ironflow calls. When the engine is down it simply does not call them. Your service stays up, healthy, and idle. Nothing in your process fails, because nothing in your process is being asked to do anything.

This is worth knowing because it shapes your alerting: the absence of Ironflow traffic will not trip a liveness probe. Alert on work completed, not on the process being up.

Everything else throws immediately. There is no degraded mode and nothing is queued:

  • emit, invoke, cancelRun
  • streams.append and entity-stream reads
  • kv(), config(), secrets
  • getRun, listRuns, getProjection
  • time-travel and audit reads

Handle these the way you handle any dependency that can be unreachable — at the boundary, with a timeout and a fallback appropriate to the call site. A failed config() read on a hot path is an outage in your service too, if you let it be.

The CLI talks to the engine over HTTP, and the dashboard is embedded in the same binary. ironflow inspect, ironflow secret and the dashboard are all unavailable during an outage — you are blind to exactly the runs you are worried about, at exactly the moment you want to look at them.

Plan for this: your run-state visibility during an Ironflow outage is whatever you have exported to your own observability stack, not the dashboard. See Audit logging.

@ironflow/browser has an opt-in offline queue. Writes go to IndexedDB and flush when connectivity returns.

Its scope is narrow and worth stating precisely:

  • It wraps only emit and streams.append. Every read still fails.
  • It is opt-in and requires an identity.
  • The rest of the client is unwrapped, on .client.

It exists for a browser in a tunnel, not for an engine that is down. If your users are on a train, it is the right tool. If your engine is down, it changes nothing for your backend.

Three questions worth answering before an outage rather than during one:

  1. Which of your scheduled jobs would be harmful to skip? Those need an effect-based freshness check, because Ironflow will not tell you they were skipped.
  2. Which of your inbound webhook providers support replay? That answer is your only recovery path for the outage window.
  3. What can you see when the dashboard is gone? If the answer is “nothing”, export audit events somewhere you control.

Note that a single-node deployment makes every case on this page reachable at once. Multi-node clustering with PostgreSQL turns most engine failures into case 2 — one node dies, survivors take over — which is a different and much shorter page. See Self Hosting.