Loams Postgres
Serverless Postgres from Loams' hard fork of Neon, with storage in your bucket, copy-on-write branches and computes that start on demand and suspend when idle.
Loams Postgres is real Postgres with Neon's storage architecture. The Postgres process (the compute) keeps no durable state; the WAL goes to a quorum service; a pageserver turns it into layer files in your bucket and serves pages back. Because history lives in layers, a branch is a new timeline that starts at an LSN of its parent and copies nothing. Loams runs it from Loams Postgres, a hard fork of Neon (ostrium-labs/loams-postgres), and supplies the control plane Neon never open-sourced.
Use it when you want Postgres, not a Postgres-like engine: your extensions, your drivers and your ORM's migrations behave as they do on Postgres 17, and you want branches for previews, tests or agents, and idle databases that cost bucket bytes rather than a running server. Keep production data elsewhere for now. The development storage stack runs one stock safekeeper, default credentials and fsync = off in its compute spec, there is no connection pooler or TLS in front of the computes, and restore, the waker and the production WAL service are still being built.
Status
Loams Postgres is Available in Loams 0.1.0, the first release (2026-10-18). The feature table in docs/release/preview.md says: "The compose stack runs its storage and computes; --profile postgres-control adds pg-control and pg-runner, which provision computes and endpoints". In detail:
| Piece | In 0.1.0 | Notes |
|---|---|---|
Storage stack (deploy/loams-postgres-dev, included in the release compose): storage broker, one pageserver, one safekeeper, computes | Available | Images pinned by digest in deploy/pins/loams-postgres-images.toml: the fork's own signed release lp-2026.10.1. The safekeeper's WAL is on a volume, and loams upgrade drains it before recreating it |
pg-control (loams.postgres.v1) and pg-runner, --profile postgres-control, image loams-pg-control | Available | New in 0.1.0 as a published image (the profile was in the compose file of preview.2, with the image built locally) |
| Projects, branches at an LSN or a time, endpoints, roles, databases, suspend when idle | Available | Through loams.postgres.v1 |
PgDog front door, TLS, wake on connect, restore, loams-wal | Not shipped | Planned PG2c to PG2e |
One line in the release notes is behind the code: their known limitations still say the images are "pinned to Neon's last public build until the fork's own builds (NF1 Task 14)". NF1 Task 14 is done; the pin file names lp-2026.10.1, built from ostrium-labs/loams-postgres with the same Postgres revisions as Neon's last public build (16.9 and 17.5), which the two previews shipped.
This is not the engine's Postgres wire listener. The loams engine can serve its collections over the Postgres protocol (--pg-listen, the pgwire feature, plan PG1). That is read-only SQL over Loams' own data, through DataFusion. It never runs a Postgres process and nothing on this page applies to it.
Architecture
The pieces, with their names in the fork and in Loams (design §28 §3, §46 §6):
- Compute.
compute_ctland Postgres with Neon's extension and walproposer, imageloams-postgres-compute-v17(or-v16). It has a local file cache but no durable data. It gets its configuration (the spec) by calling the control plane. - WAL service. The compute's walproposer speaks Neon's safekeeper protocol (v3). In the 0.1.0 stack that is one stock Neon
safekeeper, which writes WAL to its disk and offloads it to the bucket undersafekeeper/. The production design replaces it withloams-wal(crateloams-safekeeper), described below. - Pageserver. It reads the WAL from the WAL service, keeps it as delta layers, periodically materialises image layers, uploads both to the bucket and answers
GetPage@LSNrequests. A storage broker carries the timeline positions between them. - Bucket. RustFS in the dev stack, or any S3-compatible store. It is the durable copy of every layer (see the bucket as the source of truth).
- Control plane.
pg-control, a role of theloamsbinary (crateloams-pg-control). It servesloams.postgres.v1, keeps the records, drives the pageserver's API throughloams-postgres(a plain HTTP client of the fork's components, with no Neon code dependency), answers each compute's spec request, and starts and stops computes.
How it works
Tenants, timelines and ids
The resource model is org → namespace → project → branch → endpoint → compute (D701). A project is one Neon tenant; a branch is one timeline; an endpoint is a stable address with its settings; a compute is a disposable Postgres process behind it, replaced rather than restarted. Tenant and timeline ids are derived from the Loams ids with SHA-256, so a retried create lands on the same tenant and a rename changes nothing in storage. Roles and databases belong to a branch and are inherited by its children, because they are catalog state inside the timeline.
Durability and the write path
A commit is acknowledged when the WAL service's quorum holds the WAL. The pageserver is not on the commit path; it catches up and is the source for reads. Layers reach the bucket after that. This is the shape every serious database on object storage uses: design §58's research notes that Neon, TiDB X and others keep commits off S3 and use the bucket for history and branches.
The production design changes who holds the quorum. On 2026-10-08 the owner ruled "remove safekeepers": loams-wal is to be the only WAL service at launch (D714), with a hot tier plus a 250 ms group commit of .lwal segments to the bucket. Two stores sit behind one trait: a TiKV hot tier (one fenced TiKV transaction per append) and Arm A, walproposer's own Paxos over three loams-wal acceptors on local NVMe, with TiKV holding only votes, leases and metadata. A bake-off against absolute targets (for example commit-1 p99 of at most 10 ms and bulk of at least 100 MB/s per timeline) picks one, and the owner rules (Q652). On the laptop runs recorded in §46 §9.2, Arm A passes the commit gates and fails bulk; the TiKV store is behind on both. None of it is in 0.1.0; the released safekeeper is a dev stand-in.
Branches
CreateBranch takes a parent and a branch point: the parent's head, an LSN, or a time the pageserver resolves to an LSN. The point must lie inside the parent's history retention (7 days by default in the design, D713), or the call answers lsn_out_of_retention. Underneath it is one pageserver call that creates a timeline with ancestor_timeline_id and ancestor_start_lsn. The cost does not depend on the database's size, which is what makes a branch per pull request, per test run or per agent workspace practical.
Two limits come with the model. Branches cannot be merged back: Postgres has no way to merge timelines (§28 non-goals). And DeleteBranch refuses a branch that has children.
Computes start on demand and suspend when idle
An endpoint's states are idle → starting → running → suspending → suspended, plus failed and deleting (D707). pg-control's endpoint reconciler suspends a compute after suspend_timeout_s with no activity: 300 seconds by default, 0 to never suspend. A new start creates a new compute with a new id and a new spec behind the same endpoint address. Measured on compose (PG2 ruling R17.3, 50 cold starts with images cached on a shared 14-CPU host), StartEndpoint on a suspended endpoint to the first SELECT 1 took a p50 of 1,417 ms and a p99 of 6,594 ms.
In 0.1.0 a start is an API call (StartEndpoint). Waking on a client's connection needs PgDog's queue and pg-control's waker, which is PG2 Task 26. PgDog's queueing behaviour was checked first (R18.2): a client waits in PgDog's pool while the backend is down and is served once the compute listens.
Who may start containers
Starting computes means holding a container runtime's socket, and whatever holds that socket can do anything its owner can. So neither the engine nor pg-control gets it (PG2 rulings R-REL.1 to R-REL.6). A separate process, pg-runner (loams pg-runner), holds it:
- it is on an internal compose network with no route out, which only
pg-controljoins, and it publishes no ports; - every call needs a 32-byte token it writes into a volume
pg-controlmounts read-only; - its API is seven typed calls, and it starts only the compute images pinned in the pin file, never a
neon-archivecopy, from compose files it renders itself: no mounts, no privileges, no host network; - it runs as uid 10001 with every capability dropped. Prefer a rootless Podman socket, so the runner can do at most what your user can.
A filtering Docker socket proxy was considered and rejected: it filters by API endpoint, so a proxy that lets compose create containers lets anything create a privileged one.
Key decisions and why
See the decisions page for the index.
| Decision | What it says | Why, as the log states it |
|---|---|---|
| D231 | Loams Postgres is a fork of Neon | Upstream was dormant since August 2025 and its compute Postgres was 16.9 and 17.5 |
| D232 | Loams' control plane is the primary control plane: it calls the storage APIs and serves the compute spec and the storage controller's hooks | Neon's control plane was never open source; the fork's components call it |
| D800, D820 | Ostrium Labs owns the fork as a product; then a hard fork named Loams Postgres, with no merges from neondatabase/* | neondatabase/neon has had no commit since 2026-08-31; a soft fork would wait on merges that never come |
| D822, D823 | Rebrand what is ours (repository, images, lp-YYYY.MM.N tags, crate loams-neon → loams-postgres); keep compatibility names (neon extension, neon.* GUCs, neon_superuser, on-disk formats) | Stored names are never renamed in place |
| D236, D708 | PgDog is the front door, unmodified, as a separate process, one per Loams namespace | PgDog is AGPL-3.0; it is never linked, vendored or patched |
| D701, D707 | The project/branch/endpoint/compute model; computes as disposable processes with a 300 s default suspend timeout | Stable addresses across compute replacement; idempotent retries |
| D713 | Branches are copy-on-write timelines at an LSN or time inside retention; restore is branch-and-swap with a backup branch | Neon's storage gives PITR as branching; restores stay reversible |
| D714 | loams-wal is the only WAL service at launch; stock safekeepers are removed | The owner's directive of 2026-10-08; it reverses D233, which made the switch conditional on a benchmark gate |
| D1057 | TiKV and loams-wal stay separate by role | TiKV pays a timestamp round trip, a Raft write and an apply per append: fine for records, wrong for a byte log |
What was rejected. Neon for the showcase apps' own databases (D149, replaced by CloudNativePG in D230). Keeping a soft fork that follows upstream (D802 and D811, superseded by D820 and D821). Neon's own proxy and autoscaler VMs (NeonVM needs KVM nodes that many clusters lack, §46 §7.4). And for the WAL, a pure bucket log: the spike measured 22 ms p50 and 148 ms p99, an order of magnitude over safekeepers (D234).
The fork, in practice
Design §51 and the NF1 plan turn "we own it" into work:
- Builds and supply chain. GitHub Actions in the fork build
loams-postgres-storageandloams-postgres-compute-v<N>for amd64 and arm64, with SLSA provenance, an SPDX SBOM and a keyless cosign signature, verified in Loams' CI.inferno(CDDL) is built out of every binary, and the private REST broker dependency stays a stub. - One pin file.
deploy/pins/loams-postgres-images.tomlis the only place digests and supported majors are written; a script generates and checks every consumer. - Postgres minors. The fork now merges
postgres/postgresminors itself. 16.15 and 17.11 are merged and gated; a format question in 17.11 (upstream's new visibility-map block references in heap WAL records) was ruled on: the fork keeps Neon's record format, with a test that guards it. They ship inlp-2026.11.0(NF1 Task 17). - Majors. 17 is supported, 16 is maintenance only, 18 when its gate passes, 19 as a beta after upstream's GA (D813).
What is compatible
- Postgres versions. 17.5 (default) and 16.9 in
lp-2026.10.1, the same revisions as Neon's last public build, which the previews shipped. The API takespg_version; 0 means the server's default, 17. - It is Postgres. The compute runs PostgreSQL with Neon's patches, so the wire protocol, SQL, catalog and client behaviour are Postgres 17's. Users get
neon_superuser, notsuperuser, in the design, soCOPY … PROGRAM, untrusted languages and server file access are not available. - Tested in this repository. CI's Postgres jobs drive the pinned storage and real computes (
pg2-e2e.yml);pg-control's tests talk to computes withtokio-postgres0.7, and the PgDog checks usedpsql17. The driver and ORM matrix the design lists (libpq 17, psycopg 3, asyncpg, node-postgres, postgres.js, pgjdbc, Npgsql, pgx, sqlx; Prisma, Drizzle, SQLAlchemy, Django, Rails, Hibernate, TypeORM, Flyway) andpg_regressthrough PgDog are PG2 Tasks 29 and 55 and have not run. - Extensions. Whatever the pinned compute image carries today. The managed catalogue with licence checks (
compute/loams-extensions.toml) is NF1 Tasks 20 and 21. - PgDog. v0.1.60, pinned by digest, AGPL-3.0, verified for queueing, SCRAM verifiers in
users.tomlandRELOAD(PG2 Task 18). It does not yet front the computes in any stack.
How it compares
Neon and the managed services below have run in production for years; Loams Postgres had its first release on 2026-10-18. The table is about design and what exists today.
| Loams Postgres | Neon | Supabase | Amazon Aurora PostgreSQL and RDS | |
|---|---|---|---|---|
| Relationship | A hard fork of Neon's open-source code | The original, now a Databricks company | Postgres plus a platform (auth, storage, realtime, functions) | AWS-managed Postgres |
| Storage | Pageserver layers in your bucket; WAL quorum | The same architecture, operated by Neon | Postgres on a disk per project | Aurora: AWS's distributed storage; RDS: block volumes |
| Branching | Copy-on-write timelines at an LSN or time | Copy-on-write branches, the same model | Branches are separate instances seeded from migrations | Aurora fast clones; RDS snapshots and restores |
| Scale to zero | Suspend when idle; start by API today, wake on connect planned | Yes, with its proxy waking computes | Free-tier projects pause; not per-connection | Aurora Serverless v2 scales capacity; RDS does not |
| Autoscaling | Designed with in-place pod resize (D712); not running | NeonVM-based autoscaling | Instance sizes | Aurora Serverless v2 |
| Pooling and front door | PgDog per namespace (planned) | Its own proxy and pooler | Its own pooler | RDS Proxy |
| Control plane | Open source, Apache-2.0, in Loams | Closed | Mostly open source | Closed |
| Where it runs | Your machines and bucket | Neon's cloud | Supabase's cloud, or self-hosted | AWS only |
| Maturity | 0.1.0, first release | Production | Production | Production |
Where the alternatives are better today: Neon has the same storage design with years of operation, a proxy that wakes computes on connect, autoscaling, and point-in-time restore that works now. Supabase gives you a whole application backend around Postgres, which Loams Postgres does not try to be. Aurora and RDS are the conservative choice on AWS, with HA, backups and support that Loams 0.1.0 does not have. Loams Postgres' case: Neon's architecture with an open control plane, on your own infrastructure and your own bucket, maintained independently after Neon stopped developing in the open, and sharing an API, identity and console with the rest of Loams.
What is missing, and what is planned
From the PG2 plan (docs/plans/2026-10-08-pg2-postgres-production.md) and NF1:
- Not served yet:
RestoreBranch(PG2 Task 44),IssueConnectCredential(Task 25),GetConnectionInfo(Tasks 20 and 25) andUpgradeProject(Task 51) answer "not built" with the task that builds them. - PgDog in front (PG2c): the per-namespace deployments and the
loams-gatewaySNI listener (Task 22), TLS on both hops (Task 23), SCRAM roles end to end (Task 24), the waker for wake on connect (Task 26), health and read routing (Task 27). Until then clients connect to an endpoint's published port directly. loams-walas the WAL (PG2d, Tasks 30 to 43, launch-critical): the in-process interpreted sender, broker publication,bulkthroughput, TLS, HA and fencing, offload and archive restore, nemesis runs, the store bake-off, and migrating dev data off the stock safekeeper.- Operations (PG2e): bucket versioning and the restore drill, metrics and alerts, quotas, the Helm chart and Kubernetes end to end, storage HA, a security review, the performance gate.
- Single node and desktop (PG2f):
loams dev --postgresand the desktop on the API. - Self-hosted stack limits today:
pg-controlkeeps its records in a local file on one node; its link topg-runneris plain text on its internal network until PG2 Task 23; the dev storage stack uses default credentials, no TLS andfsync = offin the compute spec. - Postgres 18 and 19 (NF1 Tasks 24 to 35) and the extension catalogue (Tasks 20 to 23).
Try it
The release stack includes the storage stack. Follow docs/release/preview.md to create .env, then start everything:
cd loams/deploy/release
docker compose up -dWith the control plane (deploy/release/README.md). Set CONTAINER_SOCKET and CONTAINER_SOCKET_GID in .env (rootless Podman: CONTAINER_SOCKET=$XDG_RUNTIME_DIR/podman/podman.sock written out, and CONTAINER_SOCKET_GID=0), mint an API key, and start pg-control and pg-runner:
echo '{"keys": []}' > secrets/pg-api-keys.json
out=$(docker compose run --rm --no-deps pg-control pg-api-key \
--subject you@example.com --grant admin:namespace:default)
jq '{keys: [.entry]}' <<<"$out" > secrets/pg-api-keys.json
jq -r .api_key <<<"$out" # shown only this once
docker compose --profile postgres-control up -d pg-control pg-runnerpg-control serves loams.postgres.v1 on the host's loopback interface, port 7680 (PG_CONTROL_PORT). With PG_CONTROL set to that address and KEY to the key printed above:
curl -sS -H "authorization: Bearer $KEY" -H 'content-type: application/json' \
-d '{"namespace": "default", "name": "app", "idempotency_key": "1"}' \
"http://$PG_CONTROL/loams.postgres.v1.PostgresService/CreateProject"Endpoints are published on PG_ENDPOINT_BIND (loopback by default), on ports 55500 to 55999. Before docker compose down of the stack, suspend or delete the endpoints (or docker compose -p loams-pg-<namespace> down), or the stack's network stays in use.
By hand, on the storage stack alone (deploy/loams-postgres-dev/README.md), playing the control plane's part with curl. The pageserver's management API is on host port 9898 and the compute on 55433; set PAGESERVER and PGHOST to the host they are published on:
docker compose up -d rustfs create-bucket storage_broker pageserver safekeeper1
# Create a tenant and its first timeline through the pageserver API.
export TENANT_ID=$(openssl rand -hex 16) TIMELINE_ID=$(openssl rand -hex 16)
curl -X PUT -H 'Content-Type: application/json' \
-d '{"mode":"AttachedSingle","generation":1,"tenant_conf":{}}' \
$PAGESERVER/v1/tenant/$TENANT_ID/location_config
curl -X POST -H 'Content-Type: application/json' \
-d "{\"new_timeline_id\":\"$TIMELINE_ID\",\"pg_version\":17}" \
$PAGESERVER/v1/tenant/$TENANT_ID/timeline/
docker compose up -d compute1
until pg_isready -q -h $PGHOST -p 55433; do sleep 1; done
PGPASSWORD=cloud_admin psql -h $PGHOST -p 55433 -U cloud_admin postgresBranch it: a new timeline whose ancestor is the first. It takes tens of milliseconds and copies no data. Add "ancestor_start_lsn": "<lsn>" to branch from a point in the past.
export BRANCH_TIMELINE_ID=$(openssl rand -hex 16)
curl -X POST -H 'Content-Type: application/json' \
-d "{\"new_timeline_id\":\"$BRANCH_TIMELINE_ID\",\"ancestor_timeline_id\":\"$TIMELINE_ID\"}" \
$PAGESERVER/v1/tenant/$TENANT_ID/timeline/
docker compose --profile branch up -d compute2
until pg_isready -q -h $PGHOST -p 55434; do sleep 1; done
PGPASSWORD=cloud_admin psql -h $PGHOST -p 55434 -U cloud_admin postgresSource
- Design: §28 Loams Postgres, §46 in production, §51 the hard fork, §58 data access and HTAP §6 and §8.
- Plans: PG2, Postgres in production, NF1, the Neon fork; for the engine's separate Postgres wire listener, PG1.
- Crates:
loams-postgres(client of the fork's components),loams-pg-control(control plane, reconcilers, runtimes, PgDog rendering),loams-safekeeper(loams-wal),loams-wal-decoder(WAL decoding against the fork's code),loams-pgdog-sync(the PgDog config sidecar). - Deploy:
deploy/loams-postgres-dev,deploy/release/README.md,deploy/pins/loams-postgres-images.toml. - The fork:
ostrium-labs/loams-postgres.