Best dev DB for agents — a Postgres test DB cloned straight from your production. No seeding. No dumps.
Website · Guide · Agents · Development
# Linux
curl -fsSL https://raw.githubusercontent.com/bhpark1013/Snaplicator/main/deploy/install.sh | sudo bash
# macOS — no sudo; it builds a Linux machine first
curl -fsSL https://raw.githubusercontent.com/bhpark1013/Snaplicator/main/deploy/install.sh | bashIt asks for a connection URI to your primary. No database to hand? Append
-s -- --demo to start on a sample one instead.
1. Data and schema replicated in real time. Not only DML: schema changes on the primary are reflected in real time as well.
2. Database branching. Save a particular state of the database and keep restoring to that state as you test and run QA.
Seeding the data every time I set up a test database, a bug in production that would not reproduce in the test environment, and QA that meant testing the same scenario over and over — it was tiresome enough that I built this.
It replicates the primary's DML and DDL in real time, and you snapshot a particular state of that copy, so every test runs against the same data.
Use it if
- Production is on RDS, Azure Database for PostgreSQL, Cloud SQL or a machine you own, and you are not moving it.
- You need real production data to test against, and it cannot leave your own infrastructure.
- When your agent needs to test against production data without consequences.
- When applying every schema change to the test database has become a chore.
Don't use it if
- You cannot enable
wal_level=logicalor create a replication slot on the primary. - You already use Neon as your primary database.
| Rows follow production | Schema follows | Snapshots / branching | Your own infrastructure | Your production stays put | |
|---|---|---|---|---|---|
| Neon | ○ logical replication | ✕ yours to keep in step | ○ copy-on-write branches | ✕ open source, but scoped to experiments | △ branch a copy hosted by Neon |
| Supabase | ✕ branches start with no data | ○ from your migration files | △ their cloud only | ○ documented — but without branching | ✕ Supabase-hosted projects only |
| Aurora cloning | ✕ a clone is fixed when taken | ○ same storage as the source | △ 15, then it is a full copy | ✕ AWS Aurora only | ○ clones the cluster you already run |
| DBLab | △ managed primaries are re-copied on a schedule | △ only at the next full refresh | ○ ZFS or LVM thin clones | ○ your hardware | ○ sits beside it |
| Xata | ○ logical replication (pgstream) | ○ event triggers replay DDL | ○ copy-on-write branches | △ self-hosted, on Kubernetes | ✕ the copy lives on the Xata platform |
| Snaplicator | ○ logical replication, managed included | ○ event triggers replay DDL here | ○ btrfs copy-on-write | ○ one machine you own | ○ sits beside it |
Neon and Supabase branching — both are excellent, and if you already run on them, use their branching: a branch there is your data.
DBLab (Postgres.ai) — the OSS I took the idea from. What differs here is that Snaplicator uses logical replication to keep data and schema in sync with the dev DB in real time.
Checked against Neon branching · Neon: replicate from RDS · Supabase branching · Aurora cloning · DBLab data sources · Xata · Postgres: logical replication restrictions
It asks for a superuser, so here is exactly what that is for. Schema changes are
captured with event triggers, and CREATE EVENT TRIGGER is superuser-only —
PostgreSQL provides no GRANT for it. Putting a newly created table into the
publication additionally needs ownership of that table.
Everything it creates is named _snaplicator*, so you can find every piece:
_snaplicator_ddl_log table one row per CREATE / ALTER / DROP
_snaplicator_capture_ddl trigger writes that row
_snaplicator_capture_drop trigger the same, for drops
_snaplicator_auto_add_<pub> trigger a new table joins the publication
<publication> publication created, or one you already have
<slot> slot opened by the replica, as any has
Your rows are read and never written. Removing all of it:
DROP EVENT TRIGGER IF EXISTS _snaplicator_capture_ddl;
DROP EVENT TRIGGER IF EXISTS _snaplicator_capture_drop;
DROP EVENT TRIGGER IF EXISTS _snaplicator_auto_add_<publication>;
DROP FUNCTION IF EXISTS public._snaplicator_capture_ddl(),
public._snaplicator_capture_drop(),
public._snaplicator_auto_add_<publication>();
DROP TABLE IF EXISTS public._snaplicator_ddl_log; -- sequence and index go with it
DROP PUBLICATION IF EXISTS <publication>;
SELECT pg_drop_replication_slot('<slot>'); -- do not skip this oneThe slot is the one that matters. A slot nobody reads holds WAL on the primary until the disk is gone, so drop it if you stop using Snaplicator.
MIT — see LICENSE.