knext

Multi-cloud deploy

The object-storage providers knext supports across GKE, EKS, AKS, and OKE.

knext deploys on any Kubernetes cluster with Knative Serving — GKE, EKS, AKS, OKE, or bare-metal. With object storage configured, build output (static assets) is uploaded to an object store keyed by build ID, and served from assetPrefix. Object storage is optional — see Starting without object storage below.

Supported storage providers

The kn-next config validator accepts exactly these storage.provider values:

const SUPPORTED_STORAGE_PROVIDERS = ['gcs', 's3', 'minio', 'azure'] as const;
ProviderCloudUploads viaNotes
gcsGKE / Google CloudgsutilGoogle Cloud Storage.
s3EKS / AWSawsS3, or any S3-compatible store via endpoint.
minioAny / self-hostedmcMinIO or S3-compatible clusters.
azureAKS / AzureazAzure Blob Storage; bucket is the blob container.

Each provider shells out to that cloud's CLI, which must be installed and authenticated in the build environment (for example AZURE_STORAGE_ACCOUNT / az login for Azure, application-default credentials for gsutil, an IAM role or keys for aws). knext never handles cloud credentials itself — it invokes the already-authenticated CLI.

Config shape

kn-next.config.ts
storage: {
  provider: 's3',                 // gcs | s3 | minio | azure
  bucket: 'acme-assets',    // bucket name — for azure, the blob container
  publicUrl: 'https://cdn.example.com/acme-assets',
  // s3-compatible stores: set region / endpoint as needed
},

publicUrl is the public/CDN URL assets are served from; knext wires it into assetPrefix so the browser fetches static chunks from object storage rather than the pod.

Starting without object storage

You can leave storage out of kn-next.config.ts entirely. Your app then serves its own static files from the container image — exactly how next start works — and every deploy prints a notice saying it is running in this mode. Nothing else changes: deploys, scale-to-zero, rollback, and next/image all keep working. It is the right starting point for a new app; kn-next create scaffolds the storage block commented out for exactly this reason.

What you give up until you add a bucket:

  • CDN offload — static files are served by your app's pods (and count against their request concurrency) instead of a bucket or CDN.
  • Asset retention across deploys — each image carries only its own build's files. Page navigations stay safe (a browser on an old build detects the skew and loads the new one), but a script or style fetch already in flight when the old revision is fully retired has nothing to fall back to. With a bucket, old builds' assets outlive their revisions — see Skew protection.
  • A shared image-optimization cache — optimized image variants live per pod and are re-created on pod start instead of persisting in the bucket.

Rollback actually gets simpler in this mode: a pinned revision serves assets baked into its own image, so they can never be cleaned up out from under it.

Add the storage block when static delivery speed matters, or when your traffic holds long-lived sessions open across deploys — pick a provider from the table above and redeploy. kn-next doctor reports which mode your app is in.

Custom Dockerfiles that rebuild in-image

The scaffolded Dockerfile copies your app's already-built output into the image — next build/vite build runs once, on the host, where kn-next deploy has already set ASSET_PREFIX and the deploy's build id in the environment. If you maintain your own Dockerfile that instead runs the build a second time inside the image (for example a monorepo image that does bun install + vite build in a builder stage), that in-image build never sees those two values unless you wire them through explicitly:

# Declare the ARGs...
ARG NEXT_DEPLOYMENT_ID
ARG ASSET_PREFIX
# ...and export them into the build step's env BEFORE running your build command.
ENV NEXT_DEPLOYMENT_ID=${NEXT_DEPLOYMENT_ID}
ENV ASSET_PREFIX=${ASSET_PREFIX}

RUN ./node_modules/.bin/vite build

kn-next deploy/kn-next preview pass both automatically as --build-args to any Dockerfile that rebuilds in-image — an unused build-arg is silently ignored by Docker, so nothing breaks until you add the two lines above. Without them:

  • static assets are served from the image at relative paths instead of your configured bucket, so the uploaded objects go unreferenced;
  • the image's build id diverges from the deploy tag, breaking skew protection and the asset-retention cleanup that key off it.

The post-build check, its scope, and the opt-out

For a Dockerfile that rebuilds in-image and uploads assets, kn-next deploy checks the ACTUAL pushed image after the build — not just your Dockerfile's ARG declarations, which can be present and unused. It briefly extracts the image's server artifact and static output (docker create / docker cp, no traffic ever served) and confirms the static build namespace matches the deploy tag, and — in storage mode — that the server artifact really does reference your configured ASSET_PREFIX. A mismatch fails the deploy with a clear message before touching the cluster.

This check runs only for a Dockerfile that both rebuilds in-image and differs from the scaffolded template — the unmodified scaffolded Dockerfile/Dockerfile.vinext-node (which copy already-built host output, so there is nothing to catch) are skipped automatically, at no extra cost to every other deploy.

The check assumes your custom Dockerfile follows the same layout the scaffolded templates use — the compiled server at /app/server, or the uncompiled entry at /app/.output/server/index.mjs. If your image's layout genuinely differs and the check reports a false failure, pass --skip-image-lockstep-check to kn-next deploy to opt out. Understand the tradeoff before you do: skipping it means the CLI can no longer catch a broken ASSET_PREFIX/build-id lock-step before it reaches your cluster.

On this page