"A backup you can't restore from isn't a backup, it's an archive"
The sentence in the headline is not ours. It is from someone who had just discovered, at the worst possible moment, that the export they had been diligently taking every week was not a thing they could put back.
It is the correct test, and almost nothing in this category passes it. An export is a file. A backup is a file plus a demonstrated path back, and the second half is the expensive one, which is why it is usually the half that is missing.
What we do
Backups verify themselves by restoring. Every dump is loaded into a scratch database on the way out; one that restores with errors is quarantined rather than kept, and a dump too small to be real is refused outright instead of sitting in a bucket looking like insurance.
That check has to happen at backup time, because the alternative is finding out during the incident. Verifying at restore time is not verification. It is the incident.
Separately there is a drill that does the whole thing end to end — destroy, restore, verify — against a live stack, with checksums over canary rows rather than a "looks fine" from a human. The most recent recorded run: the application was readable again 9.4 seconds after the disaster, 120 rows were lost, and the checksums matched.
Take those numbers for exactly what they are. That is one drill, on one dataset, on one machine, recorded on 2026-08-01 — it is not a promise about your data volume, and anybody quoting a recovery time at you without naming the dataset is selling something. What it does establish is that the path back exists and has been walked recently, which is more than an untested dump can say.
The bug the drill found in our own backups
Worth telling because it is the genre of thing this whole practice exists to catch.
The scratch database used for verification was dropped on the normal exit path and not on the abnormal ones. A run killed part way through — a container stopped mid-verify — left its scratch database behind permanently. We measured two of them on one deployment, holding 200 MB inside the very volume the backups exist to protect.
The backup script had been quietly consuming the disk it was insuring. Nothing about that is visible from reading the code and agreeing with it.
Now the part we have not built
The admin panel offers a snapshot of a workspace as a single JSON file: structure, records, views, forms and workflows. It is a real export and it does what it says.
Nothing reads it back. There is no importer. Restore is operator-side — pg_restore and the drill above — and if what you wanted was a self-service "put this workspace back the way it was on Tuesday" button, we do not have one.
We looked hard at this row during an internal audit, and the interesting part was our own near miss. An internal comment described that file as a "restorable snapshot". The word never reached a user — the text in the product says only that it downloads the workspace as one JSON file, which is exactly what it does — so nobody was ever misled. But the word was in the repository, one careless copy-paste away from a marketing page, and a claim like that is only ever one paste from being true of your website and false of your product.
The comment is fixed. The gap is real and it is unbuilt work, not a roadmap promise.
Why self-hosting changes the shape of this
None of the above is impressive engineering. It is ordinary operational hygiene, and the reason it is worth a post is that on a hosted product you cannot do any of it.
You cannot verify a vendor's backup by restoring it. You cannot run a drill against their infrastructure. When the vendor's own API cannot produce a complete restore — which is the specific failure behind several of the angriest threads we read — you have no second option, because there is no second copy you control.
On your own Postgres, "can we actually restore this?" stops being a question about somebody else's competence and becomes a Tuesday afternoon task with a yes-or-no answer.
If you want to check us
Run the drill against a staging stack. It destroys, restores and verifies on its own and prints what it measured. Then stop the container half way through a backup and see whether the scratch database is cleaned up — that is the bug above, and it is the kind of thing you should be checking in any product you are about to trust with the system of record.
The number we would rather you remember is not 9.4 seconds. It is that we can tell you what the number is, when it was measured, and how to measure it yourself.