Build your own Neon
Serverless Postgres that scales to zero — but yours. pg-fc gives every app, agent, or branch its own Postgres in its own Firecracker micro-VM. It boots on the first connect, stops when nobody is using it, and wakes on the next query. One pooler endpoint, as many databases as your hardware holds, on machines you own.
The database name is the provisioning API
pg-fc is two pieces: a Postgres guest image that
boots straight into Postgres, and
pg-vm-pool, a Postgres wire-protocol
pooler listening on :6432. The
database name in a client’s connection string
picks the database. The pooler finds, restarts, or
creates the VM behind it and splices the connection
straight through.
Connect with a new name
A dbname the pooler has never
seen gets a fresh micro-VM, its own data
disk, and a clean initdb. No
console, no provisioning call.
One VM per database
Each database runs in its own VM with its own disk. Postgres settings are regenerated at every boot from the VM’s actual RAM, vCPUs and disk.
Clients wait, not fail
When a VM is full, new clients wait at the
pooler for a free backend slot instead of
getting too many clients. A
client that vanishes releases its slot.
# Start the pooler (listens on 127.0.0.1:6432) target/release/pg-vm-pool # The dbname selects the VM, creating it on first use psql "host=127.0.0.1 port=6432 user=postgres dbname=tenant1" -> VM pg-tenant1 psql "host=127.0.0.1 port=6432 user=postgres dbname=tenant2" -> VM pg-tenant2
Idle databases cost disk, not RAM
A database that nobody is querying doesn’t need a running VM. pg-fc stops idle VMs and, if you turn them on, moves cold ones down through cheaper storage tiers. The next connect brings them back.
Warm
A running VM. The pooler splices the connection immediately.
Stopped
The VM is stopped and its data disk kept. A connect costs a VM start and Postgres startup — typically under a second to a few seconds.
Compacted and frozen
A trimmed, zstd-compressed disk image, or
a pg_dump file, with the VM
deleted. A connect restores onto a fresh
VM.
Archived
An object in S3 or an S3-compatible bucket such as MinIO or R2. A connect downloads it, then restores as above.
- Idle stop by default. With no tier settings at all, pg-fc only stops idle VMs: after 900 seconds with no connections, jittered ±15% per database. Every tier below warm is opt-in.
- A two-speed reaper. Databases whose last bring-up was fast get a shorter 60-second idle timeout. When the host slows down, restarts stop counting as fast and the reaper backs off on its own.
-
No lost commits. The pooler
runs
CHECKPOINTbefore every idle stop. - Disks that grow. Data disks are thin-provisioned: the guest starts with a 2 GB filesystem and grows it online as the database grows. Turn on device growth and a data device is doubled at idle stop, up to a ceiling you set (100 GB by default).
-
Warm spares. Keep a number of
pre-booted,
initdb-complete spare VMs for new databases to claim, so a cold bring-up takes seconds instead of a full create andinitdb. -
Always-on when you need it.
List latency-critical databases in
PG_VM_POOL_KEEPALIVE_SCHEMASand they are never idle-stopped or offloaded.
Credentials, replicas, and a dashboard
Dedicated databases
Hand an app or a customer its own role and password. A dedicated credential can open only its own database and cannot create VMs. Revoking it keeps the data.
Cross-host replication
Replicate a dedicated database continuously to a peer pg-fc node, using logical replication by default. Promote and detach are operator actions, not automatic failover.
Dashboard and JSON API
An optional dashboard and JSON API inside the pooler: every VM’s state, host metrics, archives, events, log tails, webhook alerts, and runtime config changes without a restart.
# Provision a dedicated database (password is generated if omitted) curl -u admin:$DASH_PASS -X POST http://127.0.0.1:34199/api/databases \ -H 'content-type: application/json' -d '{"database":"acme"}' 201 {"database":"acme","username":"acme","password":"…","status":"provisioning",…} # The client connects normally, over TLS psql "host=pg.example.com port=6432 user=acme dbname=acme sslmode=require"
Set PG_VM_POOL_PASSWORD and the pooler
challenges every client before it dials a VM. TLS
terminates at the pooler, and certificate renewals
need no restart.
A KVM box and four steps
-
Get a KVM-capable Linux server.
Most modern Linux hardware is. Make sure your
user can reach
/dev/kvmand install Firecracker. -
Install heyvm and run its daemon.
The pooler drives VMs through the heyvm API on
:34099. -
Build the Postgres image.
heyvm mvm buildturns pg-fc’s Dockerfile into a bootable image namedpg. -
Build and run the pooler.
cargo build --release, run it under supervisord or systemd, and connect on:6432.
# Build the guest image heyvm mvm build --local-only -f Dockerfile.pg18 --name pg # Run the heyvm API the pooler talks to heyvm --api --port 34099 # First connect creates the first database psql "host=127.0.0.1 port=6432 user=postgres dbname=tenant1" heyvm list
The full walkthrough is in Build Your Own Serverless Postgres → Running the rest of Heyo Web Services alongside it? Start with the HWS installation docs →
Build your own cloud
A Postgres per app, agent, or branch that stops when idle and wakes on the next connect. Your hardware, your data, your rules.