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 addcan 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.