> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pgrust.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Test Mode - Disposable, Memory-First Pgrust for Test Suites

> Run Pgrust as a throwaway test server: durability off, data in memory with graceful disk spill, and near-instant per-test databases via copy-on-write template clones.

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.

<Note>
  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](https://www.postgresql.org/docs/current/non-durability.html), plus a filesystem choice.
</Note>

<Warning>
  **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.
</Warning>

## How it works

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

| Layer                       | What it does                                                                             | What you do                                            |
| --------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| **1. Non-durable preset**   | Turns off fsync, synchronous commit, full-page writes, autovacuum, and JIT               | Include one config file                                |
| **2. Filesystem recipe**    | Makes the kernel page cache act as your in-memory filesystem, with copy-on-write support | Run one setup script (Linux only; macOS needs nothing) |
| **3. Copy-on-write clones** | `file_copy_method = clone` reflinks template databases instead of copying them           | Already in the config preset                           |

## Layer 1: the non-durable preset

Save this as `test.conf` and include it when starting the server:

```ini test.conf theme={null} theme={null}
# Durability off — PostgreSQL's "Non-Durable Settings", all of them
fsync = off
synchronous_commit = off
full_page_writes = off
wal_level = minimal
max_wal_senders = 0

# Background noise off
autovacuum = off
checkpoint_timeout = 1h
max_wal_size = 8GB          # never checkpoint from WAL volume mid-suite

# Test-shaped defaults
file_copy_method = clone     # copy-on-write template clones (layer 3)
jit = off                    # cost misestimates fire JIT on tiny test tables
shared_buffers = 128MB       # keeps template-clone checkpoints cheap
```

Start Pgrust with the preset either via an `include` line in `postgresql.conf`, or directly from your harness:

```bash theme={null} theme={null}
pgrust -D /path/to/test-data -c include=/path/to/test.conf ...
```

<Warning>
  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.
</Warning>

## 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*:

<Tabs>
  <Tab title="macOS">
    **Nothing to do.** APFS — the default filesystem — supports copy-on-write clones natively and already satisfies both properties. Put your test data directory anywhere.
  </Tab>

  <Tab title="Linux">
    Use **XFS with reflink support** (the default since xfsprogs 5.1, so any recently created XFS filesystem qualifies) or btrfs.

    If your root filesystem is ext4, carve out a loopback image — no repartitioning required:

    ```bash theme={null} theme={null}
    truncate -s 8G /var/tmp/pgtestfs.img
    mkfs.xfs -m reflink=1 /var/tmp/pgtestfs.img
    sudo mount -o loop,noatime /var/tmp/pgtestfs.img /mnt/pgtest
    ```

    Tear it down after the suite with `umount /mnt/pgtest && rm /var/tmp/pgtestfs.img`.

    <Tip>
      On ext4, `file_copy_method = clone` still works — the kernel performs an in-kernel copy instead of a reflink. Correct, and faster than a userspace copy loop, but not O(1). The reflink filesystem is what makes multi-gigabyte templates clone in milliseconds.
    </Tip>
  </Tab>
</Tabs>

## 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

<Steps>
  <Step title="Once per suite: create the cluster and seed a template">
    ```bash theme={null} theme={null}
    # Linux only: set up the reflink filesystem (see Layer 2)
    initdb -D /mnt/pgtest/data --no-locale --encoding UTF8 -U postgres
    pgrust -D /mnt/pgtest/data -c include=/path/to/test.conf ... &

    psql -U postgres <<'SQL'
    CREATE DATABASE tpl;
    SQL
    # then run migrations and seed data into tpl, and disconnect
    ```

    Sessions connected to a database block its use as a template, so disconnect from `tpl` once seeding finishes.
  </Step>

  <Step title="Per test: clone, run, drop">
    ```sql theme={null} theme={null}
    CREATE DATABASE t_42 TEMPLATE tpl STRATEGY FILE_COPY;  -- ~1-10 ms
    -- ... run the test against t_42 ...
    DROP DATABASE t_42 WITH (FORCE);
    ```

    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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

## 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](https://github.com/allaboutapps/integresql) pattern) — Pgrust needs no changes to support it.

## FAQ

<AccordionGroup>
  <Accordion title="Is test-mode Pgrust behaviorally different from production Pgrust?">
    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.
  </Accordion>

  <Accordion title="Why not just use tmpfs?">
    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.
  </Accordion>

  <Accordion title="Does this work in CI containers?">
    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.
  </Accordion>

  <Accordion title="Can I use test mode with the Docker image?">
    Yes — pass the preset as flags or mount a `test.conf` and add `-c include=...` to the container command. See [Run Pgrust with Docker](/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).
  </Accordion>
</AccordionGroup>
