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;| Provider | Cloud | Uploads via | Notes |
|---|---|---|---|
gcs | GKE / Google Cloud | gsutil | Google Cloud Storage. |
s3 | EKS / AWS | aws | S3, or any S3-compatible store via endpoint. |
minio | Any / self-hosted | mc | MinIO or S3-compatible clusters. |
azure | AKS / Azure | az | Azure 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
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 buildkn-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.