The import contract — exactly what an import reproduces
An Adjoint golden is built by a logical import: we stream a dump of one database from your source into a fresh Postgres instance, then snapshot the result. A golden is therefore a faithful copy of your database's schema and data — not a byte-for-byte replica of your production server. This page is the precise contract: what carries over, what doesn't, and what fails loudly.
The mechanism
An import runs two Kubernetes Jobs in the adjoint-import namespace of your
data-plane cluster:
- Probe — connects to the source and measures
pg_database_size(current_database())for fail-fast sizing checks. - Load — streams, with no intermediate file:
pg_dump --format=custom --no-owner --no-privileges (from your source)
| pg_restore --no-owner --no-privileges --no-comments --role=app
The restore target is an ephemeral, single-instance Postgres cluster running
the stock CloudNativePG image for the --pg-major you specify. When the
restore completes, Adjoint takes a storage snapshot of that instance's volume
— that snapshot is the golden version. Clones are copy-on-write children
of it.
What carries over
- One database. The database you name in the source registration — schema, tables, data, indexes, constraints, views, functions, triggers, sequences (with current values), and large objects. Other databases on the same server are never read.
- Extensions installed in the source database, provided the extension is
available in the stock CloudNativePG Postgres image (the standard contrib
set plus a curated list including pgvector and pgAudit). The restore runs
CREATE EXTENSIONas superuser, so anything the image ships works without ceremony. - Data, exactly as dumped. The dump is a consistent snapshot as of the
moment
pg_dumpstarts, courtesy of Postgres MVCC — a live source stays live during import.
What does not carry over
- Roles and grants.
--no-owner --no-privilegeson both sides: every restored object is owned by theapprole, and noGRANT/REVOKEstatements are replayed. Developers connect with brokered per-instance credentials, not your production roles (see Connectivity for how those connections reach a clone). If your application depends on multiple roles or row-level-security policies tied to specific role names, recreate those in the clone (or tell us — this is a known sharp edge). - Cluster-wide objects. Tablespaces,
postgresql.confsettings,pg_hba.conf, replication slots, and other databases. A clone runs with CloudNativePG's defaults, not your production server configuration. - Extensions the image doesn't ship. If your database uses an extension
that isn't in the CloudNativePG image (PostGIS and TimescaleDB are the
common examples), the
CREATE EXTENSIONfails during restore and the import fails — with the extension error in the import's failure message. It fails loudly rather than producing a silently incomplete golden. If you need an extension we don't carry, contact [email protected]. - Planner statistics. The import does not currently run
ANALYZEafter restore, and the snapshot may be taken before autovacuum has re-analyzed large tables. First-query plans on a fresh clone can differ from production until statistics build; runANALYZEin your clone if you're investigating a plan. (Making this automatic is on the roadmap.) - Comments on extensions.
--no-commentsis set to accommodate RDS-managed sources.
Versions
--pg-major(default 16, minimum 13) selects the Postgres major for the import tooling and the golden. Set it to your source's major version. Adjoint does not verify the match — a mismatch surfaces as dump/restore errors at import time, not as a pre-flight check.- Importing across majors (e.g. a 14 source with
--pg-major 16) is how you'd rehearse a major-version upgrade, but it isn't a validated path — prefer matching majors.
Fidelity and validation
The import's success signal is the clean exit of the pg_dump | pg_restore
pipeline — Postgres's own tools, in their standard streaming configuration.
Adjoint does not currently run independent post-import validation (row-count
or checksum comparison against the source). If your acceptance process needs
that, compare source and clone directly — a clone of the new golden is the
natural place to run it.
The storage lifecycle, end to end
Everything lives on a single FSx for OpenZFS filesystem in your AWS account — there is no EBS anywhere in the import or clone path:
your source (e.g. RDS Postgres)
│ pg_dump | pg_restore (streamed, no intermediate file)
▼
ephemeral import Postgres
└── its data volume = an FSx OpenZFS child volume (import PVC)
│ storage snapshot, taken while quiesced
▼
FSx OpenZFS snapshot ═══ the golden version (immutable)
│ copy-on-write clone, one per instance
▼
FSx OpenZFS clone volumes (your sandboxes)
Two things are deliberately retained after an import completes and its ephemeral cluster is torn down:
- the FSx snapshot — it is the golden; deleting it would delete the golden, so the import teardown explicitly preserves it, and
- the import volume it was taken from — FSx does not allow deleting a
volume that still has snapshot children, so the volume is parked (its
Kubernetes PV flipped to
Retain) and labeled with the golden it backs.
Both are cleaned up when the golden itself is deleted
(adj catalog delete), and the whole filesystem — volumes, snapshots,
clones — is destroyed together at adj cluster deprovision.
What this means for how you use goldens
A golden behaves like your database for functional work: query correctness, schema migrations, data-dependent bugs, integration tests, and realistic volumes and distributions. It is not a performance replica of your production server — different compute, storage, cache, and configuration mean latency and throughput numbers don't transfer (see Benchmarks and methodology for what the clone substrate does deliver).
Source credentials, for the record: the username and password you supply at
adj source create are written directly into your data-plane cluster as
a Kubernetes Secret — they never transit or persist in Adjoint's control
plane. See Goldens and imports.