docs/install/guarantees/disaster-recovery.mdx
Activepieces runs on three backing stores. Two are systems of record; one is rebuildable state:
This page describes what happens when each one fails, how to recover, and how to compute your RPO/RTO from your own infrastructure choices. Recovery targets are stated as formulas because on a self-hosted deployment they are properties of your Postgres, Redis, and S3 setup, not of Activepieces.
| Store | Holds | RPO | RTO | Recovery action |
|---|---|---|---|---|
| Redis | Job queue, schedules, caches | Your Redis persistence window (≤1s with AOF everysec; entire queue with no persistence) | Redis failover + app restart | Restart app, then bulk-retry stranded runs |
| Postgres | Flows, runs, schedules, connections | Your Postgres HA/backup config (≈0 with a synchronous replica) | Provider failover + app/worker restart | Restart app and workers after failover |
| S3 | Run logs, large payloads, bundles | Your bucket versioning/replication config | Provider-dependent | None (runs retry automatically) |
Redis is the only store where data can be lost that Postgres doesn't also have. The exposure is precisely scoped:
Lost permanently — queued jobs that no worker had picked up. The critical case is async webhooks: Activepieces returns 200 with an x-webhook-id header after the job is enqueued to Redis, before any Postgres record exists. If Redis loses its dataset in that window, those requests vanish without a trace, and the sender — having received a 200 — will not retry. Your async-webhook RPO is your Redis persistence window:
everysec: ≤1 second of accepted webhooks.Sync webhooks are not exposed this way — the run is recorded in Postgres before execution, and on failure the caller receives an error it can retry.
Not lost — everything else. Schedules, renewals, resume timers, and in-flight runs all have their source of truth in Postgres:
QUEUED/RUNNING) until you retry them — the retry resumes from the last checkpoint and never re-runs completed steps (see Crash Recovery).QUEUED or RUNNING that started before the incident and bulk-retry them. They resume from their last checkpoint.Do not bother backing up Redis. Enable persistence (AOF everysec or a managed offering) to shrink the queue-loss window, but treat the rest of Redis as a rebuildable cache. A stale Redis backup is strictly worse than an empty Redis plus the refill on restart.
During a managed-Postgres failover (typically 1–5 minutes on Azure/AWS/GCP), the control plane is down but the data plane degrades gracefully:
5xx the sender can see and retry.Protect Postgres with your provider's point-in-time recovery. It is the system of record for everything except execution history.
S3 (or R2/GCS/MinIO) holds run logs — the step checkpoints that make crash recovery work — plus webhook payloads over the inline threshold and flow bundles.
S3 providers publish eleven-nines durability, which is why this is the lowest-risk component — but durability claims don't cover accidental deletion or misconfiguration. Enable bucket versioning; add cross-region replication if execution history is compliance-relevant.
A typical managed deployment — Azure Database for PostgreSQL (zone-redundant HA), managed Redis with AOF everysec, S3 with versioning:
| Scenario | RPO | RTO |
|---|---|---|
| Redis node loss | ≤1s of accepted async webhooks; nothing else | Minutes: Redis failover + app restart + bulk-retry |
| Postgres failover | ≈0 | ~5 min failover + container restart |
| S3 outage | 0 (retries drain) | Provider recovery time |