Migrate from Vercel to self-hosted Next.js

What changes when a Next.js app leaves Vercel for containers you run, what moves cleanly, what needs rework, and how to plan a parallel run and cutover.

On this page

Migrate from Vercel to self-hosted Next.js: what actually changes

Most of the application code moves unchanged. A Next.js app that runs on Vercel can usually run as a Node.js server or in a Docker container with the same routes, components and data fetching, because the Next.js documentation states that Node.js server and Docker deployments support all Next.js features. What changes is the work Vercel did behind the scenes. The platform team takes on image optimization, shared ISR caching, multi-instance keys, proxy streaming and environment handling, and each of these needs a deliberate decision before the first production cutover. The platform or DevOps engineers who already run container workloads usually own this work, with frontend engineers reviewing caching and rendering behavior. Effort and risk depend mostly on how many features the app uses from Vercel and how many instances it will run, so ask your team to inventory those two things before estimating.

Start the build with output: 'standalone' in next.config, which the Next.js Docker example uses to produce a minimal image containing only the runtime files and dependencies. Image optimization with next/image works under next start with no configuration, but on glibc-based Linux images the self-hosting guide notes that sharp may need extra configuration to avoid excessive memory use, so load test the image pipeline with realistic image sizes before you set container memory limits. The larger risk is ISR. By default the Next.js server cache lives on each instance's local disk, so a second replica serves its own stale copy. You need a custom cacheHandler backed by shared storage such as Redis or S3, with cacheMaxMemorySize set to 0. Also plan for revalidateTag: unless your handler implements refreshTags, an invalidation on one instance does not reach the others.

Before running more than one instance, set the same NEXT_SERVER_ACTIONS_ENCRYPTION_KEY on every replica, or server actions will fail when a request lands on a different instance. Set a deploymentId as well, since the guide recommends it for version skew protection during rolling deployments. Streaming and Partial Prerendering need the proxy to stop buffering responses, for example with an X-Accel-Buffering: no header on nginx, so test streamed pages through the exact proxy and load balancer you will run. For environment variables, vercel env pull cannot read values stored as the Secret type, which is the default for Production and Preview, so ask the app owners now which values they can recover from their own systems before cutover. Confirm each of these items against the current Next.js self-hosting guide for the version you ship.

Which Vercel features map to self-hosted Next.js and which need rework

This table maps the Vercel features a Next.js app relies on to their self-hosted equivalents in containers you run. Each row is marked clean, needs rework or gap, so you can see which parts of the app move as they are and which need code or infrastructure changes. Confirm CDN and edge behavior in your own environment, because no self-hosted setup reproduces Vercel's global network exactly.

Vercel featureSelf-hosted equivalentStatus
Build outputNode.js server or Docker image built with output set to standalone, which the Next.js Docker example uses for a minimal runtime imageClean
Image optimizationnext/image works under next start with no configuration, but on glibc Linux you may need sharp settings to limit memory useNeeds rework
ISR and data cacheDefault cache sits on each instance's local disk, so multiple instances need a custom cacheHandler backed by Redis or S3 with cacheMaxMemorySize set to 0Needs rework
revalidateTag across instancesOnly the instance that received the call is invalidated unless the cache handler implements refreshTags against shared storageNeeds rework
Server ActionsWorks on Node.js or Docker, but every instance needs the same NEXT_SERVER_ACTIONS_ENCRYPTION_KEYNeeds rework
Rolling deploymentsSet a deploymentId to protect against version skew while old and new instances run togetherNeeds rework
Streaming and Partial PrerenderingProxy buffering must be disabled, for example with the X-Accel-Buffering: no header, and streaming must work end to endNeeds rework
Global CDN and edge routingYour reverse proxy and any external CDN you add, with caching rules you write yourself. Confirm edge behavior in your own environmentGap
Environment variablesStored in your platform's settings, but Vercel Secret values cannot be read back with vercel env pull, so you must re-enter them from your own recordsNeeds rework

Migration sequence from inventory to decommission

This sequence moves a Next.js app from Vercel to containers you operate in a controlled order, so each stage produces evidence that the next one depends on. Each step closes with a named output, and Vercel is switched off only after the self-hosted deployment has been proven under real traffic patterns.

  1. Inventory routes, caching and configuration

    The app team lists every route with its rendering mode, every use of ISR and revalidation, every use of next/image, every server action and every environment variable, including which ones are marked as Secret in Vercel. Security and platform owners review the list for values that cannot be read back with vercel env pull and need to be recovered from their source systems. The output is a written inventory and a list of cache-dependent routes that defines what the self-hosted build must reproduce.

  2. Build a pilot container with standalone output

    The team sets output to standalone in the Next.js configuration and builds a Docker image that copies only the required runtime files and dependencies. The pilot runs on a single host with next/image tested against sharp, and the team sets memory limits and confirms the image resource use on glibc-based Linux. The output is a reproducible image tagged per commit and a smoke test report covering every route in the inventory.

  3. Configure shared cache and multi-instance settings

    If the pilot will run more than one instance, the team replaces the default local disk ISR cache with a custom cache handler backed by shared storage such as Redis or S3, sets cacheMaxMemorySize to 0, and implements refreshTags so revalidateTag works across instances. Each instance receives the same server actions encryption key, and a deployment ID is set for version skew protection. The output is a verified multi-instance test in which a revalidation on one instance updates every other instance.

  4. Run a parallel deployment behind a staging domain

    The container stack is deployed on the self-hosted platform, such as Coolify or Dokploy, under a staging domain while Vercel keeps serving production. The team configures the reverse proxy so streaming responses are not buffered, sets X-Accel-Buffering to no where nginx is used, and checks Partial Prerendering end to end if the app uses it. The output is a signed-off test run comparing responses, cache behavior and streaming between the two environments.

  5. Stage the DNS cutover

    The team lowers DNS record TTLs ahead of the change, confirms TLS certificates are issued for the production hostnames, and moves a rollback path to the Vercel deployment in writing. Ops watches error rates, latency and cache hit patterns during the switch and keeps Vercel active as the fallback. The output is a completed cutover record with the time of the change, the observed metrics and the named owner who can reverse it.

  6. Decommission Vercel after the observation period

    After the team has agreed an observation period with the business owner, it confirms that no integration, webhook or preview still points to Vercel, exports any data that must be retained, and removes the project's domains and environment variables. The team also reviews the Vercel plan and billing status with procurement before closing the account. The output is a decommission checklist signed by the service owner and a record of where each retained secret and configuration now lives.

Data, environment variables and integrations to rebuild

Code moves with a build, but Vercel holds several kinds of state that a self-hosted Next.js deployment does not inherit. The cards below cover the items that teams most often discover late: environment variables, the ISR cache, optimized images and Marketplace databases. Each one needs an owner and a check before the DNS cutover.

Secret-type environment variables

The Secret type cannot be exported. The vercel env pull command writes variables to a file, but values stored as Secret, which is the default for Production and Preview, cannot be read back from Vercel. Before cutover, list every variable marked Secret in the project, take the values from the systems that issued them (database users, API keys, signing keys) and load them into your own secret store. Then confirm the container runtime receives them under the same names the app reads.

Server Actions encryption key

When several Next.js instances run behind one load balancer, each must share the same NEXT_SERVER_ACTIONS_ENCRYPTION_KEY. A key generated per container will cause Server Actions to fail intermittently as requests land on different instances. Generate the key once, store it in your secret manager, and treat rotation as a planned change that touches every instance at the same time.

ISR cache and cache tags

The ISR cache is written to each instance's local disk by default, so a fleet of containers will hold different cached pages. Configure a custom cacheHandler backed by Redis or S3 and set cacheMaxMemorySize to 0. Then confirm that revalidateTag reaches every instance: unless the handler implements refreshTags, an invalidation on one instance only clears that instance's cache.

Image optimization dependencies

On Vercel, image optimization runs as a managed service. Self-hosted, next/image works under next start without configuration, but on glibc-based Linux images it may need additional sharp configuration to prevent excessive memory use. Set container memory limits, load-test pages with many images, and check that the base image includes the native libraries your sharp build expects.

Marketplace databases to rebuild

Databases held by Marketplace providers such as Neon and Upstash are not exported by the Vercel project. Export the data from the provider account, restore it to PostgreSQL, Redis or Valkey on your servers, and rewrite the connection strings. Coolify can deploy these databases and schedule backups for PostgreSQL, but its backups do not cover Redis, Dragonfly or KeyDB, so plan a separate backup for any key-value store.

Deployment ID for rolling releases

During a rolling deployment, old and new containers serve traffic at the same time. Set a deploymentId so that clients and servers can detect version skew and fetch matching assets. Without it, users with an open page may hit errors after a release. Record the ID in your release pipeline and confirm that the proxy routes asset requests to the same build that rendered the page.

How the migration differs by app type and starting point

A fully static or mostly static Next.js site has the smallest runtime footprint to move. Most pages are prerendered at build time, so the main decisions are how the build output is served and whether any dynamic routes or API handlers remain. The Next.js docs note that a static export supports a limited set of features, so a site that depends on server rendering, middleware or route handlers needs the Node.js server or Docker path instead of a plain static export.

  • Build target: Confirm whether the project uses output: 'export' or a Node.js server. Only the server path supports the full feature set.
  • Dynamic routes: List every route that renders per request. Each one needs a running server behind the proxy.
  • Static file serving: Confirm which proxy or Coolify build pack serves the static assets and how cache headers are set.
  • Forms and APIs: Identify any route handlers or third-party functions that the static export cannot run.
  • Redirects and headers: Move rules from vercel.json or next.config into the new proxy configuration and test them.

Before cutover, check that every public URL returns the same status code and content on the new host.

What usually goes wrong and how to prevent it

Moving Next.js off Vercel moves cache, encryption, deployment versioning, proxy buffering and image processing from the platform to your team. Each of these works on a single instance and fails quietly once you run several, so the checks below belong in the cutover plan before traffic moves.

Stale ISR pages after revalidateTag

With the default cache, each instance keeps ISR data on its own local disk. Calling revalidateTag on one instance only clears that instance, so other instances keep serving old pages. Set a custom cache handler backed by shared storage such as Redis or S3, set cacheMaxMemorySize to 0, and implement refreshTags so tag state syncs across instances. Then test a revalidation on one instance and confirm the others update.

Server action failures from mismatched keys

Server actions encrypt their payloads. If each instance has a different NEXT_SERVER_ACTIONS_ENCRYPTION_KEY, a request sent to one instance can fail when another instance handles the follow-up call. Generate one key, store it in your secrets manager, and inject the same value into every container. Check this during a load-balanced test where form submissions are routed across instances.

Version skew during rolling deploys

During a rolling release, old and new containers serve traffic at the same time. A browser that loaded assets from the old build can call server actions or fetch data that the new build no longer matches. Set a deploymentId in the Next.js config so clients and servers agree on the build they use. Confirm the value changes with every release and that your proxy does not cache across versions.

Buffered streaming behind a proxy

Streaming responses from React Server Components and Partial Prerendering only reach users when the proxy passes chunks through immediately. Default proxy buffering holds the stream until the response ends, which hides loading states and breaks progressive rendering. Disable proxy buffering for the app route, or send X-Accel-Buffering: no from Next.js. Test with a slow data source and watch the first bytes arrive in the browser network panel.

Image memory use on glibc Linux

next/image works without extra setup under next start, but on glibc-based Linux images, sharp can use more memory than the container limit allows. Containers then restart under load, often during traffic spikes when image requests peak. Size the container memory limit for image work, add the sharp configuration the self-hosting guide describes for glibc systems, and load test image-heavy pages before cutover.

What drives cost and effort in this migration

This table lists the factors that raise or lower the work of moving a Next.js app from Vercel to containers you run. Read each row as a question to answer before requesting a quote, since the same app can differ widely in effort depending on these inputs. The main takeaway is that ISR routes, shared cache design and the number of instances matter more than the app's line count.

DriverWhy it changes effortWhat to ask for or confirm
Routes using ISR or revalidationEach route that relies on the cache needs a shared cache handler, and revalidateTag must sync across instances through refreshTags.Ask for a list of ISR and tag-revalidated routes and a test plan for cache invalidation across two instances.
Shared cache backendRedis or S3 backends each bring their own provisioning, backup, eviction and access control work that Vercel currently handles.Ask which backend the quote assumes, who operates it, and how it is backed up and monitored.
Number of instancesEach added instance multiplies configuration checks, requires the same encryption key, and adds a deployment ID to manage during rolling releases.Ask how the design scales from one to several instances and what changes in the proxy and health checks.
Image optimization volumenext/image needs sharp configured on glibc Linux to avoid excessive memory use, and heavy image traffic raises CPU and memory sizing.Ask for estimated image requests per month and a sizing test under realistic load.
Proxy, TLS and streaming setupProxy buffering must be disabled for streaming, and Partial Prerendering needs streaming end to end, so each hop in the path adds a configuration review.Ask which proxy (Traefik, Caddy or nginx) the design uses and how streaming is tested through it.
Secret-typed environment variablesSecret values cannot be read back with vercel env pull, so each one must be recovered from its source system or reissued by its owner.Ask for a count of Secret-typed variables and who holds the original values.
Build and container pipelineOutput standalone, the Docker build and the choice of build pack affect how much of the current build pipeline must be rebuilt.Ask which build method is planned and whether preview builds are in scope.
Operations after cutoverBackups, patching, rollbacks and monitoring move to your team, and a rollback does not restore databases or persistent files.Ask who owns on-call, backup schedules, restore tests and the rollback runbook.

Which costs recur after the Vercel subscription ends

The recurring bill moves from one vendor invoice to several line items you own. Once Vercel is no longer billing you, the recurring costs are the servers or Kubernetes nodes that run the Next.js containers, the shared cache store that ISR needs when more than one instance serves traffic, the reverse proxy and its certificates, monitoring, and backups for the application and any database it uses. Each of these is paid to a different party: a cloud provider or hosting company for compute and storage, a certificate authority or ACME service for TLS (often free, but still something to monitor), and your own staff for everything in between. Ask for a line-by-line estimate that separates compute, storage, egress, and any managed services, because the drivers differ for each. If you run Coolify, its hosting option is separate from the self-hosted edition, so confirm which fees apply to your setup on Coolify's pricing page.

The largest recurring cost is usually engineering time, and it is hard to quote because it depends on your team. Someone must keep builds working as Next.js releases change, tune sharp memory on glibc-based Linux hosts, watch cache hit rates and invalidation through revalidateTag across instances, rotate the server actions encryption key under a controlled process, and respond when a container fails at 2 a.m. Migration itself is mostly one-time work: writing the cacheHandler, testing output: 'standalone' builds, setting up the proxy for streaming, and moving environment variables. Recurring work begins after cutover. For Vercel's own billing terms, including the included credit and metered usage on each plan, the current pricing page is the authoritative source, so compare your last twelve months of invoices against it rather than relying on a summary. Confirm with your infrastructure team which on-call rotation will own the new stack before you set a cutover date.

Questions teams ask before moving Next.js off Vercel

How much effort does moving a Next.js app off Vercel take?

The effort depends mostly on which Vercel features the app uses, not on the framework itself. A Next.js app that runs as a Node.js server or in a Docker container supports all Next.js features, so the core change is packaging the app and running it on servers you control. The work grows when the app relies on ISR across several instances, heavy image optimization, streaming behind a proxy, or Vercel-specific services such as Blob storage or Marketplace databases. Ask your engineers to inventory those dependencies first, because that inventory sets the scope of any quote. No fixed duration is supplied for this move, so confirm the estimate against your own codebase before committing to a timeline.

Can our in-house team move a Next.js app off Vercel without outside help?

Many teams can do the basic move internally, especially if they already run containers and know their reverse proxy and DNS. The harder parts are operational: shared ISR cache storage, consistent encryption keys across instances, rolling deployments with version skew protection, and backups for any databases the app uses. Ask whether your team has owned these pieces before on production systems. If the answer is partly yes, a fixed-scope engagement can cover only the gaps, and DeployOpen scopes and quotes that work in writing before it starts.

What does the migration require from our engineers and operators?

Plan for engineers who understand the application build, environment configuration and caching behavior, plus operators who own the servers, proxy and certificates. In practice that means:

  • Changing the build to produce a standalone output and a container image.
  • Configuring a shared cache handler and checking how revalidation propagates between instances.
  • Setting one NEXT_SERVER_ACTIONS_ENCRYPTION_KEY on every instance and a deploymentId for rolling releases.
  • Owning backups, monitoring and the rollback path, which a managed platform handled for you.

Confirm headcount and on-call coverage with your own staff before agreeing to a scope.

How long should a parallel run last before we cut over from Vercel?

Run the new deployment alongside Vercel until your team has verified the behaviors that matter most to your users, which usually include cached pages, image output, server actions, and streamed responses. The length of that period is a decision for your team, based on your traffic patterns, release frequency and risk tolerance, and no duration is supplied here. Agree on the exit criteria in writing before the cutover, such as which pages must match, which error rates are acceptable, and how DNS will move. Keep Vercel available until those criteria are met.

Can we keep Vercel preview deployments and the global CDN after the move?

Previews can be replaced, but the global CDN cannot be replicated the same way. Coolify builds pull or merge requests from GitHub, GitLab, Gitea and Bitbucket at a default {{pr_id}}.{{domain}} URL that needs wildcard DNS, and it removes them when the request closes or merges. Dokploy also offers GitHub previews. Vercel's edge network is not something a self-hosted platform provides, so you would need a CDN or caching layer you operate or buy separately, and you should confirm what caching behavior your pages need before deciding.

What does it cost to run Next.js ourselves instead of paying Vercel?

The cost has four main parts: servers or cluster capacity, a reverse proxy and certificates, object or Redis storage for shared caches, and the staff time to build, patch and monitor the system. Vercel charges metered usage above the $20 credit included in its Pro plan, according to Vercel's pricing page, so compare that metering with your own infrastructure bill. Coolify itself is Apache-2.0 and free to self-host, while Coolify Cloud costs $5 per month for 2 connected servers plus $3 per month for each additional server, according to Coolify's pricing page. Ask any quote to separate one-time migration work from recurring hosting and support.

What happens if something breaks after cutover?

Once you leave Vercel, rollback and recovery become your responsibility. Coolify's guide for teams coming from managed platforms notes that a rollback can redeploy a retained application image but does not restore databases or persistent files. Define your rollback procedure, retain previous images, and test database backups before cutover. Ask any implementation partner how they will handle backups, restore testing and upgrades, and whether that support is included in the fixed scope or sold as a retainer.

How DeployOpen scopes and runs the migration

DeployOpen is a Chainscore Labs company that plans, implements, hardens, backs up and documents self-hosted platforms inside the client's own cloud accounts, data centers or air-gapped networks. For a Vercel move, that means the Next.js containers, the reverse proxy, the cache backend, the build pipeline and the operational runbooks all run under your control from the first day. Work starts with a written scope that names the applications in scope, the target hosts, the cache store (for example Redis or S3), the image optimization settings, the deployment ID scheme and the cutover plan, with deliverables and timelines agreed before any change is made. A quote follows from that written scope, so the cost reflects the decisions your team has actually made rather than a generic template.

The scoping conversation should settle a few decisions early. Confirm which routes depend on ISR, which use revalidateTag, whether Server Actions are used across instances, and whether any route relies on streaming through a proxy. Ask who owns the NEXT_SERVER_ACTIONS_ENCRYPTION_KEY after handover and where it is stored, and who approves a rollback. Hardening covers secrets handling for the environment variables you move, TLS and proxy configuration, container images, access to the deployment host, and logging. Backup covers the cache store and any database or file storage the app uses, which a self-hosted PaaS will not protect for you. Documentation covers the runbook, the restore steps, and the evidence your compliance team may need, though DeployOpen prepares that evidence and does not issue audit reports or certifications.

After handover, ongoing support, upgrades and continuous security testing can run under a retainer, which is a separate written agreement from the fixed-scope migration. Ask any implementation partner, including DeployOpen, to state in writing what is delivered, who performs each task, how acceptance is tested and what happens after go-live. Ask for the cost components as separate lines: the migration scope, the infrastructure you pay for directly, and the recurring support. Expect the recurring part to change with the number of applications and the number of instances, and ask how each change would be priced before you agree to it.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.