Skip to content
Rowsafe
Docs

Restore a database

Rewind to any second or a Mark, bring back rows, or rewind the whole database, in one click.

Rewind saves every change, so you can go back to any second in your recovery window, or to a Mark. Open the database's Rewind tab, then Rewind.

Two ways back:

  • Restore a copy next to production, compare it, and bring back the rows you lost. Production keeps running. Usually what you want.
  • Rewind the whole database to that moment. For the worst case. You can undo it.

Everything happens on your server. Only names, counts and sizes reach Rowsafe.

Works for PostgreSQL, MySQL, MariaDB, MongoDB, ClickHouse, Redis and Valkey. This page uses PostgreSQL's words; differences are in By database.

Which one to use

What happenedDo thisWhat happens to production
Rows deleted or overwritten by mistake (a DELETE without WHERE, a bad script, a bug)Restore a copy, compare, bring back rowsOnly the rows you choose are put back. Everything else, including newer writes, stays.
You want to see the data as it was at 14:04Restore a copyNothing.
A migration or script damaged many tables, or a table was droppedRewind the whole databasePostgreSQL stops while Rowsafe restores. Later writes are removed, and kept aside 7 days for undo.
The server is lost (dead disk, deleted server)Restore to another serverA new database on another of your servers.
You want the database elsewhereFork itNothing.

You can go back to any second from the start of your oldest full backup up to the last change in your bucket (at most about a minute ago).

Restore a copy

Pick the moment

Pick a time, in your time zone, to the second: just before the mistake (14:04:00 for a DELETE at 14:05). Or pick a Mark (also Restore a copy at this Mark on the Marks page): the copy has everything committed before it, nothing after.

Restore it

Click Restore a copy. It's kept 24 hours by default, up to 7 days. It takes about as long as a Proof run (see the Proof tab), longer when the moment is far from the last backup.

Use it

When ready, you see its last change, size and expiry. Extend keeps it longer, Delete removes it. Then compare it with production.

The copy is:

  • On your server, restored like Proof does. It holds every database in that PostgreSQL server.
  • Isolated: a private Unix socket, no network port, read-only, never archives to your bucket, low CPU and IO priority.
  • Deleted by itself when it expires, even offline. Health offers Keep it 24 hours longer before then.
  • One at a time per database. Delete it to restore another moment.

It needs about 1.3 × the database size + 1 GB free in /var/lib/rowsafe/rewind. With less, Rowsafe says how much and doesn't start.

Compare with production

On the copy's card, click Compare with production, for every table or the ones you pick. Per table, by primary key:

CountMeans
Missing in productionRows in the copy whose key is gone from production: deleted since.
ChangedRows with the same key and different values.
Only in productionRows added since.

It runs on your server with temporary tables. Production isn't changed, and only counts reach Rowsafe.

Skipped tables show why:

  • No primary key: Rowsafe can't match rows. Rewinding the whole database brings such a table back.
  • Too big: over 5 GB or 20 million rows.
  • Dropped since, or its key column changed type.
  • More than 200 tables: pick the ones to compare.

Tables linked by foreign keys (like orders and order_items) are shown together, so you can bring them back in one run. Notes flag things like a column added since.

Bring back rows

Select the tables, check the counts, click Bring back N missing rows, and type the database name. Then:

  1. Rowsafe saves a Mark first.
  2. It puts back only rows whose key is missing in production. Existing and newer rows stay.
  3. Parents before children, all in one transaction per database: every table gets its rows back, or nothing changes.
  4. Triggers don't fire, so no duplicate emails or webhooks. Rowsafe checks foreign keys itself first.
  5. Sequences move past the restored keys.
  6. A row whose unique value (an email, say) now belongs to another row is skipped and reported as a conflict.

You see the result, like "Brought back 1,204 rows in applications and 3,310 in application_notes."

Also restore rows that were changed (off by default) resets changed rows to the copy's values. Use it after a bad UPDATE.

Per run:

  • Up to 50 tables.
  • Rows by plan: 1 million on Free, 10 million on Pro, 50 million on Business (today the agent stops at 10 million). Over the limit, nothing changes: bring back fewer tables.

Nothing changes, and Rowsafe says why, when:

  • rows would point to rows of another table missing in production: add that table to the run;
  • another session locks a table for more than 5 seconds: try again;
  • a column added since needs a value the copy doesn't have;
  • restoring changed rows would leave other rows pointing to missing values: bring back only missing rows.

Added columns get their default, generated columns are recomputed, and removed columns are left out.

Rewind the whole database

The worst case only

This replaces the live database. Everything written after your moment is removed, in every database inside that PostgreSQL server, and PostgreSQL stops while it runs. If only some rows are wrong, bring them back instead.

Under Rewind the whole database to this point, pick the moment and type the database name. The page says exactly what will happen.

  1. Rowsafe saves a Mark and checks everything below. If anything is missing, it stops first.
  2. Stops PostgreSQL. Apps get errors for about as long as restoring a copy takes.
  3. Keeps the current data aside by renaming the data directory to <data directory>.before-rewind-<UTC time> (instant, nothing copied).
  4. Restores into a fresh data directory, keeps your config, and replays changes up to your moment, with no network port.
  5. Starts PostgreSQL.
  6. Takes a full backup and checks changes reach your bucket.

If anything fails after the stop, or the agent restarts mid-way, Rowsafe puts the original data back and starts it.

It needs:

  • PostgreSQL installed on the server, or in Docker with the container control service and a data volume the agent can write. Otherwise, restore a copy and bring back rows.
  • Permission to restart or stop PostgreSQL, given at install. Older servers may need the install command run again: see Troubleshooting.
  • The primary, not a replica.
  • No tablespaces of your own (not supported yet).
  • About the database size + 10% free next to the data directory, for Undo.

If something's missing, the button is disabled and says why.

Standby replicas no longer match after a rewind. Rowsafe warns you; set them up again.

Undo a rewind

Undo rewind swaps the data back the same way: stop, swap, start. Writes made after the rewind are kept aside in turn, so nothing is lost. Only the latest rewind can be undone.

The old data is kept 7 days, then deleted. Delete the old data now frees the disk, and ends Undo. Health reminds you while it's there.

By database

Same buttons; what happens underneath depends on the database.

Restore a copy toCompare byBring back rowsRewind the whole database
PostgreSQLany second or a Markprimary keyone transaction per database; triggers don't firestops PostgreSQL and swaps the data directory (needs restart)
MySQL, MariaDBany second or a Markprimary keyone transaction per database; triggers run (MySQL can't turn them off)stops the server and swaps the data directory's contents (needs restart; not in Docker, not with replicas)
MongoDBany second or a Mark_idnot one transaction: running it again finishes the jobswaps each collection with a rename, MongoDB keeps running (not with time series collections)
ClickHouseany second (to the microsecond) or a Markall columns (no unique key)not one transaction; not into Summing, Aggregating or Collapsing tablesswaps data partition by partition, ClickHouse keeps running (not replicated tables)
Redis, Valkeyabout a second (when each change reached the agent), or exactly a Markkey, per logical database (large databases are sampled)keys for a pattern, with their remaining time to live; existing keys only overwritten if you chooseloads each logical database into an empty one and swaps it in (SWAPDB), Redis keeps running (needs an empty logical database and free memory for one more copy of the largest)
SQLiteany second or a Mark (WAL mode); a backup otherwiseprimary key (row number without one)one transaction; never overwrites a rowwrites the restored file into the live one, your app keeps running (writes wait a few seconds)
OpenSearcha snapshot (every 5 minutes) or a Markdocument _id (large indices: the first million)documents with their _id; existing ones only overwritten if you choosedeletes each index and restores it from the snapshot, OpenSearch keeps running (searches of an index fail while it's restored)
Qdranta snapshot (every 15 minutes) or a Marknot availablenot availablerestores each collection through Qdrant's API, Qdrant keeps running
Meilisearcha snapshot (every 15 minutes) or a Markprimary keydocuments; existing ones only overwritten if you choosecopies the indexes in under other names, then swaps them in at once, Meilisearch keeps running

Undo works the same everywhere: the old data is kept 7 days (on the server; for Redis and Valkey, as a snapshot in your bucket). Details: MySQL and MariaDB, MongoDB, ClickHouse, Redis and Valkey, SQLite, OpenSearch, Qdrant, Meilisearch. The rest of this page is about PostgreSQL.

Server lost: restore to another server

When a server stops reporting for 10 minutes, its databases show Server gone? with Restore to another server. Nothing changes on the old server.

Have another server ready

It must use the same storage (the same bucket, or Rowsafe Storage) and the same passphrase as the lost one, and run the same engine (for PostgreSQL, the same major version). Rowsafe lists your servers and says which can take it.

None yet? Install Rowsafe on a new server: the page shows the command under Do it yourself. Pick the same storage, then Use my own passphrase and paste the lost server's. It shows up in a minute.

Pick the moment and the server

Latest (the last change that reached your backups), any second, or a Mark. Name the new database and pick the server. Rowsafe restores it there and turns on its backups.

Retire the old one

Point your apps at the new server. Then click Retire on the old database: Rowsafe stops watching it and keeps its backups (in Rowsafe Storage, until an owner deletes them; in your bucket, Rowsafe deletes nothing).

Works for every engine that can be forked: PostgreSQL, MySQL, MariaDB, MongoDB, ClickHouse, Redis, Valkey and SQLite.

On a server on its own, it's sudo rowsafe restore shop --from-bucket on the new server.

Save your passphrase somewhere safe

Rowsafe never sees it. A new server needs it to read your backups: keep it in your password manager, not only on the server.

Who can do this

  • Owners and admins only, and API keys with write access. Every step is in the audit log.
  • AI agents act as their person. They can show the window (rewind_window) and, when you ask, restore a copy, bring rows back or rewind as the person who connected them, with the same confirmations and Mark first. It runs at once for an owner's or admin's agent when there's a backup to go back to; otherwise Rowsafe refuses.

Do it yourself

The buttons do all of this. These are for scripts, and for tables Rowsafe can't compare.

Advanced: restore by hand or to another server

Lost the server? Use Restore to another server: no commands. Restore by hand only when you can't use Rowsafe (no account, no access), or to practise. Your PostgreSQL backups are a standard pgBackRest repository in your bucket.

Two rules

Never restore over the only copy. Use a new server, or at least a new data directory.

One writer per bucket path. Always restore with --archive-mode=off, so the restored cluster can't archive into the same repository.

Edit on GitHub