5 min read

Incremental Migration Guide

Discover how to perform a zero downtime migration on Vercel: A guide to incrementally transitioning your site for optimal performance.
Table of Contents

Follow these steps to incrementally migrate your website to Vercel. Two possible strategies can be applied:

  1. Point your domain to Vercel from the beginning
  2. Keep your domain on the legacy server

It's advisable to peruse the troubleshooting section before initiating the migration process.

In this approach, you make Vercel the entry point for all your production traffic. When you begin, all traffic will be sent to the legacy server with rewrites and/or fallbacks. As you migrate different aspects of your site to Vercel, you can remove the rewrites/fallbacks to the migrated paths so that they are now served by Vercel.

Point your domain to Vercel approach

Use the framework of your choice to deploy your application to Vercel

Send all traffic to the legacy server using one of the following 3 methods:

Use rewrites built-in to the framework such as configuring next.config.js with fallbacks and rewrites in Next.js

The code example below shows how to configure rewrites with fallback using next.config.js to send all traffic to the legacy server:

next.config.js
module.exports = {
  async rewrites() {
    return {
      fallback: [
        {
          source: '/:path*',
          destination: 'https://my-legacy-site.com/:path*',
        },
      ],
    };
  },
};

Use vercel.json for frameworks that do not have rewrite support. See the how do rewrites work documentation to learn how to rewrite to an external destination, from a specific path.

Use Edge Config with Edge Middleware to rewrite requests at the edge with the following benefits:

  • No need to re-deploy your application when rewrite changes are required
  • Immediately switch back to the legacy server if the new feature implementation is broken

Review this maintenance page example to understand the mechanics of this approach

This is an example middleware code for executing the rewrites at the edge:

middleware.ts
import { get } from '@vercel/edge-config';
import { NextRequest, NextResponse } from 'next/server';
 
export const config = {
  matcher: '/((?!api|_next/static|favicon.ico).*)',
};
 
export default async function middleware(request: NextRequest) {
  const url = request.nextUrl;
  const rewrites = await get('rewrites'); // Get rewrites stored in Edge Config
 
  for (const rewrite of rewrites) {
    if (rewrite.source === url.pathname) {
      url.pathname = rewrite.destination;
      return NextResponse.rewrite(url);
    }
  }
 
  return NextResponse.next();
}

In the above example, you use Edge Config to store one key-value pair for each rewrite. In this case, you should consider Edge Config Limits (For example, 5000 routes would require around 512KB of storage). You can also rewrite based on URLPatterns where you would store each URLPattern as a key-value pair in Edge Config and not require one pair for each route.

Connect your production domain to your Vercel Project. All your traffic will now be sent to the legacy server.

Develop and test the first iteration of your application on Vercel on specific paths.

With the fallback approach such as with the next.config.js example above, Next.js will automatically serve content from your Vercel project as you add new paths to your application. You will therefore not need to make any rewrite configuration changes as you iterate. For specific rewrite rules, you will need to remove/update them as you iterate.

Repeat this process until all the paths are migrated to Vercel and all rewrites are removed.

In this approach, once you have tested a specific feature on your new Vercel application, you configure your legacy server or proxy to send the traffic on that path to the path on the Vercel deployment where the feature is deployed.

Keep your domain on the legacy server approach

Use the framework of your choice to deploy your application on Vercel and build the first feature that you would like to migrate.

Once you have tested the first feature fully on Vercel, add a rewrite or reverse proxy to your existing server to send the traffic on the path for that feature to the Vercel deployment.

For example, if you are using nginx, you can use the proxy_pass directive to send the traffic to the Vercel deployment.

Let's say you deployed the new feature at the folder new-feature of the new Next.js application and set its basePath to /new-feature, as shown below:

next.config.js
module.exports = {
  basePath: '/new-feature',
};

When deployed, your new feature will be available at https://my-new-app.vercel.app/.

You can then use the following nginx configuration to send the traffic for that feature from the legacy server to the new implementation:

nginx.conf
server {
    listen 80;
    server_name legacy-server.com www.legacy-server.com;
 
    location /feature-path-on-legacy-server {
        proxy_pass https://my-new-app.vercel.app/;
    }
}

Repeat steps 1 and 2 until all the features have been migrated to Vercel. You can then point your domain to Vercel and remove the legacy server.

Vercel has a limit of 1024 routes per deployment for rewrites. If you have more than 1024 routes, you will need to use the Edge Config approach.

If you're facing unexpected outcomes or cannot find an immediate solution for an unexpected behaviour with a new feature, you can set up a variable in Edge Config that you can turn on and off at any time without having to make any code changes on your deployment. The value of this variable will determine whether you rewrite to the new version or the legacy server.

For example, with Next.js, you can use the follow middleware code example:

middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { get } from '@vercel/edge-config';
 
export const config = {
  matcher: ['/'], // URL to match
};
 
export async function middleware(request: NextRequest) {
  try {
    // Check whether the new version should be shown - isNewVersionActive is a boolean value stored in Edge Config that you can update from your Project dashboard without any code changes
    const isNewVersionActive = await get<boolean>('isNewVersionActive');
 
    // If `isNewVersionActive` is false, rewrite to the legacy server URL
    if (!isNewVersionActive) {
      req.nextUrl.pathname = `/legacy-path`;
      return NextResponse.rewrite(req.nextUrl);
    }
  } catch (error) {
    console.error(error);
  }
}

Create an Edge Config and set it to { "isNewVersionActive": true }. By default, the new feature is active since isNewVersionActive is true. If you experience any issues, you can fallback to the legacy server by setting isNewVersionActive to false in the Edge Config from your Vercel dashboard.

Last updated on May 9, 2024