Backing up your Cloudflare data with downpipes
A downpipe is the unit of work in this product. It pairs one source (a KV namespace, an R2 bucket, a D1 database, a Secrets Store, a set of Workers, a Stream video inventory, a Cloudflare Images inventory, or a slice of your Cloudflare configuration) with one or more destinations, and it captures that source on a schedule. The engine runs entirely inside your own Cloudflare account, so the bytes never leave it and the vendor holds nothing.
This page is for the operator running downpipes. It gives you the mental model for what happens when a backup runs, end to end, and points you at the deeper pages for each step. It does not cover restoring or recovering data. That is the job of the Recovery section, and a backup that has never been proven recoverable is only half the story, so read both.
Nothing here is instant. Every automatic backup, every failover, and every replica copy is driven by a scheduled pass that fires on a fixed interval, so the practical floor on how fresh a scheduled backup can be is about fifteen minutes. The rest of this page explains why, and what that means for your recovery point.
What a downpipe is
A downpipe holds a few pieces of configuration that together decide what gets backed up, where it goes, and how often. The most important ones are below.
| Field | What it controls |
|---|---|
name |
A human label for the downpipe, 1 to 256 characters. The downpipe id is derived from it. |
source |
The single Cloudflare resource this downpipe captures (one source per downpipe). |
cadenceSeconds |
The interval between runs, as a whole number of seconds. The minimum the engine accepts is 60, but the console floors the schedule picker higher (see below). |
destinationIds |
The ordered list of destinations a run fans out to. Index 0 is the primary the run seals to. The rest are replicas. Absent means the account default, a single copy. |
retention |
An optional per-downpipe history policy (keepRuns and/or keepDays). Absent keeps every run. Deletion only ever happens when you turn enforcement on. |
restoreTestCadenceSeconds |
How often the engine drills the latest run to prove it restores. Off keeps backups running but stops the scheduled proof. |
A downpipe captures exactly one source. If you want to back up three KV namespaces and an R2 bucket, that is four downpipes, each on its own cadence. You never run a terminal command or a script to trigger a backup. The engine’s scheduled pass does the automatic dispatching, and you can also trigger a run on demand from the console with Run now.
How a run actually happens
A single run moves through four stages. They do not all happen at once, and two of them happen on a later scheduled pass rather than during the run itself.
Capture the source
The engine reads the source inside your account and assembles the records for this run. A run that is too large for one invocation’s CPU and subrequest budget continues across alarm-driven slices until it finishes, so a big source does not have to complete in a single tick.
Seal to the first reachable destination
The run is encrypted, hashed, signed, and written to a destination, along with that destination’s own RUNLOG chain entry. A downpipe that fans out to two or more destinations seals to the first reachable one in order: the configured primary, then each replica. If the primary is down, the run still lands on a healthy replica rather than failing.
Read it back and verify before reporting success
Right after the seal and before the run counts as a clean success, the engine reads the just-written archive back from the destination and verifies it. A corrupt or partial backup is caught now, at seal time, not at the next restore test up to a week later. The verdict is attached to the run. The check never deletes or fails the run, so a suspect verdict is a loud signal to investigate, never a destructive action.
Replicate to the other destinations on a later pass
A separate scheduled pass copies each finalised run from the destination it sealed to (its recorded origin) onto every other configured destination, catching each one up on its whole backlog. Replication runs after the seal so a replica copy never delays a backup, which is why a fresh fan-out run legitimately shows fewer than its full copy count for a short while.
The order matters for honesty. A run seals to exactly one destination and is proven recoverable there, then the copies fan out afterwards. The product never claims a run wrote to every destination at the same instant, because it does not.
The fifteen-minute floor
There is one scheduled pass that drives everything automatic: a cron trigger that fires every fifteen minutes (the */15 * * * * schedule in the engine’s wrangler.toml). That single tick is the only scheduled dispatcher, and it is what dispatches due runs, runs scheduled restore tests, computes retention plans, flies the canary, and runs the replication backlog. A console Run now (POST /admin/trigger) is the separate on-demand path: it dispatches and seals a single run immediately, outside the schedule.
A downpipe whose cadence has elapsed runs on or after that cadence, at the next tick. So a downpipe set to “hourly” does not fire at exactly the top of the hour. It fires at the first fifteen-minute tick once an hour has passed. The seal itself runs outside the scheduler Durable Object (the Durable Object owns the schedule, the run lock, and the RUNLOG index, but never the seal), and a large run continues on its own Durable Object’s alarms between ticks.
The practical consequence is your recovery point. The freshest a backup can be is bounded by the tick, so the smallest meaningful gap between “now” and “the newest good run” is about fifteen minutes. The console reflects this honestly: the schedule picker’s fastest preset is Hourly, and an advanced cron expression still dispatches on the same fifteen-minute pass, so a cron set for :07 lands at the next tick after :07.
Cadence is a single interval per downpipe. There are no backup windows, no blackout periods on the simple path, and no per-run cron expressions in the default picker. An advanced schedule can add a cron expression, a time zone, and maintenance blackout windows that defer a fire, but the dispatch still rides the fifteen-minute tick. The cron expression is a standard five-field crontab expression (minute, hour, day-of-month, month, day-of-week). Month or day names such as JAN or MON, @-macros such as @daily, and a seconds field are all rejected. The time zone must be a valid IANA zone name such as Australia/Sydney, not a UTC offset. Matching is case-insensitive, so australia/sydney resolves the same as Australia/Sydney, but it must still be a real IANA zone name. Left blank, it defaults to UTC. Treat the interval as a floor, not a guaranteed firing time.
Maintenance blackout windows
The advanced schedule can carry maintenance blackout windows: spans of local time during which a scheduled run is held back. This is the console’s Use a cron schedule and/or maintenance windows option; ticking it reveals the cron, time zone and window fields.
A blackout window never drops a backup, it only delays one. A fire that would land inside an active window is deferred to the moment the window closes, then runs on the next fifteen-minute tick. So a window is a “not during these hours” instruction, not a “skip this run” one, and your history stays complete. Back-to-back windows defer in turn, up to an internal bound, so a fire cannot be pushed indefinitely.
Each window has a set of weekdays and a start and end time.
- Weekdays. Tick the days the window applies to. Leaving every day unticked applies the window every day, rather than none.
- Start (24h) and End (24h) are local times as
HH:MMon a 24-hour clock, for example02:00and06:00. They are read in the schedule’s time zone, the same zone as the cron expression (UTC when you leave the zone blank), so a window follows your wall clock across daylight saving. - The window is start-inclusive and end-exclusive:
02:00to06:00covers 02:00 up to but not including 06:00. - An end earlier than the start wraps past midnight, so
22:00to06:00is the overnight span. An end of24:00means end of day. - A window whose start equals its end is refused: it would cover no time, so the save is blocked until you change one of the times. A window row with both times blank is ignored; a row with only one of the two times set is likewise flagged and blocks the save until you complete or clear it.
A blackout window only shifts when a run fires; it does not change the cadence, the retention policy, or anything a run does once it starts.
Where backups go
Destinations are your own archive targets: an R2 bucket or an S3-compatible store you control. Each destination is a self-contained archive with its own RUNLOG, so it is an independent, restorable mirror rather than a shard of one logical store. That independence is what makes redundancy real: losing one destination does not corrupt another.
The setup order is destinations first, then downpipes. A downpipe can only fan out to destinations that already exist, and the console verifies a destination with a write probe at save time. Attach two or more destinations to a downpipe to get redundant off-source copies. A single-destination downpipe has no redundancy concept and shows no copy-count readout.
How long history is kept
By default, downpipes keeps every run forever. History is only ever pruned when you set a per-downpipe retention policy, and even then nothing is deleted until you explicitly turn on enforcement.
A retention policy bounds history by keepRuns (the most recent N runs), by keepDays (runs within the last N days), or by both. Setting a policy without enforcement is a dry run: the engine computes and logs what it would prune and deletes nothing. Only the off-by-default enforcement gate makes the engine delete, and even then a pruned run’s RUNLOG entry is kept and marked superseded rather than removed. Retention and pruning is the single path in the whole product that deletes archive bytes, so it has its own page and its own loud warnings.
Where this fits
Connect a source
Attach a KV, R2, D1, Secrets Store, Workers, Stream, Images, or Cloudflare-config source from the console, with no command line.
3-2-1 redundancy
Exactly what 3-2-1 means in this product, and where downpipes proves redundancy versus where a leg is your own attestation.
Multiple destinations
Add destinations and attach two or more to a downpipe for failover, with the primary and copy ordering explained.
Verify at seal
How each just-written archive is read back and verified before a run is reported a clean success.
Retention and pruning
Keep and delete backup history safely, with the dry-run-by-default gate and the supersede-not-delete rule.
The data-safety model
The trust argument: why manifest-driven pruning and the abstain-on-unreadable invariant mean a retained run can never lose its bytes.
Recovering data
Restoring and proving recoverability live in Recovery. A backup you have not proven recoverable is only half done.
For where the engine and its scheduled pass sit in your account, see the deployment topology. To read the run history a recovery picks from, see runs and history.
Last updated .