Deploy Next.js on GCP: Cloud Run and GKE guide (2026)
Deploy Next.js on Google Cloud Platform with standalone output, Docker, Artifact Registry, Cloud Run, GKE, autoscaling, GitHub Actions, and production-safe caching.
TL;DR: The simplest way to deploy Next.js on GCP is to build a standalone Docker image, store it in Artifact Registry, and run it on Cloud Run. Choose GKE instead when your team already operates Kubernetes or needs pod-level control, sidecars, custom networking, or cluster scheduling. Both options support SSR, App Router streaming, Server Functions, image optimization, and route handlers.
Self-hosting Next.js gives you control over regions, networking, identity, autoscaling, and deployment policy. It also makes your team responsible for cache coordination, secrets, version skew, health checks, rollbacks, security, and production observability.
Cloud Run vs GKE for Next.js
Cloud Run is the default choice for most Next.js applications on Google Cloud. It deploys immutable container revisions, scales instances automatically, and removes the need to manage a Kubernetes control plane. GKE is appropriate when Next.js is one service inside an existing Kubernetes platform or needs controls that Cloud Run does not expose.
| Requirement | Cloud Run | GKE |
|---|---|---|
| Infrastructure management | Fully managed | Kubernetes cluster and workloads |
| Scale to zero | Built in | Requires additional platform design |
| Per-pod scheduling and sidecars | Limited container-level controls | Full Kubernetes controls |
| Deployment revisions and traffic splitting | Built in | Configure with Deployments and routing |
| Custom cluster networking | Managed networking options | Full VPC and Kubernetes network control |
| Operational overhead | Lower | Higher |
| Best fit | Most standalone Next.js services | Existing Kubernetes platforms |
Google also offers App Engine, Compute Engine, and static hosting options. This guide focuses on container deployments because they preserve the full Next.js server runtime and make the same image portable between Cloud Run and GKE.
What works when self-hosting Next.js on GCP?
According to the official
Next.js self-hosting guide,
a Node.js or Docker deployment supports App Router streaming, Server Functions,
route handlers, image optimization, Proxy, Cache Components, and the after()
API.
Static pages can be served through a CDN, while dynamic pages continue to run on the Next.js server. Image optimization runs at request time unless you use a custom loader or external image service.
A static export is different. output: "export" creates HTML, CSS, and
JavaScript that can be served without Node.js, but features requiring a server
runtime are unavailable. Use standalone output for SSR, authenticated pages,
dynamic rendering, server actions, and API routes.
Prerequisites
Before deploying, you need:
- A Next.js application with a working production build.
- A Google Cloud project with billing enabled.
- The Google Cloud CLI authenticated to that project.
- Artifact Registry, Cloud Build, and Cloud Run APIs enabled.
- Docker only if you plan to build the image locally.
- A runtime service account with only the permissions the app needs.
Set the active project and enable the required services:
Replace placeholders such as PROJECT_ID, REGION, and REPOSITORY throughout
the guide. Do not paste credentials into the Dockerfile, repository, image, or
build arguments.
Step 1: Enable Next.js standalone output
Next.js output-file tracing identifies the runtime files needed by each page and
the production server. Setting output: "standalone" copies those traced files
into .next/standalone and creates a minimal server.js.
The standalone directory does not include public or .next/static
automatically. The Dockerfile must copy them separately unless they are served
from object storage or a CDN.
In a monorepo, tracing starts from the Next.js project directory by default. If
the application imports workspace packages or files above that directory, set
outputFileTracingRoot to the monorepo root. Build the Docker image from the
same root so package manifests and workspace sources exist in the build context.
Step 2: Add a production Next.js Dockerfile
This multi-stage Dockerfile targets a single pnpm application. It installs locked dependencies, builds the standalone server, copies only runtime files, and runs as an unprivileged user.
Cloud Run injects a PORT environment variable and requires the ingress
container to listen on that port. The standalone Next.js server also needs
HOSTNAME=0.0.0.0; binding only to localhost prevents the platform from reaching
the process.
For npm or Yarn, replace the pnpm installation steps with the matching locked
install command. For a pnpm workspace, also copy pnpm-workspace.yaml, relevant
package manifests, and workspace source before installing and building.
Add a .dockerignore
Keep local dependencies, build output, secrets, and Git metadata out of the container context:
If the build intentionally reads a non-secret environment file, adjust the
pattern explicitly. Never place server secrets in NEXT_PUBLIC_* variables;
Next.js inlines those values into browser JavaScript during next build.
Step 3: Create an Artifact Registry repository
Artifact Registry is Google Cloud's recommended container registry. Older
tutorials often use gcr.io, but new deployments should use the
REGION-docker.pkg.dev format.
Configure local Docker authentication when building on your workstation:
Use unique release or commit tags instead of updating latest. An immutable tag
makes it clear which source produced a Cloud Run revision and makes rollbacks
repeatable.
Step 4: Build and push the Next.js image
Cloud Build can build the Linux image without relying on the developer's local
CPU architecture. Add a cloudbuild.yaml so the immutable release identifier
is forwarded to the Docker build argument:
Submit the build with both substitutions:
The Dockerfile uses a build argument, so a direct Docker build can provide it:
The explicit platform avoids a common failure when an image is built on an Apple Silicon Mac and deployed to an x86 runtime. Cloud Run also supports Arm in applicable configurations, but the built image must match the selected runtime architecture.
Step 5: Deploy Next.js to Cloud Run
Create a dedicated runtime identity instead of running the application with a broad default service account:
Grant this identity only the roles required by the application—for example, access to one Secret Manager secret or one Cloud Storage bucket. Deployment permissions belong to the CI/CD identity, not the runtime identity.
Deploy the image:
Use --allow-unauthenticated only for a public website. Private dashboards and
internal services can require authenticated invocation through Cloud Run IAM or
an identity-aware entry point.
Cloud Run resolves the image tag to a digest and creates an immutable revision. Later deployments create new revisions, which can receive all traffic, a small canary percentage, or no traffic until verified.
Configure CPU, memory, and concurrency from measurements
The values above are starting points, not universal production settings. Server-rendered pages, image optimization, large JSON responses, and AI SDKs can have different CPU and memory profiles.
Higher concurrency reduces instance count but makes each process handle more simultaneous renders. If CPU-bound SSR slows under load, reduce concurrency or allocate more CPU. Set maximum instances low enough to protect databases and downstream APIs, then raise it after load tests prove those dependencies can handle the connection volume.
Minimum instances reduce cold starts but keep compute allocated. Measure p50, p95, and p99 latency before paying to keep instances warm.
Step 6: Manage environment variables and secrets
Server-only variables can be supplied when the container starts. Public
variables prefixed with NEXT_PUBLIC_ are replaced during the build and cannot
be changed by promoting the same image to another environment.
Store sensitive values in Secret Manager and grant the runtime service account access only to the required secrets:
For non-secret configuration, use --set-env-vars or a declarative service
configuration. Avoid placing credentials in build arguments: Docker history and
build logs can expose them.
If one image must move through staging and production, read server variables
during dynamic rendering. Values used in statically generated pages are fixed at
build time even without the NEXT_PUBLIC_ prefix.
Step 7: Add health checks and graceful shutdown
Create a lightweight route that confirms the process can serve requests without running an expensive database or model call:
Use platform startup and liveness probes where appropriate. Keep readiness checks separate from deep dependency diagnostics: one slow third-party service should not necessarily restart every healthy Next.js instance.
During termination, let Next.js receive SIGTERM and finish in-flight requests.
The official self-hosting guidance recommends a 10-to-30-second drain period so
pending after() callbacks can complete. Avoid wrapping server.js in a shell
script that swallows signals.
Step 8: Handle caching across Cloud Run instances
Local memory and disk are not a shared durable Next.js cache. When Cloud Run scales to several instances—or GKE runs several pods—each process otherwise has its own cached entries and invalidation state.
For applications that depend on revalidation or cross-request caching:
- Configure a custom cache handler backed by shared durable storage.
- Set
cacheMaxMemorySize: 0when the design requires disabling the default per-instance memory cache. - Coordinate cache tags through the handler's
refreshTags()behavior sorevalidateTag()on one instance reaches the others. - Test concurrent requests during deployment and invalidation.
Do not put user-specific dynamic responses behind a public CDN cache. Next.js marks routes using dynamic APIs as private; preserve those cache headers through load balancers and proxies.
Static assets under .next/static use content hashes and are safe candidates
for long-lived CDN caching. If assets move to Cloud Storage and Cloud CDN,
configure assetPrefix and test the extra DNS, TLS, and cross-origin behavior.
Step 9: Prevent version skew during rollouts
Rolling deployments can temporarily serve old and new Next.js builds at the same time. A browser may load JavaScript from one version and send a Server Function request to another.
Use the same built image for every instance in a revision and set a deployment identifier from the immutable release version:
Next.js uses the deployment ID to detect mismatches and fall back to a full page navigation instead of attempting an incompatible client transition.
Server Function closure encryption also needs consistency. Set
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY to the same build-time value for all
instances built from one release. Store the base64-encoded AES key securely; do
not generate a different key in each pod at startup.
Cloud Run traffic splitting can canary a new revision before full rollout. Keep the previous revision available long enough to roll back, and test navigation, prefetching, static assets, and Server Functions while both revisions receive traffic.
Step 10: Preserve Next.js streaming
App Router streaming and Suspense can improve time to first byte by returning a shell while slower server work continues. Verify that each proxy, load balancer, and CDN between the browser and Next.js passes chunked responses without buffering.
Cloud Run supports streaming HTTP responses. A custom reverse proxy or GKE Ingress can still introduce buffering, compression, or timeouts. Test a route with a deliberately delayed Suspense boundary from the public endpoint rather than assuming a successful local test proves end-to-end streaming.
Within the application, start independent server data requests together and use Suspense boundaries around slow regions. Container deployment cannot remove an application-level sequential data-fetching waterfall.
Deploy Next.js to GKE instead
Use GKE when the application belongs in an existing Kubernetes environment or needs pod-level policies, sidecars, custom service meshes, specialized scheduling, or cluster-native service discovery.
The same standalone image can run in a Deployment:
Expose the ClusterIP service through a GKE Gateway or Ingress with managed TLS. Keep the application container private inside the cluster.
Add a HorizontalPodAutoscaler
CPU-based autoscaling requires CPU requests on the container. The HPA uses those requests to calculate utilization:
Apply the manifests and inspect the rollout:
GKE does not solve Next.js cache consistency automatically. Use the same shared cache, tag coordination, deployment ID, and Server Function encryption strategy described for multiple Cloud Run instances.
Automate deployment with GitHub Actions
Use GitHub Actions Workload Identity Federation instead of storing a long-lived Google service-account key. The workflow receives a short-lived identity token for the specific repository and job, then authenticates to Google Cloud.
Restrict the federated identity to the expected GitHub organization, repository, branch, and workflow. Give the deployment service account only the Cloud Run, Artifact Registry, and service-account impersonation permissions it needs.
Production pipelines should run tests before building, scan the container, deploy a revision without traffic or with a small canary percentage, verify the public health and critical user paths, and then promote traffic.
Common Next.js on GCP deployment problems
Cloud Run says the container failed to start
Confirm that the process listens on the injected PORT and binds to
0.0.0.0. Run the exact production image locally with PORT=8080 and inspect
startup logs. Also confirm the image architecture matches the runtime.
CSS, JavaScript, or public images return 404
Standalone output does not copy public or .next/static. Verify both Docker
COPY instructions and inspect the final image rather than the builder stage.
Runtime environment changes do not affect the browser
NEXT_PUBLIC_* variables are inlined at build time. Build separate public
configuration into each image or load explicitly public runtime configuration
from a server endpoint. Never expose a secret to solve this problem.
One instance serves stale data after revalidation
The default cache and tag state are per instance. Configure a shared cache and distributed tag coordination, or avoid cross-request caching for that route.
Server Functions fail during a rollout
Check that every container for the release shares the build-time Server Function
encryption key and that deploymentId matches the immutable release. Verify
that old assets remain available while old browser sessions exist.
SSR is slow for distant users
Choose a region near the database and primary users before adding more regions. Cross-region database calls can erase the benefit of moving compute closer to the browser. Cache static work, stream slow regions, remove server-side request waterfalls, and measure from real user locations.
Cloud Run creates too many database connections
Instance count multiplied by per-instance concurrency can exceed the database pool. Use a connection-aware proxy or serverless database option, set sensible pool limits, cap Cloud Run maximum instances, and load-test connection behavior.
Frequently asked questions
Can Next.js run on Google Cloud Platform without Vercel?
Yes. Next.js supports self-hosting as a Node.js server or Docker container. Cloud Run, GKE, and other container platforms can run SSR, App Router, route handlers, streaming, image optimization, and Server Functions when configured correctly.
Is Cloud Run or GKE better for Next.js?
Cloud Run is better for most teams because it has lower operational overhead and built-in revision scaling. GKE is better when the team already operates Kubernetes or requires pod-level networking, scheduling, sidecars, policies, or cluster integrations.
Is App Engine a good Next.js hosting option?
App Engine can run Node.js applications, but Cloud Run maps more directly to a portable standalone container and offers a clearer revision-based workflow. Choose App Engine when its application platform model already matches the rest of the system.
Does Cloud Run support Next.js streaming?
Cloud Run supports streaming HTTP responses. Test the complete path when a load balancer, CDN, reverse proxy, or security product sits in front because any layer can buffer or time out the response.
Does Next.js need a shared cache on Cloud Run?
Not every app does. A shared cache is necessary when cached data and tag invalidations must remain consistent across multiple instances. Fully static or deliberately uncached routes may not need one.
How should I deploy static Next.js sites on GCP?
If every route supports static export, use output: "export" and serve the
generated files from static hosting or Cloud Storage behind a CDN. Do not ship a
Node.js container when the application has no server-runtime requirements.
Observe an AI-enabled Next.js deployment with Currai
Deploying Next.js on GCP tells you whether containers are healthy. Currai shows what happens inside the AI requests those containers serve. Trace each user interaction across model generations, retrieval, tools, and fallbacks; record tokens, model cost, time to first token, end-to-end latency, and errors; and tag traces with the Cloud Run revision, GKE workload, region, and application version.
That context helps separate platform latency from model latency and identify whether a deployment changed AI quality or cost. Currai is not a GCP deployment platform or a replacement for Cloud Monitoring; it is the observability and evaluation layer for the AI workflow inside the application.
Learn how to add OpenTelemetry LLM tracing, measure latency and TTFT, and track token cost. Explore LLM observability, then create a free account to inspect production AI traces from Cloud Run or GKE.
