Blue/Green Deployments with Azure App Service + GitHub Actions
How to set up blue/green deployments with Azure App Service and GitHub Actions
How to set up blue/green deployments with Azure App Service and GitHub Actions
Setting up blue/green deployments with Azure App Service and GitHub Actions is a great way to ensure that your application is always available to users, even during deployment. It is especially useful for applications that require zero downtime during deployment, such as e-commerce sites or applications that handle sensitive data.
This document outlines how to set up a Blue/Green deployment strategy using Azure App Service Slots and GitHub Actions, including a dedicated Maintenance Page that can be activated at any time with zero downtime.
production
, staging
, and maintenance
production
(default slot)staging
maintenance
Add these repository secrets:
Secret Name | Purpose |
---|---|
AZURE_CLIENT_ID | App registration/client ID |
AZURE_TENANT_ID | Tenant ID |
AZURE_SUBSCRIPTION_ID | Azure subscription |
AZURE_RESOURCE_GROUP | The App Service’s resource group |
.github/workflows/build-and-deploy.yml
Key features:
staging
slot on push to main
maintenance
or staging
staging
→ production
name: Build and Deploy to Azure Web App
on:
push:
branches:
- main
workflow_dispatch:
inputs:
slot:
description: "Slot to deploy to (for manual deploy or swap)"
required: true
default: staging
type: choice
options:
- production
- staging
- maintenance
- swap-to-production
permissions:
id-token: write
contents: read
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: dev
steps:
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- uses: actions/checkout@v3
- name: Cache Bun deps
uses: actions/cache@v3
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lockb') }}
- name: Install Bun
run: |
curl -fsSL https://bun.sh/install | bash
echo "$HOME/.bun/bin" >> $GITHUB_PATH
- name: Install deps + Prisma + Build
run: |
bun install
npx prisma generate
echo "NEXT_DISABLE_ESLINT_PLUGIN=true" >> $GITHUB_ENV
bun run build
- name: Zip for deployment
run: |
rm -rf deploy nextjs-app.zip
mkdir deploy
cp -r .next public node_modules package.json bun.lockb next.config.js deploy/
cd deploy
zip -r ../nextjs-app.zip .
- name: Deploy to staging
if: github.event_name == 'push' || github.event.inputs.slot == 'staging'
uses: azure/webapps-deploy@v2
with:
app-name: event-planning-trpc
slot-name: staging
package: nextjs-app.zip
- name: Deploy to maintenance
if: github.event.inputs.slot == 'maintenance'
uses: azure/webapps-deploy@v2
with:
app-name: event-planning-trpc
slot-name: maintenance
package: nextjs-app.zip
- name: Swap staging → production
if: github.event.inputs.slot == 'swap-to-production'
uses: azure/CLI@v1
with:
azcliversion: 2.30.0
inlineScript: |
az webapp deployment slot swap \
--name event-planning-trpc \
--resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \
--slot staging \
--target-slot production
import MaintenancePage from './maintenance/page';
export default function RootLayout({ children }) {
const isUnderMaintenance = process.env.SHOW_MAINTENANCE_PAGE === 'true';
return (
<html lang="en">
<body>
{isUnderMaintenance ? <MaintenancePage /> : children}
</body>
</html>
);
}
import { notFound } from 'next/navigation';
export default function MaintenancePage() {
const isUnderMaintenance = process.env.SHOW_MAINTENANCE_PAGE === 'true';
if (!isUnderMaintenance) {
return notFound();
} else {
return (
<main className="min-h-screen flex items-center justify-center text-center">
<div>
<h1 className="text-3xl font-bold">🚧 Site Under Maintenance</h1>
<p className="mt-4">We’ll be back shortly!</p>
</div>
</main>
);
}
}
Make sure to check the Deployment slot setting
checkbox
Slot | SHOW_MAINTENANCE_PAGE |
---|---|
maintenance | true |
staging | false |
production | false |
Then you can:
This workflow supports manual deployments and slot swaps using GitHub’s workflow_dispatch input.
It lets you:
ℹ️ Tip: This is useful for hotfixes, testing staging before a swap, or triggering a maintenance window without code changes.
You’re now fully set up for: