Sleep at night.
Nightly dumps make backups. Rowsafe proves you can restore — to the second.
Continuous WAL archiving, point-in-time recovery, weekly restore drills, monitoring and alerts for the PostgreSQL you already run on your own servers or in Docker. Adopted in place, into your own bucket, without us ever restarting your database.
Free for one server and three databases. PostgreSQL 13+ on Debian, Ubuntu or Docker.
From a read-only plan to a passing restore drill
The CLI's real output. IDs, sizes and times are examples.
- $ rowsafe adopt appRegistered app (db_5k2m9x7q4t1vh). Planning...Waiting for the agent to pick up adopt task task_8h3n2c6w1r0zq...Running...Task task_8h3n2c6w1r0zq: adopt SUCCEEDEDPostgreSQL 18.0, 12.4 GiB across 2 databases, data directory /var/lib/postgresql/18/mainPlan (nothing has been changed yet):+ write pgBackRest config (0600, contains repository credentials) to /etc/rowsafe/pgbackrest/app.conf+ pgbackrest stanza-create: initialise the repository for this cluster~ archive_mode: off -> on [needs restart]~ archive_command: (disabled) -> /usr/bin/pgbackrest --config=/etc/rowsafe/pgbackrest/app.conf --stanza=app archive-push %p~ archive_timeout: 0 -> 300! PostgreSQL must be restarted for WAL archiving to start. Rowsafe never restarts your database; restart it in a maintenance window, then run `rowsafe db verify`.Next: rowsafe apply app
Plan
Read-only. With one enrolled host, Rowsafe picks it. The agent inspects PostgreSQL over its Unix socket and lists exactly what it would change.
- $ rowsafe apply appThis changes PostgreSQL settings on db-1 (app) with ALTER SYSTEM and reloads it.It never restarts PostgreSQL. Review the plan first with `rowsafe db plan`.Apply? [y/N] yWaiting for the agent to pick up adopt task task_3v9d0m4k7s2pa...Running...Task task_3v9d0m4k7s2pa: adopt SUCCEEDEDPostgreSQL 18.0, 12.4 GiB across 2 databases, data directory /var/lib/postgresql/18/mainApplied:the same five changes and the same restart warning as the plan aboveNext: restart PostgreSQL in a maintenance window (e.g. `sudo systemctl restart postgresql`), then run `rowsafe verify app`.
Apply
Writes the pgBackRest config, creates the stanza (the first real test of your bucket and passphrase), then ALTER SYSTEM and a reload. No restart.
- $ sudo systemctl restart postgresql@18-main.service
Restart, on your schedule
The only downtime, and you run it: usually a few seconds.
- $ rowsafe verify appWaiting for the agent to pick up check task task_q1w7e5r9t3y0b...Running...Task task_q1w7e5r9t3y0b: check SUCCEEDEDapp is protected. The first full backup is queued; follow it with `rowsafe tasks app`.
Verify
Forces a WAL switch and proves the segment reached your bucket. Schedules start and the first full backup is queued.
- $ rowsafe backup list appLABEL TYPE FINISHED DURATION DB SIZE STORED20260924-140641F full 2026-09-24 14:17 10m52s 12.4 GiB 3.1 GiB
First backup
Compressed and encrypted on the host before upload. Your bucket only ever holds ciphertext.
- $ rowsafe drill appWaiting for the agent to pick up drill task task_m6n2b8v4c0x7d...Running...Task task_m6n2b8v4c0x7d: drill SUCCEEDEDDrill PASSED: restored backup 20260924-140641F (12.6 GiB) in 5m41sRecovered to the last transaction at Thu, 24 Sep 2026 14:29:51 UTCapp ok tables 214/214postgres ok tables 0/0
Drill
Restores the backup plus every archived WAL segment into a socket-only scratch cluster, checks each database, then deletes it.
A nightly dump is a backup nobody has checked.
It captures one moment a day. When the disk fails in the evening, everything since that moment is gone, and nobody knows whether the dump restores.
archive_timeout=300).Nobody has restored it
Unless someone tests it, the first real restore happens during the outage it was meant for. That is when you learn how long it takes, and what is missing.
It isn't the whole cluster
pg_dump copies one database. Roles and other cluster-wide objects need a separate pg_dumpall --globals-only, and are easy to forget.
Success can be silent
A cron job that pipes pg_dump into gzip exits 0 when pg_dump fails, unless the script sets pipefail. The file is there. It just isn't a backup.
Built for the agent era.
Agents now run migrations and destructive SQL, often unattended. When one drops the wrong table, you need to rewind to the second before it, and to know that restore works.
- you
- Drop legacy_orders. orders_v2 replaced it in July.
- agent
- safety_check {"database": "app"}
- → PROTECTED: app can be restored to any point from 2026-10-11T01:09:52Z up to about now (WAL last archived 41s ago, last backup 14.0h ago, last drill passed 3d ago).
- agent
- create_restore_point {"database": "app", "name": "before-drop-legacy-orders"}
- → Restore point "before-drop-legacy-orders" on app is ARCHIVED (confirmed in the backup repository), LSN 5/2A0001F8.
- agent
- psql -f migrations/0042_drop_legacy_orders.sql
- → DROP TABLE
- agent
- Dropped legacy_orders. Restore point: before-drop-legacy-orders.
- you
- The invoicing job still reads legacy_orders. It's failing.
- agent
- Stopping here; I won't try to repair it. You can restore app to before-drop-legacy-orders (restore from backup 20261018-010004F_20261022-010003D).
# on a scratch host: rewind to the name
$ sudo -u postgres pgbackrest --stanza=app --archive-mode=off \ --set=20261018-010004F_20261022-010003D \ --type=name --target=before-drop-legacy-orders --target-action=promote restore$ sudo systemctl start postgresql@18-main.service$ pg_dump -h scratch-1 -d app --table=legacy_orders | psql -h db-1 -d appKnow before you touch
safety_checktells the agent whether the database is recoverable right now, and why not if it isn't.A name for the moment before
create_restore_pointreturns only once the restore point is stored in your bucket. Then the change runs.Read-only by default
The MCP server offers read tools until you allow restore points, or writes. No tool runs SQL, restarts PostgreSQL or restores a backup.
Works with Claude Code, Claude Desktop and any MCP client, locally with rowsafe mcp or remotely over HTTPS. The Claude Code plugin's hook creates a restore point before migration commands run, even when the model forgets.
Your server, your bucket. We keep the schedule.
A small agent on the database host runs pgBackRest, the open-source backup tool, and ships everything to storage you own. Rowsafe decides when, keeps the history, and tells you when something is off.
Your server
VPS or bare metal, no inbound ports
PostgreSQL
Your cluster, unchanged except for its archiving settings.
↓ archive_command, every WAL segment
pgBackRest
Compresses and encrypts backups and WAL before upload.
↑ runs backups, checks and drills
rowsafe-agent
Runs as postgres, talks to PostgreSQL over the Unix socket, holds the secrets (0600).
pgBackRest to your bucket: base backups and every WAL segment, AES-256 and zstd.
Your bucket
Cloudflare R2 or any S3-compatible store, on your account. It only ever holds ciphertext.
The agent to Rowsafe: outbound HTTPS only. It asks for work; nothing calls in.
Rowsafe control plane
Schedules, history, dashboard and metrics. Never has your bucket credentials or passphrase.
Agents pull
The agent polls for work over HTTPS and runs only a fixed set of tasks: inspect, adopt, check, backup, drill and restore points.
PostgreSQL archives WAL itself
archive_command calls pgBackRest directly, so point-in-time recovery keeps working even while the agent or Rowsafe is down.
The control plane schedules
By default a full backup on Sundays, a differential the other six nights, and a restore drill every Sunday evening (UTC). Change any of it per database.
Backups, proof, and eyes on the database.
Every plan gets all of it. Plans differ only in how many servers and databases you protect.
Continuous WAL archiving
PostgreSQL hands every WAL segment to pgBackRest through
archive_command, so recovery keeps working even while the agent or the control plane is down.Point-in-time recovery
Restore to any second between the oldest retained full backup and the last archived segment. Two weekly fulls by default: one to two weeks of history.
Scheduled full and differential backups
pgBackRest with zstd, bundling and block incremental. Full on Sunday, differential the other six nights, on cron schedules you can change.
Weekly restore drills
Every Sunday the latest backup and all archived WAL are restored into a scratch cluster, started, and checked database by database.
Adopt in place
No migration and no new server. Rowsafe configures the PostgreSQL you already run, after you read the plan, and never restarts it.
Named restore points
Mark the moment before a risky change. Rowsafe confirms the restore point is in your bucket before the change goes ahead.
Built-in monitoring
Connections, slow queries from
pg_stat_statements, long-running and idle transactions, locks, disk, WAL rate, transaction ID wraparound headroom, and host CPU and memory.Alerts where you work
Missed backups, failing WAL archiving, failed drills and monitoring problems, sent by email or to Slack, Discord or any webhook.
Host or Docker
Install the agent on Debian or Ubuntu with one command, or run it as a sidecar next to the official
postgresimage.Agent-ready
An MCP server lets coding agents check protection and create restore points. Read-only unless you allow more.
Self-updating agent
Signed releases, a self-test before every switch, probation, and automatic rollback if a new version misbehaves.
Your bucket, your keys
Backups go to your own S3-compatible bucket, encrypted on the host. Bucket credentials and the passphrase never leave the server.
A backup you haven't restored is a guess.
Every Sunday, Rowsafe restores each database next to production and checks it. You find out a restore fails on a quiet evening, not in the middle of an outage.
Checks free disk first: at least 1.3 × the cluster size + 1 GiB, or the drill refuses to start.
Restores the latest backup plus every archived WAL segment with
pgbackrest restore, at low CPU and IO priority, with archiving off.Starts the copy on a private Unix socket with no TCP listener, and waits for recovery to finish.
Compares every database with production. A missing database, or one restored empty, fails the drill.
Stops the scratch cluster and deletes its directory, whatever the outcome.
$ rowsafe drill list appWHEN RESULT BACKUP RECOVERED TO DURATION DATABASES2026-10-18 21:15 PASSED 20261018-010004F 2026-10-18 21:14:37 6m3s 22026-10-11 21:15 PASSED 20261011-010002F 2026-10-11 21:14:52 5m58s 22026-10-04 21:15 PASSED 20261004-010003F 2026-10-04 21:14:41 5m52s 22026-09-27 21:15 PASSED 20260927-010005F 2026-09-27 21:14:49 5m47s 22026-09-24 14:30 PASSED 20260924-140641F 2026-09-24 14:29:51 5m41s 2Four scheduled drills after the first one. Each row says which backup was restored, the last transaction it recovered, and how long it took: your real restore time, measured weekly.
See trouble coming. Hear about it where you work.
The same agent watches the database while it guards the backups. The dashboard shows what PostgreSQL is doing, and alerts reach you before a full disk or a stuck transaction becomes an outage.
Built-in monitoring
- Connections
- By state and database, against
max_connections. - Query performance
- The slowest and most frequent statements from
pg_stat_statements. - Long and idle transactions
- Sessions stuck in a transaction or
idle in transaction. - Locks
- Who is waiting on whom.
- Disk
- Data directory and
pg_walgrowth, and free space. - WAL rate
- How fast WAL is written, and how far archiving lags.
- Wraparound headroom
- Transaction IDs left before PostgreSQL must freeze.
- Host
- CPU, memory and load of the database server.
Built-in alerts
- Slack
- Discord
- Webhook
You hear about:
- Backups missing or failing, and no recent full backup
- WAL archiving failing or falling behind
- Restore drills failing or overdue
- PostgreSQL unreachable, or the agent offline
- Monitoring problems such as disk filling up, blocked locks or shrinking wraparound headroom
Runs where your PostgreSQL runs.
On a Debian or Ubuntu server, or in Docker next to the official image. The dashboard prints the exact command with a one-time enrollment token.
On the host
Debian 12/13 or Ubuntu 22.04/24.04, amd64 or arm64. The installer checks the release signature before it touches anything, installs pgBackRest if it is missing, and runs the agent under systemd as postgres.
$ curl -fsSL https://rowsafe.sh | sudo sh -s rse_...https://rowsafe.sh/install serves the same script. Then, from your machine: rowsafe login and rowsafe adopt app.
In Docker
Add a sidecar to your existing compose file. Your postgres service keeps its stock image.
services:
postgres:
image: postgres:17
volumes:
- pgdata:/var/lib/postgresql/data
+ - pgsocket:/var/run/postgresql
+ - rowsafe-spool:/rowsafe-spool
+
+ rowsafe-agent:
+ image: ghcr.io/rowsafe/agent:<version>-pg17
+ hostname: db-1
+ user: "999:999"
+ restart: unless-stopped
+ depends_on: [postgres]
+ env_file: rowsafe-agent.env
+ shm_size: 256m
+ volumes:
+ - pgdata:/var/lib/postgresql/data:ro
+ - pgsocket:/var/run/postgresql
+ - rowsafe-spool:/rowsafe-spool
+ - rowsafe-state:/var/lib/rowsafe
volumes:
pgdata:
+ pgsocket:
+ rowsafe-spool:
+ rowsafe-state:- PostgreSQL stays on the stock
postgresimage. The agent is a sidecar that mounts the data volume read-only, the socket volume, and a spool volume. archive_commandcopies each WAL file into the spool and fsyncs it; the agent pushes it to your bucket with pgBackRest, in order, and deletes it only once it is stored.- Restore points and
rowsafe verifycount only once the WAL is in the bucket: the agent fetches the segment back from the bucket before it reports a restore point as archived. - PostgreSQL 18's new data layout is handled: mount the volume at
/var/lib/postgresqlin both services and use the-pg18tag. - Agent images are Debian-based, about 370 MB, tagged
<version>-pg14to-pg18, with-alpinevariants for the Alpine postgres images. Update by changing the tag; the agent doesn't update itself in Docker. - Linux hosts only for production. Adding the mounts recreates the postgres container: a few seconds of downtime.
Built to be trusted with production.
Rowsafe touches the most important process on your server. So it asks first, changes as little as possible, and can't reach your data.
- Plan before apply
- Adopting a database prints a read-only plan. Nothing changes until you run
rowsafe apply, and an existingarchive_commandis never replaced without--force. - Never restarts PostgreSQL
- Settings that need a restart are written with
ALTER SYSTEM. You restart in your own maintenance window, then verify. - Isolated drills
- Drill clusters listen on a private Unix socket only, run with archiving off at low CPU and IO priority, and are deleted afterwards.
- Signed updates, probation, rollback
- The agent runs only releases signed with an Ed25519 key compiled into it. It refuses downgrades, self-tests a new version, and rolls back one that fails.
- Secrets stay on your host
- Bucket credentials and the encryption passphrase live in
/etc/rowsafeon the database host (mode 0600). The control plane never receives them. - Client-side encryption
- Backups and WAL are encrypted with
aes-256-cbcbefore they leave the host. Your bucket holds only ciphertext. - Outbound only
- The agent makes outbound HTTPS requests and listens on nothing. It runs one of a fixed set of tasks and never executes commands sent to it.
- Open-source agent
- Everything that runs on your servers (agent, CLI, installer) is open source under Apache-2.0, so you can audit every line that touches your database.
Keep your server. Get the recovery of a managed database.
How Rowsafe compares with the two usual answers. These are categories, not vendors: details vary by provider, so check yours.
| Question | Nightly pg_dump or a scheduled dump service | Fully managed PostgreSQL | Rowsafe |
|---|---|---|---|
| Where PostgreSQL runs | Your server | The provider's infrastructure | Your server, unchanged |
| Data you can lose | Everything since the last dump: up to 24 hours with a nightly job | Depends on the provider; point-in-time recovery is common | About 5 minutes at most on a quiet database, less on a busy one |
| Restore to a moment in time | No, only to when a dump ran | Usually, within the provider's retention window | Yes, to the second, or to a named restore point |
| Restores tested | Only if you build and run the test yourself | The provider's responsibility | Every week, every database, with a report you can read |
| Where backups live | Wherever you copy them | The provider's storage | Your own bucket, encrypted before upload |
| Who can decrypt them | Whoever you give the files or key to | The provider, or a key you manage in their KMS | Only holders of your passphrase |
| Migration needed | None | Yes, your data moves to the provider | None; one restart in your own window |
| Control over version, extensions and tuning | Full | Whatever the provider supports | Full |
| Monitoring and alerts | Build it yourself | Usually included | Built in: queries, locks, disk, WAL, wraparound, host; email, Slack, Discord, webhook |
| Replicas and automatic failover | No | Usually available | Not yet (on the roadmap) |
Priced by servers, not by gigabytes.
Every plan gets every feature. You choose how many servers and databases to protect, and your bucket stores the backups, so the price never grows with your data.
Free
One server, fully protected.
$0forever
- Servers
- 1
- Databases
- 3
- Continuous WAL archiving and point-in-time recovery
- Weekly full and daily differential backups
- Weekly automated restore drills
- Named restore points
- Monitoring and alerts
- MCP server for AI agents
- Retention and schedules you set per database
- Dashboard, CLI and API keys
- Health issues for missed backups, WAL archiving and drills
- Signed agent auto-updates with rollback
- Audit log
Pro
For teams running a handful of Postgres servers.
$39per month
- Servers
- 5
- Databases
- 25
- Continuous WAL archiving and point-in-time recovery
- Weekly full and daily differential backups
- Weekly automated restore drills
- Named restore points
- Monitoring and alerts
- MCP server for AI agents
- Retention and schedules you set per database
- Dashboard, CLI and API keys
- Health issues for missed backups, WAL archiving and drills
- Signed agent auto-updates with rollback
- Audit log
Business
For fleets that can't afford to lose a row.
$149per month
- Servers
- 25
- Databases
- 200
- Continuous WAL archiving and point-in-time recovery
- Weekly full and daily differential backups
- Weekly automated restore drills
- Named restore points
- Monitoring and alerts
- MCP server for AI agents
- Retention and schedules you set per database
- Dashboard, CLI and API keys
- Health issues for missed backups, WAL archiving and drills
- Signed agent auto-updates with rollback
- Audit log
Prices in USD per organization, billed monthly; cancel anytime. Payments are processed by Polar, our merchant of record, which handles sales tax and VAT. Storage is billed by your bucket provider, not by us.
Compare plans
| Feature | Free$0 | Pro$39 per month | Business$149 per month |
|---|---|---|---|
| Servers | 1 | 5 | 25 |
| Databases | 3 | 25 | 200 |
| Backups and recovery | |||
| Continuous WAL archiving | Included | Included | Included |
| Point-in-time recovery | Included | Included | Included |
| Weekly full and daily differential backups | Included | Included | Included |
| Retention and schedules per database | Included | Included | Included |
| Client-side encryption to your own bucket | Included | Included | Included |
| Proof | |||
| Weekly automated restore drills | Included | Included | Included |
| Named restore points | Included | Included | Included |
| Monitoring and alerts | |||
| Monitoring dashboard | Included | Included | Included |
| Alerts on backups, WAL archiving and drills | Included | Included | Included |
| Email, Slack, Discord and webhook notifications | Included | Included | Included |
| Tools | |||
| Dashboard, CLI and API keys | Included | Included | Included |
| MCP server for AI agents | Included | Included | Included |
| Audit log | Included | Included | Included |
| Host installer or Docker sidecar | Included | Included | Included |
| Signed agent auto-updates with rollback | Included | Included | Included |
| Start free (Free plan) | Start with Pro (Pro plan) | Start with Business (Business plan) | |
Billing
What counts as a server and a database?
A server is a host with the Rowsafe agent enrolled. A database is one PostgreSQL cluster you adopt on that host (one host and port); every database inside that cluster is backed up and checked together.
How does billing work?
Paid plans are billed monthly, in USD. Payments are processed by Polar, our merchant of record, so Polar calculates and collects sales tax or VAT where it applies and issues the invoices. Upgrades take effect as soon as Polar confirms the payment, and plan changes are prorated.
Can I cancel anytime?
Yes, from the billing page in the dashboard. Your plan stays active until the end of the paid month, then the organization moves to Free. Nothing is deleted: backups, schedules and drills keep running; you just can't add servers or databases beyond the Free limits.
Questions, answered plainly.
Does Rowsafe ever restart my database?
No. Settings that need a restart (archive_mode, and wal_level if it is minimal) are written with ALTER SYSTEM. You restart PostgreSQL in your own maintenance window, usually a few seconds of refused connections, then run rowsafe verify.
What does the database host need?
PostgreSQL 13 or newer on Debian 12/13 or Ubuntu 22.04/24.04 (amd64 or arm64), a postgres user, and outbound HTTPS. The installer verifies a signed release and installs pgBackRest if it is missing. Nothing needs to listen for inbound connections.
Does it work with PostgreSQL in Docker?
Yes, on Linux hosts. PostgreSQL stays on the stock postgres image (14 to 18, Debian or Alpine). The agent runs as a sidecar, ghcr.io/rowsafe/agent:<version>-pg<major>, that mounts the data volume read-only, the socket volume and a spool volume: PostgreSQL copies each WAL file into the spool and the agent pushes it to your bucket. Backups, drills, restore points, monitoring and alerts work as on a host. You update the agent by changing its image tag.
What does the monitoring collect?
Connections, query statistics from pg_stat_statements (when the extension is enabled), long-running and idle-in-transaction sessions, locks, disk usage, WAL rate, transaction ID wraparound headroom, and host CPU and memory. Query statistics are normalized: literal values are replaced by placeholders. Row data never leaves your server.
Where do alerts go?
Email, Slack, Discord, or any webhook, on every plan. You get alerts for missed or failed backups, failing WAL archiving, failed or overdue drills, an unreachable database or offline agent, and monitoring problems such as a filling disk.
Which storage can I use?
Any S3-compatible bucket you own. The docs use Cloudflare R2; MinIO and other S3-compatible stores work through the same settings. Give Rowsafe a private bucket and a token scoped to it, and don't add a lifecycle rule that deletes objects: pgBackRest expires old backups and WAL itself.
How much data can I lose?
PostgreSQL archives each WAL segment as it fills, and Rowsafe sets archive_timeout=300, so on a quiet database at most about 5 minutes of writes are not yet in the bucket. A nightly pg_dump can lose up to 24 hours.
How far back can I restore?
From the end of the oldest retained full backup to the last archived WAL segment. Rowsafe keeps 2 full backups by default, with a full backup every Sunday: about one to two weeks. Set retention and schedules per database with rowsafe db set.
Can Rowsafe read my data?
No. Backups are encrypted on your host before upload, and the bucket credentials and passphrase stay on the host. The control plane stores metadata only: hostnames, database names and sizes, table counts, PostgreSQL settings, task logs and monitoring metrics.
What happens if Rowsafe is down?
PostgreSQL keeps archiving WAL through archive_command, which needs only pgBackRest on your host, so point-in-time recovery keeps working. Scheduled backups and drills resume when the control plane is back.
How do I restore?
Today, with pgBackRest, following the restore runbook: to the latest point, a moment in time, or a named restore point, onto a new host or a scratch cluster. The weekly drill runs the same restore, so you know it works and how long it takes.
What if I lose the encryption passphrase?
Then nobody can restore those backups, Rowsafe included. Store the passphrase in your secret manager, and a second place, before the first backup exists.
Does it do replicas and failover?
Not yet. Rowsafe protects and proves your backups today. Standby replicas with automatic failover are on the roadmap.
Is it open source?
Everything that runs on your servers is: the agent, the CLI (including rowsafe mcp and the Claude Code plugin) and the installer are Apache-2.0, on GitHub. The control plane and dashboard that Rowsafe runs for you are proprietary.
Should I keep my existing backups?
Yes, for a while. The adoption runbook suggests running your old job in parallel for 30 days: four passing drills and one manual restore, then retire it.
Not built yet
Rowsafe protects and proves the PostgreSQL you run today. Restoring production is a documented pgBackRest procedure. These come later, and we'll say so here when they ship.
Coming
Standby replicas and automatic failover
A streaming replica on a second server, promoted automatically when the primary is lost.
Coming
Managed hosting
Rowsafe runs the PostgreSQL server too, with the same backups and drills.
Coming
More engines
PostgreSQL first. Other databases after that.
Sleep at night.
Adopting a production database takes about an hour of work and one short restart in your window. The first drill tells you it worked.