Guides
Postgres
Create a Postgres database, connect an app or your laptop to it, branch it for an experiment, fork it from a point in time, restore it in place, and set when it sleeps.
A Rytiv database is PostgreSQL 16 on a machine of its own, with its data on a volume and a continuous archive of its write-ahead log. Only apps you attach can reach it, always over TLS. Every database can be branched (a copy-on-write copy of its disk, ready in seconds) and forked (a full copy from its archive, from now or from a moment in the past), so an agent can change a schema without touching the original.
1. Create a database
Section titled “1. Create a database”ry postgres create appdbry postgres create appdb --size 20 --locale en-UScurl -X POST "https://api.rytiv.app/v1/resources" \ -H "Authorization: Bearer $RYTIV_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "appdb", "kind": "postgres"}'const res = await fetch("https://api.rytiv.app/v1/resources", { method: "POST", headers: { Authorization: `Bearer ${process.env.RYTIV_TOKEN}`, "Content-Type": "application/json" }, body: JSON.stringify({ name: "appdb", kind: "postgres" }),});const db = await res.json(); // db.url is shown in this answer onlyimport os, requests
db = requests.post( "https://api.rytiv.app/v1/resources", headers={"Authorization": f"Bearer {os.environ['RYTIV_TOKEN']}"}, json={"name": "appdb", "kind": "postgres"},).json() # db["url"] is shown in this answer onlyYour agent uses the tool create_postgres, which never returns the password.
| Field | Type | Default | Notes |
|---|---|---|---|
name |
string | required | Like an app name, unique across Rytiv |
kind |
string | required | postgres |
memoryMb |
integer | 1024 |
128 to 16384 |
sizeGb |
integer | 10 |
Size of the data volume, 4 to 500 |
locale |
string | de-DE |
ICU locale for sorting and comparing text, such as en-US. Fixed once the database exists. |
durability |
string | standard |
standard: one machine. host-redundant: a standby on a second host (API only; a redundant database cannot be branched or forked). |
suspendPolicy |
object | suspend after 300 s | When the database sleeps, see Sleep. {"mode": "off"} keeps it always on. |
POST /v1/resources answers 201 with name, kind, port, secretKey (DATABASE_URL) and url. The url contains the password and is returned only here; the CLI shows it only with --json. You do not need to store it if you attach the database to an app: the app gets it as a secret.
2. Connect from an app
Section titled “2. Connect from an app”ry attach appdb --to webry deploy webPOST /v1/apps/:name/attach with {"resource": "appdb"} gives the app a managed secret DATABASE_URL. A running machine keeps the environment it started with, so deploy the app again (or stop and start its machine) after attaching. The URL looks like this:
postgres://postgres:<password>@appdb.internal:5432/appdb?sslmode=verify-full&sslrootcert=/etc/rytiv/ca/postgres.crtappdb.internalresolves only inside apps that are attached to the database. Everyone else cannot reach it.- The database accepts TLS connections only. The certificate authority is at
/etc/rytiv/ca/postgres.crtin every machine, soverify-fullworks without setup. - The database name is the resource name with
-replaced by_.
3. Connect from your computer
Section titled “3. Connect from your computer”A database has no public address. Open a tunnel with the CLI and connect to the local end:
ry proxy appdb 15432:5432psql -h 127.0.0.1 -p 15432 -U postgres appdbThe first connection wakes a sleeping database.
4. Branch, fork, restore
Section titled “4. Branch, fork, restore”Branch a database
Section titled “Branch a database”A branch is a copy-on-write copy of the database as it is now, taken on its host. It is ready in seconds, takes almost no space at first, and is a database of its own with its own password: writes to the branch never reach the source.
ry postgres branch appdb appdb-try --expires-in 1dcurl -X POST "https://api.rytiv.app/v1/resources/appdb/branch" \ -H "Authorization: Bearer $RYTIV_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "appdb-try", "ttlSeconds": 86400}'await fetch("https://api.rytiv.app/v1/resources/appdb/branch", { method: "POST", headers: { Authorization: `Bearer ${process.env.RYTIV_TOKEN}`, "Content-Type": "application/json" }, body: JSON.stringify({ name: "appdb-try", ttlSeconds: 86400 }),});import os, requests
requests.post( "https://api.rytiv.app/v1/resources/appdb/branch", headers={"Authorization": f"Bearer {os.environ['RYTIV_TOKEN']}"}, json={"name": "appdb-try", "ttlSeconds": 86400},)| Field | Type | Notes |
|---|---|---|
name |
string | Name of the new database |
ttlSeconds |
integer | Delete the branch after this long, 60 to 2592000 (30 days) |
expiresAt |
string | Or: delete it at this moment (ISO 8601). Not together with ttlSeconds. |
ephemeral |
boolean | Ready faster, without an archive of its own, and lost if its host fails |
The answer is 202 with state: "restoring". Poll GET /v1/resources/:name until state is ready; the CLI waits for you. A database can have up to 15 branches.
This is where an agent changes a schema or tries a migration. Through MCP, create_branch makes one, and execute_sql only writes when you name a branch of the database; on the database itself it runs read-only.
Fork from a point in time
Section titled “Fork from a point in time”A fork is a full copy made from the database’s archive, on any host. It takes longer than a branch, minutes for a large database, and can start from any moment the archive covers (7 days):
ry postgres fork appdb appdb-yesterday --at 2026-10-19T09:00:00ZPOST /v1/resources/:name/fork takes name, an optional asOf (ISO 8601) and the same ttlSeconds or expiresAt as a branch.
Connection URL of a copy
Section titled “Connection URL of a copy”A branch or fork has its own password. Attach it to an app like any database, or fetch its URL once:
curl -X POST "https://api.rytiv.app/v1/resources/appdb-try/url" \ -H "Authorization: Bearer $RYTIV_TOKEN"The answer is {name, url}. A second call answers 409 with url-handed-out: the URL is handed out exactly once. For the original database, the URL came with the create answer and this route answers 409 with not-a-copy. The CLI asks for it right after a branch or fork is ready and prints it with --json.
Restore in place
Section titled “Restore in place”ry postgres restore appdb --at 2026-10-19T09:00:00ZPOST /v1/resources/:name/restore with {asOf} puts a database back to that moment. Name, URL and password stay the same; open connections end once, at the switch. The state before the restore is kept as a branch named <name>-old-<timestamp>, or the name you pass as backupName. The CLI asks before it does this; pass --yes in scripts.
Expiry
Section titled “Expiry”PATCH /v1/resources/:name with ttlSeconds or expiresAt sets when a branch or fork is deleted; "expiresAt": null keeps it. Only copies can expire.
ry postgres expire appdb-try --in 12hry postgres expire appdb-try --never5. Sleep
Section titled “5. Sleep”A standalone database carries a sleep policy: by default it is suspended after 300 seconds without a running query, an open transaction or traffic, and stopped a day after that; the next connection wakes it. A redundant database never sleeps.
ry postgres sleep appdb --after 10mry postgres sleep appdb --offry postgres create reports --always-onPATCH /v1/resources/:name with suspendPolicy changes the rule; {"mode": "off"} keeps the database always on. The policy is stored with the database and shown by ry postgres list; whether a region’s hosts act on it is part of the region’s configuration, so do not build on a database being asleep.
List and delete
Section titled “List and delete”| Method | Path | What it does |
|---|---|---|
GET |
/v1/resources |
Your databases, with state (restoring, ready, failed), sourceName and expiresAt for copies. Never a URL. |
GET |
/v1/resources/:name |
One database |
DELETE |
/v1/resources/:name |
Deletes the database, its machine and its volume, and detaches it from every app. It cannot be undone. |
ry postgres listry postgres destroy appdb-tryErrors you will meet
Section titled “Errors you will meet”| Status | Code | When |
|---|---|---|
409 |
name-taken |
The name is used somewhere on Rytiv |
400 |
name-reserved |
The name is reserved for the platform |
503 |
no-capacity |
No room right now, try again |
409 |
url-handed-out |
The URL of this copy was handed out already |
409 |
not-ready |
The copy is still restoring, or failed |
409 |
conflict |
The database already has 15 branches: delete one, or fork |
See Postgres in the API reference for every field, and the CLI reference for every flag of ry postgres.