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.
Explore a live demo of blue-green deployments: https://blue-green.vercel.app
- Deploy Your Project: You can begin with deploying our template that is configured for blue-green deployments.
- 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.
- Activate Deployment Protection Bypass: This setting ensures that your deployments can bypass any protections, facilitating smooth transitions between blue and green environments.
- 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.
- 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 }}
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.
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.
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.