Skip to main content
Test mode is a supported way to run Pgrust as a disposable test server — the database your test suite spins up, hammers, and throws away. It gives you three things:
  1. Durability off. Crash recovery is abandoned by contract: if the machine dies, you delete the data directory and start over. In exchange, every commit returns without waiting on disk.
  2. Memory-first storage with graceful spill. Data lives in RAM as much as possible and spills to disk only under memory pressure — without the hard capacity cliff of a ramdisk.
  3. Near-instant per-test databases. CREATE DATABASE ... TEMPLATE tpl completes in milliseconds via copy-on-write file clones, regardless of how large your seeded template is.
Test mode is a launch profile, not a fork of the engine. Your tests exercise the exact same binary and the same code paths as production — the whole point of testing against real Pgrust instead of a mock. The only differences are configuration knobs PostgreSQL itself documents as non-durable settings, plus a filesystem choice.
Availability: copy-on-write template clones (file_copy_method = clone) ship in the next release after v0.2. On v0.2, omit the file_copy_method line from test.conf — template creation falls back to an ordinary file copy (still fast for small templates), and every other part of test mode works as described.

How it works

Test mode is three independent layers. Each is useful on its own; together they compound:

Layer 1: the non-durable preset

Save this as test.conf and include it when starting the server:
test.conf
Start Pgrust with the preset either via an include line in postgresql.conf, or directly from your harness:
With fsync = off, an OS or machine crash can corrupt the cluster. Test mode’s contract is that the data directory is disposable: your harness recreates it (or restores a cached post-initdb copy) rather than ever trusting crash recovery. Never point test mode at data you care about.

Layer 2: in-memory storage without a ramdisk

You might reach for tmpfs here — don’t. tmpfs has no copy-on-write clone support (which would defeat layer 3), and a ramdisk hard-caps at its allocation, so “spilling to disk” means swap thrashing. The key insight: once fsync is off, an ordinary on-disk filesystem already behaves like an in-memory filesystem with disk spill. Every write lands in the kernel page cache and returns immediately; writeback happens lazily in the background, and only memory pressure forces pages out. Reads of recently written test data are cache hits. That is exactly “in memory as much as possible, spill to disk when needed” — implemented by the kernel, with no capacity cliff. So layer 2 is just a filesystem choice:
Nothing to do. APFS — the default filesystem — supports copy-on-write clones natively and already satisfies both properties. Put your test data directory anywhere.

Layer 3: instant template clones

With file_copy_method = clone, CREATE DATABASE ... TEMPLATE ... STRATEGY FILE_COPY clones the template’s relation files with copy-on-write:
  • On macOS, via copyfile(..., COPYFILE_CLONE_FORCE) on APFS.
  • On Linux, via copy_file_range(), which reflinks on XFS (reflink=1) and btrfs.
Cost is proportional to the number of relation files, not the bytes in them. A multi-gigabyte seeded template clones in roughly the same few milliseconds as a 50 MB one — the case where the default WAL_LOG strategy takes tens of seconds.

Putting it together: a test harness recipe

1

Once per suite: create the cluster and seed a template

Sessions connected to a database block its use as a template, so disconnect from tpl once seeding finishes.
2

Per test: clone, run, drop

Each test (or each slot in a pooled harness) gets a pristine, fully seeded database for a few milliseconds, with no TRUNCATE sweeps or transaction-rollback tricks — and therefore no restrictions on what the test may do.
3

On any crash: recreate, don't recover

If the server or machine crashes, delete the data directory and re-run the suite setup (or restore a cached copy of the post-initdb directory). Never rely on crash recovery of a test-mode cluster.

Expected performance

  • Template clone: ~1–10 ms for typical test schemas, and roughly constant in template size on a reflink-capable filesystem — cloning is O(number of files), not O(bytes).
  • Residual per-test cost: two cheap internal checkpoints that FILE_COPY requires (single-digit milliseconds at shared_buffers = 128MB on an idle test instance) plus connection establishment.
  • If clone latency ever dominates your suite, the standard mitigation is a warm pool of pre-cloned databases maintained by the harness (the IntegreSQL pattern) — Pgrust needs no changes to support it.

FAQ

No. Test mode changes configuration only — the same settings PostgreSQL documents as non-durable settings, plus file_copy_method and two noise-reduction knobs. Query execution, WAL, checkpointing, and crash-handling code are identical to a production launch. What changes is the durability contract, not the engine.
Two reasons: tmpfs cannot reflink, so copy-on-write clones degrade to full copies, and a ramdisk’s fixed allocation turns “spill to disk” into swap thrashing. With fsync = off, a normal filesystem’s page cache gives you the in-memory behavior for free, with graceful writeback under pressure.
Yes. The loopback-image recipe needs only mount privileges (a privileged container or a host-prepared mount). If you can’t get a reflink filesystem in CI, everything still works on ext4/overlayfs — clones become fast in-kernel copies, so you lose the O(1)-in-template-size property but keep all the durability and memory-first behavior.
Yes — pass the preset as flags or mount a test.conf and add -c include=... to the container command. See Run Pgrust with Docker for how configuration is passed. For the copy-on-write clone path, the data volume must live on a reflink-capable filesystem (APFS/XFS/btrfs on the host).