Implementing Blue-Green Deployments on Vercel

Blue-green deployments are a strategy that allows developers to transition users from one version of an application to another with minimal downtime and risk. It involves maintaining two identical environments: one that serves live traffic (Blue) and another staged version for testing (Green). This guide will walk you through setting up blue-green deployments on Vercel, leveraging its features like Skew Protection, Edge Config, and Middleware in Next.js for a seamless transition.

Demo

Explore a live demo of blue-green deployments: https://blue-green.vercel.app

Getting Started

  1. Deploy Your Project: You can begin with deploying our template that is configured for blue-green deployments.
  2. Activate Skew Protection: Enable Skew Protection for your project in Vercel. This ensures that users stick to the assigned blue or green deployments throughout their session, providing a consistent user experience across Vercel’s global CDN and serverless function infrastructure.
  3. Activate Deployment Protection Bypass: This setting ensures that your deployments can bypass any protections, facilitating smooth transitions between blue and green environments.
  4. Disable Auto-assign Custom Production Domains: This prevents Vercel from automatically assigning your custom domains to new production deployments, giving you a production-like deployment to use as a staging environment instead.
  5. Create an Edge Config: Edge Config allows you to define how traffic is split between your blue and green deployments. Use the following settings to get started:
{
"blue-green-configuration": {
"deploymentDomainBlue": "https://blue-green-61yvm4f5d.vercel.rocks",
"deploymentDomainGreen": "https://blue-green-nq2hvhtsv.vercel.rocks",
"trafficGreenPercent": 50
}
}

Implementing Middleware for Traffic Splitting

Next.js Middleware is utilized to direct traffic between your blue and green deployments based on predefined rules. This setup requires fetching the blue-green configuration from Edge Config and routing requests accordingly.

Below is an example of how to implement the middleware in your Next.js application:

import { get } from "@vercel/edge-config";
import { NextRequest, NextResponse } from "next/server";
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
"/((?!api|_next/static|_next/image|favicon.ico).*)",
],
};
// Configuration stored in Edge Config.
interface BlueGreenConfig {
deploymentDomainBlue: string;
deploymentDomainGreen: string;
trafficGreenPercent: number;
}
export async function middleware(req: NextRequest) {
// We don't want to run blue-green during development.
if (process.env.NODE_ENV !== "production") {
return NextResponse.next();
}
// We only want to run blue-green for GET requests that are for HTML documents.
if (req.method !== "GET") {
return NextResponse.next();
}
if (req.headers.get("sec-fetch-dest") !== "document") {
return NextResponse.next();
}
// Skip if the request is coming from Vercel's deployment system.
if (/vercel/i.test(req.headers.get("user-agent") || "")) {
return NextResponse.next();
}
// Skip if the middleware has already run.
if (req.headers.get("x-deployment-override")) {
return getDeploymentWithCookieBasedOnEnvVar();
}
if (!process.env.EDGE_CONFIG) {
console.warn("EDGE_CONFIG env variable not set. Skipping blue-green.");
return NextResponse.next();
}
// Get the blue-green configuration from Edge Config.
const blueGreenConfig = await get<BlueGreenConfig>(
"blue-green-configuration"
);
if (!blueGreenConfig) {
console.warn("No blue-green configuration found");
return NextResponse.next();
}
const servingDeploymentDomain = process.env.VERCEL_URL;
const selectedDeploymentDomain =
selectBlueGreenDeploymentDomain(blueGreenConfig);
console.info(
"Selected deployment domain",
selectedDeploymentDomain,
blueGreenConfig
);
if (!selectedDeploymentDomain) {
return NextResponse.next();
}
// The selected deployment domain is the same as the one serving the request.
if (servingDeploymentDomain === selectedDeploymentDomain) {
return getDeploymentWithCookieBasedOnEnvVar();
}
// Fetch the HTML document from the selected deployment domain and return it to the user.
const headers = new Headers(req.headers);
headers.set("x-deployment-override", selectedDeploymentDomain);
headers.set(
"x-vercel-protection-bypass",
process.env.VERCEL_AUTOMATION_BYPASS_SECRET || "unknown"
);
const url = new URL(req.url);
url.hostname = selectedDeploymentDomain;
return fetch(url, {
headers,
redirect: "manual",
});
}
// Selects the deployment domain based on the blue-green configuration.
function selectBlueGreenDeploymentDomain(blueGreenConfig: BlueGreenConfig) {
const random = Math.random() * 100;
const selected =
random < blueGreenConfig.trafficGreenPercent
? blueGreenConfig.deploymentDomainGreen
: blueGreenConfig.deploymentDomainBlue || process.env.VERCEL_URL;
if (!selected) {
console.error("Blue green configuration error", blueGreenConfig);
}
if (/^http/.test(selected || "")) {
return new URL(selected || "").hostname;
}
return selected;
}
function getDeploymentWithCookieBasedOnEnvVar() {
console.log(
"Setting cookie based on env var",
process.env.VERCEL_DEPLOYMENT_ID
);
const response = NextResponse.next();
// We need to set this cookie because next.js does not do this by default, but we do want
// the deployment choice to survive a client-side navigation.
response.cookies.set("__vdpl", process.env.VERCEL_DEPLOYMENT_ID || "", {
sameSite: "strict",
httpOnly: true,
maxAge: 60 * 60 * 24, // 24 hours
});
return response;
}

The middleware ensures that only production GET requests for HTML documents are considered for blue-green deployment routing, excluding API routes, static files, image optimization files, and the favicon.

Setting Up CI/CD Integrations for Deployment Management

Start Splitting Traffic Upon New Staged Deployment

Using GitHub Actions as an example, we can create a workflow to automate the process of updating your Edge Config settings when a new staged deployment is successful. We initiate the traffic splitting based of the configuration you specify. In this case we are starting the traffic at 50%.

name: Create Blue-Green Deployment
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_PROD_URL: ${{ secrets.VERCEL_PRODUCTION_DOMAIN }}
on:
deployment_status:
jobs:
create-blue-green-deployments:
if: github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success'
runs-on: ubuntu-latest
steps:
- name: Update Edge Config
run: |
curl -X 'PATCH' 'https://api.vercel.com/v1/edge-config/${{ secrets.VERCEL_EDGE_CONFIG_ID }}/items?teamId=${{ secrets.VERCEL_ORG_ID }}' \
-H 'Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}' \
-H 'Content-Type: application/json' \
-d $'{ "items": [ { "operation": "upsert", "key": "blue-green-configuration", "value": { "deploymentDomainBlue": "${{ env.VERCEL_PROD_URL }}", "deploymentDomainGreen": "${{ github.event.deployment_status.environment_url }}", "trafficGreenPercent": 50 } } ] }'

Promote a Build After Approval

Set up another workflow to manage the promotion of the green deployment to production upon approval of the deployment. This specific workflow allows for manual triggers, providing flexibility in deployment management, but this can also be automated based on your organizational needs.

name: Promote Blue-Green Deployment
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_PROD_URL: ${{ secrets.VERCEL_PRODUCTION_DOMAIN }}
on:
# Allow manual runs
workflow_dispatch
jobs:
promote-blue-green-deployment:
runs-on: ubuntu-latest
steps:
- name: Install Vercel CLI
run: npm install --global vercel
- name: Read Edge Config
run: |
curl 'https://edge-config.vercel.com/${{ secrets.VERCEL_EDGE_CONFIG_ID }}/item/blue-green-configuration' \
-H 'Authorization: Bearer ${{ secrets.EDGE_CONFIG_SECRET }}' \
-o workflow.json
- name: Parse Staged URL
id: blue-green
run: |
echo "url=$(cat workflow.json | jq -c '.deploymentDomainGreen')" >> $GITHUB_OUTPUT
- name: Promote Vercel Deployment
run: vercel promote ${{ steps.blue-green.outputs.url }} --token=${{ secrets.VERCEL_TOKEN }} --scope jasonwikerentdemo
- name: Update Edge Config
run: |
curl -X 'PATCH' 'https://api.vercel.com/v1/edge-config/${{ secrets.VERCEL_EDGE_CONFIG_ID }}/items?teamId=${{ secrets.VERCEL_ORG_ID }}' \
-H 'Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}' \
-H 'Content-Type: application/json' \
-d $'{ "items": [ { "operation": "upsert", "key": "blue-green-configuration", "value": { "deploymentDomainBlue": "${{ env.VERCEL_PROD_URL }}", "deploymentDomainGreen": "${{ steps.blue-green.outputs.url }}", "trafficGreenPercent": 0 } } ] }'

This setup involves fetching the current Edge Config, parsing the deployment to promote, and updating the Edge Config to reflect the new state.

Conclusion

Blue-green deployments on Vercel offer a robust strategy for managing application updates with minimal risk and downtime. By leveraging Vercel's features along with GitHub Actions, developers can automate and control the deployment process, ensuring smooth and reliable application updates. Follow the steps outlined in this guide to implement your blue-green deployments, and visit the provided demo for a practical example.

Couldn't find the guide you need?