Skip to content
pilots
Dashboard
All pages

Databases

A database on pilots is a machine running the stock image, with a volume and a snapshot schedule. That is a genuinely good place to run one, and it is not the same thing as a managed database.

Adding one

pilot add postgres
pilot add postgres --durable-volume --to web
pilot add redis --name cache

pilot add writes a compose entry, a volume, a snapshot policy with retention, a health gate, a private address and a generated password into your own project. The fragment lands in your file where you can read it, change it and commit it. Nothing about it is a second system: the same rollout, the same gate, the same volume, the same snapshots as any other service.

The password is generated on your machine and stored in your local secret store. It never travels: the compose file references it by name, and the deploy seals it on the way through. There is no route that returns a database password, which is why opening a session is a command you run rather than a call an agent makes.

How much you can lose, exactly

Mode Loses at most Costs
wal-archive, the Postgres default A minute of writes on a host failure. Nothing per commit. Segments ship to the volume every minute.
--durable-volume Nothing. An object-storage round trip on every commit.
Every other engine Nothing. The data directory is on the volume, so a write is durable before it is acknowledged.

The default is the first, because a minute of exposure on a machine that has not crashed beats putting object-storage latency in the commit path of every write. Choose the other one deliberately. pilot add prints which one you chose, every time, because choosing it silently would make this paragraph a formality.

Using it

pilot db connect
pilot metrics postgres

pilot db connect opens the engine's own client over a tunnel, or inside the machine if you have neither the client nor the password. pilot metrics reports what the engine says about itself: connections against the limit, cache hits, commits against rollbacks.

Two addresses, and which is which

Variable Port For
DATABASE_URL 6432 The application. A pooler in transaction mode, so hundreds of client connections sit on a handful of server ones.
DATABASE_URL_DIRECT 5432 Migrations, LISTEN and NOTIFY, session advisory locks, temporary tables, and anything meant to outlive a transaction.

Recovery

pilot db restore postgres --to 2026-09-20T14:31:00Z

Point-in-time recovery replays the write-ahead log onto the newest base backup at or before the moment you name, as a NEW private service on a FORK of the archive volume. The original keeps serving throughout, so you can query both and compare before pointing anything at either. How far back you can go is bounded by the oldest base backup the archive still holds.

The release health gate is the restore gate: the new service only becomes ready once the engine accepts connections, so a recovery that never reaches its target never becomes a running release.

High availability, and whose it is

pilot db ha enable postgres
pilot db ha status postgres
  • Only a Postgres written by pilot add can become a cluster. The label that allows several volumes with one writer each is written once and cannot be added to a hand-written service.
  • Each node gets its own volume and lands on a different host where the fleet has one. The address and the connection string do not change: every node runs a proxy on the published port that follows whichever node the cluster manager says is primary, checked every second. No leader is recorded anywhere to read, deliberately, because a replicated row would be stale exactly when it mattered.
  • The smallest sensible cluster is two Postgres machines and three coordination members, each with its own volume, all of them billed.
  • A failover resets connections that were open to the old primary, which is physics rather than policy: the process they were talking to is gone.
  • Turning it off keeps the FIRST node and destroys the rest with their volumes, so it is refused while any other node is primary.

Which half is ours

The configuration, the volume, the snapshots, the rescue onto another host and the placement of nodes are ours. The choice of leader, the tuning, the upgrade and the three in the morning are yours. What we operate is the unabridged version, including the recovery times and what is not automatic.

If you need more than that, use a managed database and point the connection variable at it. An application here talking to a database elsewhere is an ordinary configuration rather than a workaround.