knext
Learn knext

7 · A database that sleeps

Deploy knext's Beta scale-to-zero Postgres, built on Neon's open-source storage and compute, so ACME's database consumes zero compute while idle and wakes on the first connection.

An app that sleeps behind a database that never does is only half the saving. This chapter closes the other half.

Beta. The scale-to-zero database is shipped and usable, but it is not part of knext's stability promise. Bringing your own Postgres is the default. See Stability.

What the scale-to-zero database is

Native PostgreSQL that consumes zero compute while idle and wakes on the first client connection.

It runs Postgres 17 on Neon's open-source storage and compute, licensed Apache-2.0. knext adds the Kubernetes layer around it. It ships in the knext repository under packages/scale-zero-pg.

knext is not affiliated with or endorsed by Neon. knext runs Neon's open-source components itself, and knext alone maintains this deployment. That is one reason it ships as Beta.

How it's built

  • Storage (Neon open source). Safekeepers hold the durable write-ahead log. A pageserver serves pages and offloads them to object storage (MinIO by default, or any S3-compatible store). The storage plane never scales to zero.
  • Compute (Neon open source). Standard Postgres 17 processes with Neon's extension. A compute holds no data. It starts, attaches to its timeline, and fetches pages lazily. When idle, it stops.
  • Wake path (knext). A gateway speaks the Postgres wire protocol, so a database connection wakes the compute. Knative's activator only handles HTTP, so it cannot do this.

What knext adds

AdditionWhat it does
Wake-on-connect gatewayA client connection wakes a sleeping compute. The gateway holds the connection, replays the startup, and puts the compute back to sleep after an idle window.
AppDatabase operatorProvisions a database, or a branch per app, and delivers its DATABASE_URL as a Kubernetes Secret that the app binds.
Zone operatorReplicates data between zone databases with eventual consistency, for multi-zone setups.
Read poolsScales read-only replicas from zero to N and exposes them as DATABASE_URL_RO.
FailoverA standby pageserver and a failover controller promote the standby if the primary pageserver fails.
Scheduled warm windowswarmSchedule on an AppDatabase keeps the database awake during set hours. Its effect on wake latency has not been measured on a cluster yet.
Self-enabled extensionsApps enable TimescaleDB and pgvector over their own DATABASE_URL. Compression and continuous aggregates are not supported.
SCRAM auth and TLSEvery per-app role uses SCRAM-SHA-256 from boot. The internal hop to the compute is TLS, and mutually authenticated for per-app databases.
Backup and restore drillsA scheduled backup job copies the storage bucket to a second object store. The restore runbook is drilled.
AlertingPrometheus and Alertmanager rules cover the wake path, the storage plane, and the backup jobs.

Status and choices

  • Beta. Expect changes with notice. It is not covered by the stability promise.
  • Recommended default: bring your own Postgres. Use a CloudNativePG cluster in the same cluster, or any managed Postgres, and bind its DSN with DATABASE_URL.
  • Prefer not to run the storage layer yourself? Any managed Postgres works through DATABASE_URL, including providers that offer serverless or scale-to-zero plans. Nothing else in knext changes.

Deploy it

shell
kubectl apply -f packages/scale-zero-pg/deploy/

That creates a scale-zero-pg namespace containing durable WAL storage (three safekeepers at a 2/3 write quorum), a pageserver, object storage, a wake-on-connect gateway — and the Postgres compute itself at replicas: 0.

Zero replicas is not a broken deploy. The database is asleep and correct — it wakes on the first connection. This is minScale: 0, applied to Postgres.

Point ACME at the gateway

The gateway is what makes waking transparent, so ACME connects to it rather than to Postgres directly:

shell
kubectl create secret generic acme-db \
  --from-literal=DATABASE_URL='postgres://cloud_admin:cloud_admin@pggw.scale-zero-pg.svc.cluster.local.:55432/postgres?sslmode=disable'

Your knext.config.ts does not change at all. It still just names the Secret.

That is the payoff of binding by reference from chapter 6: swapping an always-on Postgres for a scale-to-zero one is a Secret change. No application change. No config change. No redeploy of anything but the Secret's consumers.

Watch both tiers sleep

Two terminals. The app:

shell
kubectl get pods -l serving.knative.dev/service=acme -w

The database:

shell
kubectl -n scale-zero-pg get pods -l app=compute -w

Leave it alone. The app pod goes after ~90 seconds; the database compute after ~60 seconds idle. Both lists empty. Nothing is running, and nothing is billing compute.

Now make one request that touches the database. Both wake: ACME cold-starts, its first query opens a connection, the gateway holds it while the compute scales 0→1, and the query returns.

What that cost

Measured in the project's benchmarks:

  • Cold wake (first connection after full idle): p50 3.72 s, p95 4.14 s over 20 runs on Oracle OKE (July 2026). On a single-node local cluster, the same wake measured 2.4 to 2.6 s.
  • Warm tier (an opt-in pod kept parked): 413 ms p50, 558 ms p95 over 20 runs on the local cluster. The OKE drill checks that a warm wake finishes under 1.5 s.

These are the database's own wake times, not end-to-end request times.

That is on top of ACME's own cold start. A first request after full idle pays both. Decide whether idle cost matters more than tail latency for your app — and note you can make that choice per tier: a warm app floor with a sleeping database, or the reverse.

The three things that surprise people

Every cold start is a fresh connection. A pod that terminated took its connection pool with it. Put a connection pooler (PgBouncer, pgcat) between ACME and Postgres and point the DSN at the pooler — otherwise a burst of waking pods opens a burst of connections and the database becomes the bottleneck instead of the app.

Connect timeouts must exceed the database's wake time. If your client gives up after 2 seconds and the database takes 2.5 to wake, you get an error that looks like a database fault and is actually a timeout you chose.

A burst of cold pods shares one wake. The pool single-flights the 0→1 wake: the first request triggers it, the rest wait on the same wake rather than each starting their own.

A database per app? scale-zero-pg supports DB-per-app multi-tenancy — each app on its own branch of one storage plane, sleeping and waking independently, declared with an AppDatabase custom resource. See packages/scale-zero-pg/docs/getting-started.md.

Chapter 8: Automate with CI →

On this page