Scale‑to‑zero Next.js, on Knative.
knext is a deployment adapter that runs Next.js on the official Adapter API with true scale-to-zero — pods drop to nothing when idle and wake on the first request, with bytecode-cached cold starts. One operator. Any cloud. No lock-in.
New to Kubernetes? The tutorial starts with the six words you need, then deploys ACME — a small storefront — and gives it a database.
Official adapter, not a fork
Built on Next.js's official Deployment Adapter API (NextAdapter, output:'standalone'). No reverse-engineered runtime. Validated against the official Next.js deploy-mode e2e suite — 778 tests, zero failures on Node, re-verified nightly. Scope and exclusions: verified compatibility.
True scale-to-zero
Knative KPA + activator: idle services drop to 0 replicas and wake on demand through the activator. You pay for requests, not idle containers.
Bytecode-cached cold starts
A V8 compile cache is baked into your image at build time — every cold pod skips JS recompilation, with nothing to enable. Or run Bun with build-time bytecode precompilation (−47% measured boot). Self-hosted, on your cluster.
Multi-cloud, no lock-in
One Go operator + a NextApp CRD, on any Kubernetes cluster running Knative Serving. Verified end-to-end on GKE, OKE and kind; portable to EKS, AKS and bare-metal by design, not yet certified there. Object storage via gcs, s3, azure or minio. Your manifests are yours.
Your app is a resource.
Describe the deployment once as a NextApp custom resource. The operator — the single source of truth for cluster state — builds the Knative Service, wires scale-to-zero, and rejects any image that is not digest-pinned.
- 01 push a digest-pinned image
- 02 apply the NextApp CR
- 03 operator reconciles → reachable URL, scaled to zero
# the whole deployment, declared
apiVersion: apps.kn-next.dev/v1alpha1
kind: NextApp
metadata:
name: acme
spec:
image: registry/acme@sha256:9f1c... # digest-pinned (required)
scaling:
minScale: 0 # scale to zero
maxScale: 20
# bytecode cache is baked into the image — nothing to configureBring your own database.
knext is engine-agnostic: it provisions nothing and manages no database. You bring your own Postgres — a CloudNativePG cluster in the same cluster, or a managed / serverless provider — and knext binds its DSN from a Kubernetes Secret into your pods as DATABASE_URL. Swapping providers is a Secret change: no app or CR schema change.
- 01 put your Postgres DSN in a Kubernetes
Secret - 02 reference it with
spec.database.secretRef— the operator wiresDATABASE_URL(+ optionalroSecretRef→DATABASE_URL_RO) - 03 query it with typed
@getknext/db(Drizzle) — writer / bounded-stale reader split - 04 front Postgres with a connection pooler (PgBouncer / pgcat) so scale-to-zero fan-out never storms the database
# bring your own Postgres — bind an existing Secret
apiVersion: apps.kn-next.dev/v1alpha1
kind: NextApp
metadata:
name: acme
spec:
image: registry/acme@sha256:9f1c...
scaling:
minScale: 0 # the app scales to zero
database:
secretRef: { name: acme-db } # → DATABASE_URL from a K8s Secret
roSecretRef: { name: acme-db } # optional → DATABASE_URL_RO
# point the DSN at a pooler, not the primary