# Backups and restore

A backup is only worth what a restore proves. The data is usually the easy part; what defeats a restore is everything that lives outside the database — the security policies applied by an operational script, the credential held in a managed secret, the provider connections that were never in the dump. A restore nobody has rehearsed is an assumption.

- **Status:** Reference
- **Audience:** both, developer
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/technology/backup-restore/

## The two numbers, before the mechanism

**Recovery point** — How much recent work you accept losing. Set by how often a backup is taken, or by whether continuous recovery is available
**Recovery time** — How long you accept being down. Set by the restore procedure, not by the backup

These are business decisions dressed as technical ones, and they pull in opposite directions from cost. Deciding them first stops the discussion becoming an argument about backup frequency, which is only one of the two levers and not usually the binding one.

## What a database dump does not contain

A restored database is a set of tables with rows in them. Several things this system depends on are not in that set, and each has to be re-established explicitly.

| Thing | Where it really lives | What happens if you forget it |
|---|---|---|
| Row-level security policies | An operational hardening script, not the schema definition | Tables exist and every workspace can see every row — isolation silently absent |
| The database password | A managed secret, rotated by the database service | The restored instance cannot be reached by the application at all |
| Provider connections | Sealed credentials and OAuth tokens, valid for the identity that granted them | Mail and messaging stay disconnected until reconnected by a person |
| Anything derived at boot | Migrations and backfills in the start-up path | Fixed on the first start, which is the design working as intended |

> **Careful** The first row is the one that turns a successful restore into an incident. Tables created without their policies look completely normal — the data is there and queries return results. What is missing is the refusal.

## Does Connect use backups?

The database is a managed PostgreSQL instance, so backup and point-in-time recovery are properties of that managed service and of the operator's configuration of it. This corpus does not publish the schedule, the retention or the recovery targets, because a documentation page is the wrong place to assert an operational fact that can change without the page changing. Ask JBRH Digital Solutions if the specifics bear on a decision you are making.

What a workspace controls directly is different and more immediate: **export**. The Data grid exports to CSV across its thirteen sheets, files are downloadable, and an export is the copy you hold under your own control rather than under the operator's. It is not a backup of the application — it will not restore autonomy settings, memory tiers or mailbox connections — but it is the record of your own information.

## The rehearsal

1. Restore into a fresh database rather than over the live one, and name it so that nothing can point at it by accident.
   - Result: You find out whether the backup is readable at all, which is the first thing that can be false.
2. Re-apply everything that lives outside the dump — the security policies first, then the credential the application will read at boot.
   - Result: The restored copy now has refusals as well as rows, which is the difference between a database and a safe one.
3. Start the application against it and let the boot path run.
   - Result: Migrations and backfills bring the schema to current. If start-up fails, you have learnt it here rather than during an incident.
4. Check the things that are supposed to be invisible: that one workspace cannot see another's rows, and that a list which should be empty is empty.
   - Result: A restore verified only by counting rows would pass with isolation switched off.
5. Write down how long it took.
   - Result: That number is your real recovery time. The estimate you had before the rehearsal was not.

The step people leave out is the fourth. Verifying a restore by checking that the data is present tests exactly the half that rarely fails.

## Questions

### Is exporting my data the same as having a backup?

No. An export is a copy of your records — conversations, people, deals, follow-ups and the rest — in a format you can read anywhere. It does not carry the configuration that makes Connect behave the way yours does, and it cannot be loaded back to reconstitute a workspace. Treat it as your own copy of your information, which is valuable for a different reason.

### Why would a restored database be less safe than the original?

Because row-level security is applied by an operational script rather than carried in the schema definition. A restore that recreates tables and rows can therefore produce a database with no policies on it, where every query succeeds and nothing is refused. That is why the rehearsal has to check for an empty result, not just a populated one.

### How often should a restore be rehearsed?

Often enough that the procedure is current, and always after a change to what lives outside the database — a new scoped table, a change in how credentials are read, a new operational step. Those changes are what make a previously good procedure quietly wrong.

## Related

- [Export your data](https://connectbyjbrh.com/docs/how-to/export-your-data/)
- [PostgreSQL](https://connectbyjbrh.com/docs/technology/postgresql/)
- [Row-level security](https://connectbyjbrh.com/docs/technology/row-level-security/)
- [What is kept and for how long](https://connectbyjbrh.com/docs/security/data-retention/)
- [Importing and exporting](https://connectbyjbrh.com/docs/files-data/data-import-export/)

## What this page is based on

- `docs-source/sources/GENERAL.md` §3, §9 and §10 — RLS from an operational script, exports, the database secret
- `docs-source/facts.py` — PRODUCTION (PostgreSQL with forced row-level security)
- Connect capability registry (docs-source/facts.py)
