knext deploys an existing Next.js app to Kubernetes as a Knative Service, built on the official Next.js Deployment Adapter API — so your app scales to zero when idle and scales back up on the next request, on any Kubernetes cluster with Knative Serving installed.
knext is not a general-purpose PaaS. It does not run arbitrary containers or frameworks; it is a focused, Next.js-specific deployment tool.
The credential (in progress): knext runs the official Next.js compatibility test suite
across four runtime x build-system combinations (Node/Bun x Turbopack/Webpack). Its credential is
14 consecutive green independent runs of that suite per combination (three scheduled runs a
day, at least two hours apart), against a frozen release reference — that window is in progress, not complete, and knext does not claim verified status
until it is. v1.0 is the semver-stable release; it ships ahead of the credential, from the same
bytes the credential runs execute. See docs/compat-matrix.md for
the current, evidence-gated status of each cell — do not rely on a number quoted here, which would
go stale; that file is the live source of truth. See
docs/release/v1.0.0.md for
the credential's status and which parts of knext the v1.0 contract covers (stability tiers).
npx @getknext/core create my-app
cd my-app
# fill in registry, bucket, and database settings in knext.config.ts
npx @getknext/core deployThat builds your app with the official Next.js adapter, pushes the image, and applies a NextApp
custom resource that the knext operator reconciles into a Knative Service. See
Quick Start below for the full walkthrough, including cluster prerequisites.
- Why Knative?
- How It Works: Next.js Adapter
- Features
- Quick Start
- Configuration Reference
- Caching & Adapters
- Multi-Cloud Deployment
- Architecture
- File Manager Demo
- Development
Knative is a Kubernetes-based platform that provides serverless capabilities without lock-in to any specific cloud provider. Knative runs on any Kubernetes cluster (GKE, EKS, AKS, or on-premise).
| Feature | Knative (via knext) |
|---|---|
| Portability | Any Kubernetes cluster (portable by design; verified end-to-end on GKE and OKE, kind in CI, and the core operator/CLI deploy path validated end-to-end on EKS; AKS and OpenShift are not yet live-validated) |
| Scale-to-Zero | Idle apps run zero pods; the platform brings a pod back up on the next request |
| Autoscaling | Configurable (KPA/HPA) |
| Cold starts | Optimized for scale-to-zero — the build pipeline includes bytecode compile caching to remove V8 compilation work from a cold wake. End-to-end wake time is dominated by cluster scheduling and is environment-dependent; see docs/benchmarks/scale-to-zero-oke.md and the tuning cold start guide for measured numbers and their conditions — we do not publish a single number as a universal guarantee |
| Container control | Full Docker/OCI image access |
| Networking | Full Kubernetes networking; a default-on NetworkPolicy is reconciled per app, with enforcement depending on your cluster's CNI |
| Cost model | Per-pod-second, not per-invocation |
- Multi-cloud deployments requiring platform portability
- On-premise or air-gapped environments
- Hybrid architectures with existing Kubernetes workloads
- Cost optimization for spiky or low-traffic apps that don't need an always-on pod
- No global edge network or CDN — knext deploys to the cluster(s) you run.
- No edge middleware or Partial Prerendering (PPR) — partly gated on upstream Next.js, not solely a knext gap.
- The compiled single-executable ("vinext") build target is Beta, stays Beta in every release, and is outside the compatibility credential's scope.
kn-next integrates via the official Next.js Adapter API (NextAdapter), registered through the top-level adapterPath config (Next.js 16.2+; on 16.0.x–16.1.x it lives under experimental.adapterPath). A standard next build with output: 'standalone' produces a self-contained Node server; the adapter hooks into the build to wire Knative-specific behavior — no custom compiler, no fork of Next.js.
flowchart LR
subgraph Input
A["Next.js App<br/>(App Router)"]
end
subgraph Build
B["next build<br/>output: 'standalone'<br/>+ NextAdapter"]
end
subgraph Output
C["Standalone Server<br/>(distroless Node)"]
end
A --> B --> C
C --> D["Static Assets<br/>→ CDN/GCS"]
C --> E["Server (server.js)<br/>→ Docker"]
C --> F["Adapters<br/>(GCS/S3/Redis)"]
style A fill:#3b82f6,color:#fff
style B fill:#8b5cf6,color:#fff
style C fill:#10b981,color:#fff
style D fill:#f59e0b,color:#fff
style E fill:#f59e0b,color:#fff
style F fill:#f59e0b,color:#fff
modifyConfig→ defaultsoutputto'standalone'(an explicitoutput: 'export'is left alone and the CLI refuses it — a static export is not a server deployment, and knext deploys a Next.js server) and injects the RediscacheHandlerat build timeonBuildComplete→ uploadsstaticFiles+prerendersto cloud storage, keyed bybuildId- Standalone server →
next buildemits a self-containedserver.jsthat runs on plain Node 20+ (and Bun vianode:httpcompat) — no bundler-specific runtime - Cold-start bytecode caching →
NODE_COMPILE_CACHEpersists the V8 code cache so scaled-from-zero pods skip re-compilation (the mechanism behind Vercel Fluid)
- ✅ Official Next.js Adapter API –
next buildstandalone, no fork, runs on Node 20+ and Bun - ✅ V8 Bytecode Caching – the compile cache is populated at build time and baked into the image (Vercel-Fluid-style), so it is present from the very first cold pod on a stock cluster. Nothing to enable, no volume, no storage class, and no limit on how wide the app scales
- ✅ Fluid Compute – Scale-to-zero, high concurrency, auto-scaling
- ✅ Distributed Caching – Redis-backed caching with automatic tag invalidation
- 🟡 Portable by design – verified end-to-end on GKE and OKE (kind in CI), and the core operator/CLI deploy path is also verified end-to-end on EKS (
knext deploy→ NextApp CR → operator reconciles → Knative Service → live200route). The EKS data-plane steps (S3 asset upload, ISR/tag invalidation) are documented but not each validated there; AKS and OpenShift are not yet live-validated. See Multi-Cloud Portability and the docs-site support matrix. - ✅ Cache Monitoring – Built-in cache event dashboard
- ✅ Single-Command Deploy – Automated build, push, and deploy
- ✅ Monorepo Ready – Turborepo for efficient builds
- ✅ Kubernetes Operator – Declarative
NextAppCRD for GitOps-style deployment
What Next.js features does knext support? See the evidence-gated compatibility matrix — every ✅ is backed by a red-on-fail check, and a guard test fails CI on any overclaim. knext's dispatched runs of the official Next.js deploy-test suite pass on all four default cells (Node/Bun x Turbopack/Webpack) — 1101 of 1101 tests at Next.js v16.3.8 — with a small, documented set of exclusions. That is evidence, not the credential, which is still in progress (see above). The matrix has the exact scope and the run evidence.
Benchmarks measured on Knative Serving with scale-to-zero (
minScale: 0) on GKE. Cold start speed is achieved through Knative resource caching and V8 bytecode caching (a compile cache baked into the image at build time).
With minScale: 0, pods terminate after 10 seconds of inactivity and must be provisioned fresh on next request. The figures below are from a single early GKE run and are not representative — cold start is scheduling-dominated and environment-dependent. Our multi-run OKE measurements on a quiet two-node cluster, with a light app and the image already on the node, gave medians of about 1.9 to 2.4 s from the first request to a full page (scheduling-bound; individual wakes can take several times the median; your numbers will differ) — treat that as the honest figure and these as one favorable data point. See scale-to-zero & cold starts for the method and the table, and benchmarks for the older multi-run record.
| Metric | Value (single early GKE run — not representative) |
|---|---|
| Time to First Byte (TTFB) | 0.66s |
| Total Response Time | 0.92s |
| Pod Provisioning | Container goes from Pending → Running in ~1s |
| Metric | Value |
|---|---|
| Time to First Byte (TTFB) | 0.58s |
| Total Response Time | 0.80s |
seq 1 100000 | xargs -n1 -P100 -I {} curl -s -o /dev/null -w "%{time_total}\n" \
"http://file-manager.default.136.111.227.195.sslip.io/audit"| Metric | Value |
|---|---|
| Total Requests | 100,000 |
| Concurrency | 100 parallel workers |
| Average Response Time | 0.521s |
| Requests/Second (RPS) | ~192 req/s |
| Per-Pod RPS | ~96 req/s (with maxScale: 2) |
| Total Test Duration | ~521s (~8.7 min) |
| Error Rate | 0% (all requests successful) |
The Dockerfile uses a 2-stage build producing a small Alpine image that ships one executable:
- Build Stage –
oven/bun:1.4.2-alpinerunsvite build(Vite/rolldown → a Nitro.output), then compiles that output into a single self-contained binary withbun build --compile --minify --bytecode. sharp's native addon is staged beside it intonative/, with a sha256 manifest derived from the lockfile. - Runtime Stage –
alpine:3.22with onlylibstdc++andlibgcc, running the compiled binary directly. No Node, no npm, no package manager, and no bytecode cache to mount.
Bytecode is compiled into the binary at build time, as one unit, so every pod — including the very first cold start — starts from precompiled bytecode rather than parsing and JIT-compiling JavaScript. It needs no volume, no PVC, and no cluster feature flags.
(This section previously described a node:22 builder emitting output: 'standalone' and a distroless Node runtime reading a NODE_COMPILE_CACHE directory. That is the older shape and no longer what this Dockerfile builds.)
What that buys, measured: on a fresh local process with the binary already page-cached, this vinext executable's process boot measured 61 ms median against the Node standalone's 884 ms, at higher steady-state throughput (1103 vs 630 req/s) — both local-machine numbers, not a cluster figure. A truly cold local spawn is closer to 1.8 s. On a real Knative cluster (OKE, N=7 paired cold cycles), scale-from-zero was a tie: node-standalone 3610 ms vs vinext single-exec 3401 ms median — cold start there is dominated by Knative activation/scheduling/container-create, not process boot, so it does not differentiate the two build targets. The per-file bytecode pass that used to run for runtime: bun standalone builds is retired: it traded throughput for cold start (537 req/s), and the whole-bundle compile wins both axes.
Running the legacy standalone shape on Bun? One mechanism still applies:
- Runtime transpiler cache: Bun's transpiler cache is derived from the image-baked
NODE_COMPILE_CACHEpath. Bun persists the transpiled source of large modules (roughly 50 KB and up) so a pod skips re-transpiling them — about 20% faster alone, and it picks up whatever the build-time pass didn't cover. Per-pod only: it no longer survives scale-to-zero, because the persistent volume that used to carry it across cold starts has been removed. Fail-open: if the cache directory is missing or unwritable, Bun simply serves without it. SetBUN_RUNTIME_TRANSPILER_CACHE_PATH=0to opt out.
New to knext? Follow the step-by-step Quickstart guide — from an empty cluster (operator install included) to a deployed, scale-to-zero app.
- Node.js 22.18+ (24 LTS recommended) or Bun 1.2+
- Docker with buildx
- kubectl configured for your cluster
- Cloud storage bucket (GCS/S3/Azure/MinIO)
- Redis instance (optional, for cache invalidation)
bun installCreate knext.config.ts in your app directory:
import type { KnativeNextConfig } from '@getknext/core';
const config: KnativeNextConfig = {
name: 'my-app',
// Storage for static assets
storage: {
provider: 'gcs', // 'gcs' | 's3' | 'azure' | 'minio'
bucket: 'my-assets-bucket',
publicUrl: 'https://storage.googleapis.com/my-assets-bucket',
},
// Cache for fast tag-based invalidation
cache: {
provider: 'redis',
url: 'redis://redis.default.svc.cluster.local.:6379',
keyPrefix: 'my-app',
},
// Container registry
registry: 'gcr.io/my-project',
// Knative autoscaling
scaling: {
minScale: 1, // Keep 1 pod always running (no cold starts)
maxScale: 10, // Scale up to 10 pods
},
};
export default config;# Using the deploy script
./deploy.sh
# Or using the CLI
npx @getknext/core deployThis single command:
- Runs
next build(output: 'standalone') with the knext adapter - Syncs static assets + prerenders to cloud storage (keyed by
buildId) - Builds & pushes the runtime image, pinned by digest
- Applies a
NextAppcustom resource — the only thing the CLI writes to your cluster - The operator reconciles it into a scale-to-zero Knative Service
interface KnativeNextConfig {
// Required: Application name (used for K8s resources)
name: string;
// Required: Container registry URL
registry: string;
// Required: Storage configuration
storage: StorageConfig;
// Optional: Cache configuration (default: none)
cache?: CacheConfig;
// Optional: ISR revalidation queue (default: none)
queue?: QueueConfig;
// Optional: Deploy infrastructure services alongside app
infrastructure?: InfrastructureConfig;
// Optional: Knative autoscaling settings
scaling?: ScalingConfig;
// Optional: bring-your-own database binding (no provisioning)
database?: DatabaseConfig;
}Configure where static assets are stored:
// Google Cloud Storage
storage: {
provider: 'gcs',
bucket: 'my-bucket',
publicUrl: 'https://storage.googleapis.com/my-bucket',
}
// AWS S3
storage: {
provider: 's3',
bucket: 'my-bucket',
region: 'us-east-1',
publicUrl: 'https://my-bucket.s3.amazonaws.com',
}
// Azure Blob Storage
storage: {
provider: 'azure',
bucket: 'my-container', // container name
region: 'eastus',
}
// MinIO (S3-compatible, self-hosted)
storage: {
provider: 'minio',
bucket: 'my-bucket',
endpoint: 'http://minio.default.svc.cluster.local:9000',
accessKey: 'minioadmin',
secretKey: 'minioadmin',
}Configure distributed cache for tag-based invalidation:
// Redis (recommended)
cache: {
provider: 'redis', // the only implemented cache provider
url: 'redis://redis:6379',
keyPrefix: 'my-app', // Optional namespace
tls: true, // Enable TLS
}Configure background revalidation queue:
// Kafka (Knative Eventing compatible)
queue: {
provider: 'kafka',
brokerUrl: 'kafka:9092',
topic: 'my-app-revalidation',
}
// No queue (synchronous revalidation)
queue: {
provider: 'none',
}Auto-deploy PostgreSQL, Redis, or MinIO alongside your app:
infrastructure: {
postgres: {
enabled: true,
version: '16',
storage: '10Gi',
},
redis: {
enabled: true,
version: '7',
},
minio: {
enabled: true,
storage: '20Gi',
accessKey: 'admin',
secretKey: 'secretpassword',
},
}scaling: {
minScale: 0, // Scale to zero (save costs, but cold starts)
maxScale: 10, // Maximum pods
}
// For low-latency requirements
scaling: {
minScale: 1, // Always keep 1 pod running
}Beyond minScale/maxScale/cpuRequest/memoryRequest/cpuLimit/memoryLimit,
scaling also exposes the Knative autoscaling knobs the NextApp resource
supports. All are optional; leaving one unset omits it from the emitted
NextApp resource and the operator's own default applies unchanged:
scaling: {
// Concurrent requests per pod before Knative adds another pod.
// Operator default: 20.
containerConcurrency: 20,
// Per-pod DATABASE_URL connection-pool maximum. When set, the operator
// enforces `maxScale × poolMax ≤ 80` (the app connection budget) and
// caps the runtime pg pool to match.
poolMax: 5,
// Scheduled warm-floor windows: during each window the app is
// pre-warmed to a floor of `replicas` pods so a known traffic peak
// doesn't pay a cold start. Cron expressions are evaluated in `timezone`
// (default "UTC"). Outside every window the floor reverts to `minScale`.
warmSchedule: [
{ start: '0 8 * * 1-5', end: '0 20 * * 1-5', replicas: 2, timezone: 'UTC' },
],
// Whether the Knative activator stays in the request path as a burst
// buffer for an unpredicted spike. -1 = always keep it in the path
// (max burst tolerance); >= 0 = the buffered request count before it's
// removed. Unset = the Knative cluster default (200) applies.
targetBurstCapacity: -1,
// How fast the Knative Pod Autoscaler reacts to an unpredicted surge,
// as a percentage of the stable window (1-100; smaller = more
// reactive). Unset = the Knative cluster default (10%) applies.
panicWindowPercentage: 10,
// The overshoot (as a percentage of the steady-state target) that trips
// panic mode (>= 110; lower = trips sooner). Unset = the Knative
// cluster default (200%) applies.
panicThresholdPercentage: 200,
}knext is engine-agnostic and provisions no database — bring your own Postgres (or
whatever engine your app uses) and bind it into knext.config.ts. database binds an
existing Kubernetes Secret's connection string into the app as DATABASE_URL (and
optionally a read-only replica DSN as DATABASE_URL_RO):
database: {
// Binds an existing Secret as the app's DATABASE_URL.
// `key` defaults to "DATABASE_URL" when omitted.
secretRef: { name: 'storefront-db' },
// Optional: bind a read-only DSN as DATABASE_URL_RO. Requires secretRef.
// A single Secret carrying both keys binds with roSecretRef: { name: <same> }.
// `key` defaults to "DATABASE_URL_RO" when omitted.
roSecretRef: { name: 'storefront-db' },
}Only the Secret name and key are ever written to this config file — never a
connection string or credential. The DSN itself lives exclusively in the Kubernetes Secret.
Omitting database entirely leaves the app's database wiring unchanged (back-compat).
flowchart TB
subgraph Storage["Cloud Storage (GCS/S3/Azure/MinIO)"]
direction TB
S1["Static Assets (JS/CSS)"]
S2["Public Files"]
end
subgraph Redis["Redis (ISR & Tag Cache)"]
direction TB
R1["ISR page & fetch cache"]
R2["tag:{tagName} → Set of keys"]
R3["Fast O(1) tag-based invalidation"]
end
%% The two tiers are independent stores, so there is no data flow to draw
%% between them. This invisible link exists only to fix their rank order:
%% with no edge at all, `flowchart TB` has nothing to rank on and packs the
%% subgraphs side by side in reverse declaration order.
Storage ~~~ Redis
style Storage fill:#4f46e5,color:#fff
style Redis fill:#dc2626,color:#fff
| Adapter | File | Purpose |
|---|---|---|
| Node Server | node-server.ts |
HTTP server wrapper for Knative |
| Bytecode Metrics | bytecode-metrics.ts |
Prometheus metrics for the node server |
Next.js emits shared-cache directives on prerendered/ISR responses (e.g.
s-maxage=2, stale-while-revalidate=…, or s-maxage=31536000 for pages that
never revalidate). Those directives are meant to be consumed by a deployment
platform's cache layer — they are not what a browser should see. On a knext
deployment, Knative serves clients directly, so the runtime entry
(@getknext/core/internal/node-server) normalizes them by default, exactly like
the official reference adapter does in its serving layer:
- Applies to
GET/HEADresponses only. immutablevalues (hashed static assets) pass through untouched.- Any
Cache-Controlcontainings-maxage=becomespublic, max-age=0, must-revalidate— browsers always revalidate, and ISR freshness is handled server-side by the incremental cache. - The pages-router fallback-shell
private, no-cache, no-store, …value is normalized the same way, only when the response carries thex-nextjs-cachemarker and is not a/_next/data/request. - Everything else (custom headers you set in routes/middleware) is untouched.
When to disable it: if you front knext with your own CDN or shared cache
that honors s-maxage (Cloudflare, CloudFront, Fastly, Varnish, …), the
default normalization strips s-maxage before your CDN sees it, so the CDN
stops shared-caching ISR pages. In that setup, disable normalization and let
your CDN consume the origin directives:
// knext.config.ts — plain, non-secret env vars ride the NextApp resource's env field
export default {
// ...
env: {
KNEXT_CACHE_CONTROL_NORMALIZE: "0",
},
};or directly on the NextApp resource:
spec:
env:
KNEXT_CACHE_CONTROL_NORMALIZE: "0"(Secret-backed values still go through spec.secrets / the secrets config
option — env is for plain configuration flags only. A Dockerfile
ENV KNEXT_CACHE_CONTROL_NORMALIZE=0 also works for per-image settings.)
Any value other than 0 — including unset — leaves normalization on.
On Bun ≤ 1.3.x only, the runtime preloads a guard that advertises
Connection: close on every response, working around a Bun node:http bug
that resets reused keep-alive sockets (fixed upstream in Bun ≥ 1.4).
Tradeoff: clients — including the Knative activator — open a fresh connection
per request while the guard is active, trading keep-alive reuse for
correctness on affected Bun versions. It self-disables on fixed Bun versions
and never loads under Node; set KNEXT_BUN_KEEPALIVE_GUARD=0 to disable it
(or 1 to force it on a Bun version the ceiling says is fixed).
# Invalidate by tag
curl -X POST http://your-app/api/cache/invalidate \
-H "Content-Type: application/json" \
-d '{"tag": "products"}'
# Response
{
"success": true,
"message": "Cache invalidated for tag: products",
"timestamp": "2026-02-05T10:00:00.000Z"
}Connect to /api/cache/events for Server-Sent Events:
const events = new EventSource('/api/cache/events');
events.onmessage = (e) => {
const event = JSON.parse(e.data);
// { type: 'HIT', source: 'redis', key: '/products', durationMs: 2 }
};Verification status: knext is portable by design. End-to-end deploys are verified on GKE and OKE (and on kind in CI), and the core deploy path is validated on EKS; the AKS example below is a design-correct configuration, not yet validated on a live cluster. See the docs-site support matrix. Before deploying to a non-GKE cluster, read Multi-Cloud Portability for the per-cloud prerequisites (ingress-class, StorageClass, LoadBalancer/gateway IP, and build-host CLI tools).
const config: KnativeNextConfig = {
name: 'my-app',
storage: {
provider: 'gcs',
bucket: 'my-gcs-bucket',
publicUrl: 'https://storage.googleapis.com/my-gcs-bucket',
},
cache: {
provider: 'redis',
url: 'redis://redis.default.svc.cluster.local.:6379',
},
registry: 'gcr.io/my-project',
};GKE Prerequisites:
# Ensure GCS write access (one of these)
gcloud container node-pools create storage-enabled \
--scopes="gke-default,storage-full"
# Or use Workload Identity for production
gcloud iam service-accounts add-iam-policy-binding ...const config: KnativeNextConfig = {
name: 'my-app',
storage: {
provider: 's3',
bucket: 'my-s3-bucket',
region: 'us-east-1',
publicUrl: 'https://my-s3-bucket.s3.amazonaws.com',
},
cache: {
provider: 'redis',
url: 'redis://my-elasticache.xxx.cache.amazonaws.com:6379',
tls: true,
},
registry: '123456789.dkr.ecr.us-east-1.amazonaws.com',
};EKS Prerequisites:
# Configure IAM roles for S3 access
eksctl create iamserviceaccount \
--name default \
--namespace default \
--cluster my-cluster \
--attach-policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccessconst config: KnativeNextConfig = {
name: 'my-app',
storage: {
provider: 'azure',
bucket: 'my-container',
region: 'eastus',
},
cache: {
provider: 'redis',
url: 'redis://my-redis.redis.cache.windows.net:6380',
tls: true,
},
registry: 'myregistry.azurecr.io',
};const config: KnativeNextConfig = {
name: 'my-app',
storage: {
provider: 'minio',
bucket: 'assets',
endpoint: 'http://minio.storage.svc.cluster.local:9000',
accessKey: process.env.MINIO_ACCESS_KEY,
secretKey: process.env.MINIO_SECRET_KEY,
},
cache: {
provider: 'redis',
url: 'redis://redis.cache.svc.cluster.local.:6379',
},
registry: 'registry.internal.local:5000',
infrastructure: {
minio: { enabled: true, storage: '50Gi' },
redis: { enabled: true },
},
};See docs/ARCHITECTURE.md for detailed diagrams including:
- System architecture (Mermaid)
- Request/response flow
- Cache invalidation flow
- Deployment pipeline
flowchart TB
A["Next.js App<br/>(App Router)"] --> B["next build<br/>(standalone + adapter)"]
B --> C["Static Assets<br/>(Cloud CDN)"]
B --> D["Server Function<br/>(Docker)"]
B --> E["Cache Data<br/>(Storage+Redis)"]
D --> F["Knative Service"]
subgraph Cluster["Kubernetes Cluster"]
F
E
end
style A fill:#3b82f6,color:#fff
style B fill:#8b5cf6,color:#fff
style C fill:#f59e0b,color:#fff
style D fill:#10b981,color:#fff
style E fill:#dc2626,color:#fff
style F fill:#06b6d4,color:#fff
style Cluster fill:#1e293b,color:#fff
├── apps/
│ └── file-manager/ # Example Next.js 16 app
│ ├── knext.config.ts # App configuration
│ ├── deploy.sh # Deployment script
│ └── src/app/ # App Router pages
│
├── packages/
│ ├── kn-next/ # Core framework package
│ │ ├── src/adapters/ # GCS, Redis, Kafka adapters
│ │ ├── src/generators/ # Manifest generators
│ │ └── src/cli/ # CLI tools (build, deploy)
│ └── lib/ # Shared utilities
│
└── docs/
└── ARCHITECTURE.md # Detailed architecture
The example app demonstrates all framework capabilities:
- File upload/download with MinIO/GCS
- PostgreSQL metadata storage
- Real-time cache monitoring
- Paginated audit logs with infinite scroll
- Tag-based cache invalidation
- Cache testing playground with various strategies
| Page | URL | Description |
|---|---|---|
| Home | / |
File listing |
| Dashboard | /dashboard |
Statistics |
| Audit Logs | /audit |
Activity log |
| Cache Monitor | /cache |
Real-time events |
| Cache Tests | /cache-tests |
Testing playground |
| Test | URL | Strategy |
|---|---|---|
| Time-based | /cache-tests/time-based |
revalidate: 60 |
| On-demand | /cache-tests/on-demand |
revalidateTag() |
| Fetch cache | /cache-tests/fetch-cache |
force-cache |
| Parallel | /cache-tests/parallel |
Concurrent fetches |
| Nested | /cache-tests/nested |
Layout caching |
| Dynamic/Static | /cache-tests/dynamic-static |
Rendering modes |
cd apps/file-manager
bun run dev# Full deployment with defaults
npx @getknext/core deploy
# Or step-by-step
npx @getknext/core build # next build (standalone) + adapter
npx @getknext/core deploy # Deploy to cluster
npx @getknext/core cleanup # Remove from clusternpx @getknext/core deploy [options]| Option | Short | Description |
|---|---|---|
--registry <url> |
-r |
Override container registry |
--bucket <name> |
-b |
Override storage bucket |
--tag <tag> |
-t |
Image tag (default: timestamp) |
--namespace <ns> |
-n |
Kubernetes namespace (default: default) |
--skip-build |
Skip the next build step |
|
--skip-upload |
Skip asset upload to storage | |
--skip-infra |
Skip infrastructure deployment | |
--dry-run |
Generate manifests without deploying | |
--help |
-h |
Show help |
These environment variables can be used instead of CLI flags:
| Variable | Description |
|---|---|
KN_REGISTRY |
Container registry URL |
KN_BUCKET |
Storage bucket name |
KN_IMAGE_TAG |
Docker image tag |
KN_NAMESPACE |
Kubernetes namespace |
KN_REDIS_URL |
Redis connection URL (overrides config) |
KN_DATABASE_URL |
Database connection URL (overrides config) |
GitHub Actions:
- name: Deploy to Knative
env:
KN_REGISTRY: gcr.io/${{ secrets.GCP_PROJECT }}
KN_IMAGE_TAG: ${{ github.sha }}
KN_NAMESPACE: production
KN_REDIS_URL: ${{ secrets.REDIS_URL }}
KN_DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npx @getknext/core deployGitLab CI:
deploy:
script:
- npx @getknext/core deploy --tag $CI_COMMIT_SHA --namespace production
variables:
KN_REGISTRY: gcr.io/my-project
KN_REDIS_URL: $REDIS_URLProduction with specific tag:
npx @getknext/core deploy --tag v1.2.3 --namespace productionPreview manifest only:
npx @getknext/core deploy --dry-runApache License 2.0 — consistent with the Go operator source headers and the Knative/Kubernetes ecosystem this project builds on. The Apache-2.0 patent grant matters for an infrastructure project intended for production adoption.