Tutorials
This guide covers deploying Next.js applications to Azure using the three most common deployment targets: Azure Static Web Apps, Azure App Service, and Docker containers. Each approach has different capabilities and trade-offs.
Deploying your Agility CMS Next.js site to Azure provides several advantages:
Most developers deploying Agility CMS websites to Azure will be starting from an existing Next.js project:
These starters already include proper Next.js configuration, Agility CMS integration, and the necessary build scripts. You'll still need to clone the repository, install dependencies, and configure your environment variables, but you won't need to set up Next.js from scratch or configure Agility CMS integration manually.
| Deployment Method | Best For | Next.js Features Support | Complexity |
|---|---|---|---|
| Azure Static Web Apps | Hybrid Next.js apps with SSR/ISR | Full (with managed backend) | Low |
| Azure App Service | Full-featured Node.js hosting | All features supported | Medium |
| Docker | Custom infrastructure, K8s | All features supported | Medium-High |
Azure Static Web Apps provides the easiest deployment path with built-in support for Next.js hybrid rendering, including Server-Side Rendering (SSR), React Server Components, and API routes.
staticwebapp.config.json features unsupported (navigation fallback, certain rewrites)If you don't already have an Agility CMS instance set up:
GUID (Instance ID)Live API Key (for production)Preview API Key (for development/preview)Security Key (for webhooks)You'll need these credentials for both local development and Azure deployment configuration.
Start with an Agility CMS Next.js Starter:
Clone the Agility CMS Next.js Starter (recommended):
git clone https://github.com/agility/agilitycms-nextjs-starter.git
cd agilitycms-nextjs-starter
Or use the Comprehensive Demo for advanced features:
git clone https://github.com/agility/nextjs-demo-site-2025.git
cd nextjs-demo-site-2025
Verify your package.json has the required scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
Note: The Agility starters already include proper Next.js configuration for Azure deployment, including image optimization settings that work with Agility's Edge CDN.
If using your own existing Agility Next.js project, ensure it has the same script configuration above.
Navigate to Azure Portal:
Create a New Resource:
Basics Tab:
Deployment Details Tab:
/ (or your app folder if your Next.js app is in a subdirectory)Review + Create:
Note: After creation, Azure will automatically create a GitHub Actions workflow file in your repository. The initial deployment may take a few minutes.
Enable Server-Side Features:
Add server-rendered data with Server Components:
// app/page.tsx
import { unstable_noStore as noStore } from 'next/cache';
export default function Home() {
noStore(); // Forces dynamic rendering
const timeOnServer = new Date().toLocaleTimeString('en-US');
return (
<main>
<div>
Server time: <strong>{timeOnServer}</strong>
</div>
</main>
);
}
Add API routes:
// app/api/currentTime/route.ts
import { NextResponse } from 'next/server';
export const dynamic = 'force-dynamic';
export async function GET() {
const currentTime = new Date().toLocaleTimeString('en-US');
return NextResponse.json({
message: `Current time is ${currentTime}.`
});
}
Enable Standalone Output:
// next.config.js
module.exports = {
output: 'standalone',
}
Update build script to copy static assets:
{
"scripts": {
"build": "next build && cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/"
}
}
Note: In Next.js 16+,
middleware.tshas been renamed toproxy.ts(the functionality is the same). For Next.js 15 and earlier, usemiddleware.ts. This guide uses both terms interchangeably. See Next.js Proxy Documentation for migration details.
How Middleware/Proxy Works in Azure Static Web Apps:
Azure Static Web Apps uses a proxy architecture where:
Critical Configuration: Static Web Apps validates deployment by accessing /.swa/health.html. You must exclude .swa routes from middleware/proxy and custom routing:
// proxy.ts (Next.js 16+) or middleware.ts (Next.js 15 and earlier)
import { NextResponse, NextRequest } from 'next/server'
export const config = {
matcher: [
/*
* Match all request paths except:
* - .swa (Azure Static Web Apps health checks)
* - _next/static (static assets)
* - api (API routes handled separately)
*/
'/((?!.swa|_next/static|api).*)',
],
}
// Next.js 16+ (proxy.ts)
export function proxy(request: NextRequest) {
// Your proxy/middleware logic here
// This runs on the backend App Service instance
return NextResponse.next()
}
// Next.js 15 and earlier (middleware.ts)
// export function middleware(request: NextRequest) {
// return NextResponse.next()
// }
Important Notes:
agilitypreviewkey parameternext.config.js, not staticwebapp.config.jsonFor redirects in next.config.js:
module.exports = {
async redirects() {
return [
{
source: '/((?!.swa).*)/old-route',
destination: '/new-route',
permanent: false,
},
]
},
};
Reference: Next.js Proxy Documentation (Next.js 16+) | Next.js Middleware Documentation (Next.js 15 and earlier) | Azure Static Web Apps Next.js Support
Environment variables are needed at both build time and runtime:
In GitHub Repository (for build):
AGILITY_GUIDAGILITY_API_FETCH_KEYAGILITY_API_PREVIEW_KEYAGILITY_SECURITY_KEYAGILITY_LOCALES (e.g., en-us)In Azure Static Web App (for runtime):
In GitHub Actions workflow:
- name: Build And Deploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
action: "upload"
app_location: "/"
env:
AGILITY_GUID: ${{ secrets.AGILITY_GUID }}
AGILITY_API_FETCH_KEY: ${{ secrets.AGILITY_API_FETCH_KEY }}
AGILITY_API_PREVIEW_KEY: ${{ secrets.AGILITY_API_PREVIEW_KEY }}
AGILITY_SECURITY_KEY: ${{ secrets.AGILITY_SECURITY_KEY }}
AGILITY_LOCALES: ${{ secrets.AGILITY_LOCALES }}
### Troubleshooting Build Errors
If you encounter build errors during deployment, check the following:
1. **Verify API Credentials** - Ensure your `AGILITY_GUID` and API keys are correct in GitHub Secrets
2. **Check Environment Variables** - Make sure all required Agility CMS variables are set in both GitHub Secrets and Azure Configuration
3. **Review Build Logs** - Check the GitHub Actions logs or Azure deployment logs for specific error messages
4. **Local Build Test** - Run `npm run build` locally to catch build errors before deploying:
```bash
npm run build
.env.local - If testing locally, ensure your .env.local file exists and contains all required variablesCommon issues:
AGILITY_API_FETCH_KEY matches your Live API Key from Agility CMSAGILITY_GUID is correctFor better performance and control, link a dedicated backend:
Note: Linked backends require Standard plan or above
Azure App Service provides full Node.js hosting with complete control over the runtime environment. Use this when you need features not available in Static Web Apps or want more control.
Step 1: Local Setup
Note: If you're deploying from the Agility CMS Next.js Starter or Comprehensive Demo, you can skip the
create-next-appstep and proceed directly to Step 2.
# If starting from scratch (not recommended for Agility CMS projects)
npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
# OR clone and use an Agility starter:
git clone https://github.com/agility/agilitycms-nextjs-starter.git
cd agilitycms-nextjs-starter
Step 2: Add Required Files
Next.js on App Service requires specific configuration files:
Create .env.local for local development:
# Your Agility CMS Instance credentials
AGILITY_GUID=<your-guid>
AGILITY_API_FETCH_KEY=<your-live-api-key>
AGILITY_API_PREVIEW_KEY=<your-preview-api-key>
AGILITY_SECURITY_KEY=<your-security-key>
AGILITY_LOCALES=en-us
Create web.config (for IIS on Windows) or skip for Linux:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\\/debug[\\/]?" />
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
<iisnode watchedFiles="web.config;*.js"/>
</system.webServer>
</configuration>
Create server.js (critical for App Service):
const { createServer } = require("http");
const next = require("next");
const port = process.env.PORT || 8080;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
createServer((req, res) => {
handle(req, res);
}).listen(port, (err) => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
Step 3: Set Node Version
Add to your App Service Configuration:
Application Setting:
WEBSITE_NODE_DEFAULT_VERSION = 18.17.1
Step 4: Build the Application
npm run build
This creates the .next folder required for deployment.
Step 5: Deploy via Local Git
# Initialize git repository
git init
git add .
git commit -m "Initial commit"
# Add Azure remote (get URL from Portal → Deployment Center)
git remote add azure https://<sitename>.scm.azurewebsites.net:443/<sitename>.git
# Push to Azure
git push azure master
Oryx will automatically detect Next.js and build your application.
Critical Fix for Symlink Issue:
When deploying via GitHub Actions or Azure DevOps (ZipDeploy), NPM symlinks may break. Update your package.json:
{
"scripts": {
"start": "node_modules/next/dist/bin/next start"
}
}
GitHub Actions Workflow:
name: Build and Deploy Next.js to Azure App Service
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: '18.x'
- name: npm install and build
run: |
npm install
npm run build --if-present
env:
AGILITY_GUID: ${{ secrets.AGILITY_GUID }}
AGILITY_API_FETCH_KEY: ${{ secrets.AGILITY_API_FETCH_KEY }}
AGILITY_API_PREVIEW_KEY: ${{ secrets.AGILITY_API_PREVIEW_KEY }}
AGILITY_SECURITY_KEY: ${{ secrets.AGILITY_SECURITY_KEY }}
AGILITY_LOCALES: ${{ secrets.AGILITY_LOCALES }}
- name: Zip all files
# IMPORTANT: Include .next hidden folder
run: zip -r next.zip ./* .next
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: node-app
path: next.zip
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: Download artifact
uses: actions/download-artifact@v2
with:
name: node-app
- name: Deploy to Azure Web App
uses: azure/webapps-deploy@v2
with:
app-name: 'your-app-name'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: next.zip
- name: Cleanup
run: rm next.zip
Azure Pipelines YAML:
trigger:
- main
variables:
azureSubscription: 'your-subscription'
webAppName: 'your-app-name'
vmImageName: 'ubuntu-latest'
stages:
- stage: Build
jobs:
- job: Build
pool:
vmImage: $(vmImageName)
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js'
- script: |
npm install
npm run build --if-present
displayName: 'npm install and build'
env:
AGILITY_GUID: $(AGILITY_GUID)
AGILITY_API_FETCH_KEY: $(AGILITY_API_FETCH_KEY)
AGILITY_API_PREVIEW_KEY: $(AGILITY_API_PREVIEW_KEY)
AGILITY_SECURITY_KEY: $(AGILITY_SECURITY_KEY)
AGILITY_LOCALES: $(AGILITY_LOCALES)
- task: ArchiveFiles@2
displayName: 'Archive files'
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
- publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
artifact: drop
- stage: Deploy
dependsOn: Build
jobs:
- deployment: Deploy
environment: 'production'
pool:
vmImage: $(vmImageName)
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
displayName: 'Deploy to Azure App Service'
inputs:
azureSubscription: $(azureSubscription)
appType: webAppLinux
appName: $(webAppName)
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
Set environment variables in two places:
Azure Portal → App Service → Configuration → Application settings:
AGILITY_GUID=<your-guid>
AGILITY_API_FETCH_KEY=<your-fetch-key>
AGILITY_API_PREVIEW_KEY=<your-preview-key>
AGILITY_SECURITY_KEY=<your-security-key>
AGILITY_LOCALES=en-us
GitHub/DevOps workflow (for build-time variables) - shown in examples above
App Service Log Stream is useful for debugging deployment issues and monitoring your application in real-time:
You can also use Azure CLI:
# View live logs
az webapp log tail --name myapp --resource-group myResourceGroup
Tip: Enable Application Logging in Configuration → General settings to see more detailed logs.
Note: In Next.js 16+,
middleware.tshas been renamed toproxy.ts(the functionality is the same). For Next.js 15 and earlier, usemiddleware.ts. This guide uses both terms interchangeably.
How Middleware/Proxy Works in Azure App Service:
Azure App Service runs Next.js as a standard Node.js application:
Middleware/Proxy Configuration:
// proxy.ts (Next.js 16+) or middleware.ts (Next.js 15 and earlier)
import { NextResponse, NextRequest } from 'next/server'
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (image optimization)
* - favicon.ico (favicon file)
*/
'/((?!_next/static|_next/image|favicon.ico).*)',
],
}
// Next.js 16+ (proxy.ts)
export function proxy(request: NextRequest) {
// Proxy/middleware runs in your Node.js process
// Full access to request/response, headers, cookies, etc.
// Example: Handle Agility CMS preview mode
const previewKey = request.nextUrl.searchParams.get('agilitypreviewkey')
if (previewKey) {
// Handle preview logic
}
return NextResponse.next()
}
// Next.js 15 and earlier (middleware.ts)
// export function middleware(request: NextRequest) {
// return NextResponse.next()
// }
Important Notes:
agilitypreviewkey parameterCustom Server Considerations:
If you're using a custom server.js, middleware/proxy still works normally. The custom server uses app.getRequestHandler(), which automatically processes middleware/proxy:
// server.js
const { createServer } = require("http");
const next = require("next");
const app = next({ dev });
const handle = app.getRequestHandler(); // This includes middleware/proxy processing
app.prepare().then(() => {
createServer((req, res) => {
handle(req, res); // Middleware/Proxy runs automatically
}).listen(port);
});
Reference: Next.js Proxy Documentation (Next.js 16+) | Next.js Middleware Documentation (Next.js 15 and earlier) | Next.js Deployment Options
Docker provides maximum flexibility and works with any container orchestration platform.
Important: For Agility CMS projects, ensure environment variables are passed at build time and runtime.
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies
COPY package.json package-lock.json* ./
RUN npm ci
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Accept build arguments for Agility CMS
ARG AGILITY_GUID
ARG AGILITY_API_FETCH_KEY
ARG AGILITY_API_PREVIEW_KEY
ARG AGILITY_SECURITY_KEY
ARG AGILITY_LOCALES
# Set environment variables for build
ENV AGILITY_GUID=$AGILITY_GUID
ENV AGILITY_API_FETCH_KEY=$AGILITY_API_FETCH_KEY
ENV AGILITY_API_PREVIEW_KEY=$AGILITY_API_PREVIEW_KEY
ENV AGILITY_SECURITY_KEY=$AGILITY_SECURITY_KEY
ENV AGILITY_LOCALES=$AGILITY_LOCALES
RUN npm run build
# Production image
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
// next.config.js
module.exports = {
output: 'standalone',
}
# Build the Docker image with Agility CMS build arguments
docker build -t nextjs-app \
--build-arg AGILITY_GUID=<your-guid> \
--build-arg AGILITY_API_FETCH_KEY=<your-fetch-key> \
--build-arg AGILITY_API_PREVIEW_KEY=<your-preview-key> \
--build-arg AGILITY_SECURITY_KEY=<your-security-key> \
--build-arg AGILITY_LOCALES=en-us \
.
# Run locally with Agility CMS environment variables
docker run -p 3000:3000 \
-e AGILITY_GUID=<your-guid> \
-e AGILITY_API_FETCH_KEY=<your-fetch-key> \
-e AGILITY_API_PREVIEW_KEY=<your-preview-key> \
-e AGILITY_SECURITY_KEY=<your-security-key> \
-e AGILITY_LOCALES=en-us \
nextjs-app
# Login to Azure
az login
# Create container registry
az acr create --resource-group myResourceGroup \
--name myregistry --sku Basic
# Build and push to ACR with Agility CMS build arguments
az acr build --registry myregistry \
--image nextjs-app:latest \
--build-arg AGILITY_GUID=<your-guid> \
--build-arg AGILITY_API_FETCH_KEY=<your-fetch-key> \
--build-arg AGILITY_API_PREVIEW_KEY=<your-preview-key> \
--build-arg AGILITY_SECURITY_KEY=<your-security-key> \
--build-arg AGILITY_LOCALES=en-us \
.
# Create App Service with container and environment variables
az webapp create --resource-group myResourceGroup \
--plan myAppServicePlan --name myapp \
--deployment-container-image-name myregistry.azurecr.io/nextjs-app:latest
# Set Agility CMS environment variables
az webapp config appsettings set --name myapp \
--resource-group myResourceGroup \
--settings \
AGILITY_GUID=<your-guid> \
AGILITY_API_FETCH_KEY=<your-fetch-key> \
AGILITY_API_PREVIEW_KEY=<your-preview-key> \
AGILITY_SECURITY_KEY=<your-security-key> \
AGILITY_LOCALES=en-us
# Create container instance with Agility CMS environment variables
az container create --resource-group myResourceGroup \
--name nextjs-app --image myregistry.azurecr.io/nextjs-app:latest \
--dns-name-label nextjs-app-unique --ports 3000 \
--environment-variables \
AGILITY_GUID=<your-guid> \
AGILITY_API_FETCH_KEY=<your-fetch-key> \
AGILITY_LOCALES=en-us \
--secure-environment-variables \
AGILITY_API_PREVIEW_KEY=<your-preview-key> \
AGILITY_SECURITY_KEY=<your-security-key>
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nextjs-app
spec:
replicas: 3
selector:
matchLabels:
app: nextjs-app
template:
metadata:
labels:
app: nextjs-app
spec:
containers:
- name: nextjs-app
image: myregistry.azurecr.io/nextjs-app:latest
ports:
- containerPort: 3000
env:
- name: AGILITY_GUID
valueFrom:
secretKeyRef:
name: agility-secrets
key: guid
- name: AGILITY_API_FETCH_KEY
valueFrom:
secretKeyRef:
name: agility-secrets
key: fetch-key
- name: AGILITY_API_PREVIEW_KEY
valueFrom:
secretKeyRef:
name: agility-secrets
key: preview-key
- name: AGILITY_SECURITY_KEY
valueFrom:
secretKeyRef:
name: agility-secrets
key: security-key
- name: AGILITY_LOCALES
value: "en-us"
---
apiVersion: v1
kind: Service
metadata:
name: nextjs-app
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 3000
selector:
app: nextjs-app
Create the Kubernetes secret first:
kubectl create secret generic agility-secrets \
--from-literal=guid=<your-guid> \
--from-literal=fetch-key=<your-fetch-key> \
--from-literal=preview-key=<your-preview-key> \
--from-literal=security-key=<your-security-key>
Deploy:
kubectl apply -f deployment.yaml
trigger:
- main
variables:
dockerRegistry: 'myregistry.azurecr.io'
imageName: 'nextjs-app'
tag: '$(Build.BuildId)'
stages:
- stage: Build
jobs:
- job: Build
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Docker@2
displayName: 'Build and Push'
inputs:
containerRegistry: '$(containerRegistry)'
repository: '$(imageName)'
command: 'buildAndPush'
Dockerfile: '**/Dockerfile'
tags: |
$(tag)
latest
- stage: Deploy
jobs:
- deployment: Deploy
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebAppContainer@1
inputs:
azureSubscription: 'mySubscription'
appName: 'my-nextjs-app'
containers: '$(dockerRegistry)/$(imageName):$(tag)'
Cause: Missing web.config or server.js files
Solution: Add both files to your root directory (see App Service section above)
Cause: NPM symlinks broken during ZipDeploy
Solution: Update package.json start script:
{
"scripts": {
"start": "node_modules/next/dist/bin/next start"
}
}
Cause: .next folder not included in deployment
Solution:
.next in zip: zip -r next.zip ./* .nextbuild script exists in package.jsonCause: Port configuration mismatch
Solution: Use port 8080 (default) or ensure PORT environment variable is set correctly
Cause: Too many small files being uploaded individually
Solution: Zip artifacts before upload (see GitHub Actions examples above)
Cause: Middleware/Proxy or routing blocking /.swa/health.html
Solution: Exclude .swa routes from middleware/proxy matcher (see Static Web Apps section). Ensure your proxy.ts (Next.js 16+) or middleware.ts (Next.js 15 and earlier) excludes .swa routes:
export const config = {
matcher: ['/((?!.swa).*)'],
}
// next.config.js
module.exports = {
output: 'standalone',
}
For App Service, use Azure CDN for static assets:
// next.config.js
module.exports = {
assetPrefix: process.env.NODE_ENV === 'production'
? 'https://your-cdn.azureedge.net'
: '',
}
Important for Agility CMS: ALL image caching for Agility websites is handled at the Edge via Agility's CDN. You do not need to configure server-side image caching for Agility CMS images.
Important for Agility CMS: ALL image optimization for Agility websites is done at the Edge (via Agility's CDN) and does NOT need to happen on the web server. The Next.js Image component will work with Agility images, but you should configure it to use Agility's CDN domains and can disable server-side optimization.
// next.config.js
module.exports = {
images: {
// Use Agility CMS CDN domains
domains: ['cdn.agilitycms.com', 'your-cdn.azureedge.net'],
formats: ['image/avif', 'image/webp'],
// Optional: Disable Next.js image optimization since Agility handles it at the Edge
// unoptimized: true, // Uncomment if you want to rely entirely on Agility's Edge optimization
},
}
Using the Next.js Image component with Agility CMS:
import Image from 'next/image';
// Agility images are already optimized and cached at the Edge
<Image
src={agilityImage.url} // Direct URL from Agility CMS
alt={agilityImage.label}
width={agilityImage.width}
height={agilityImage.height}
// No need for server-side optimization or caching - Agility handles both at the Edge
/>
For App Service, compression is enabled by default. For Docker:
// next.config.js
module.exports = {
compress: true,
}
For Static Web Apps:
For App Service:
# Enable Application Insights
az webapp config appsettings set --name myapp \
--resource-group myResourceGroup \
--settings APPINSIGHTS_INSTRUMENTATIONKEY=<your-key>
For Docker: Add to Dockerfile:
ENV APPLICATIONINSIGHTS_CONNECTION_STRING=<connection-string>
App Service:
# View live logs
az webapp log tail --name myapp --resource-group myResourceGroup
Container Apps:
# View container logs
az containerapp logs show --name myapp --resource-group myResourceGroup
# Enable managed identity for App Service
az webapp identity assign --name myapp --resource-group myResourceGroup
Static Web Apps has built-in authentication providers:
App Service use Easy Auth:
az webapp auth update --name myapp --resource-group myResourceGroup \
--enabled true --action LoginWithAzureActiveDirectory
@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/agility-security-key/)
@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/agility-api-preview-key/)
Example for creating secrets in Key Vault:
# Create Key Vault
az keyvault create --name myvault --resource-group myResourceGroup
# Store Agility CMS secrets
az keyvault secret set --vault-name myvault \
--name agility-security-key --value <your-security-key>
az keyvault secret set --vault-name myvault \
--name agility-api-preview-key --value <your-preview-key>
# Grant App Service access to Key Vault
az keyvault set-policy --name myvault \
--object-id $(az webapp identity show --name myapp \
--resource-group myResourceGroup --query principalId -o tsv) \
--secret-permissions get
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin',
},
],
},
]
},
}
Recommendation: Start with Static Web Apps Free tier, upgrade to Standard when you need linked backends or more bandwidth.
| Scenario | Recommended Solution |
|---|---|
| Small app, mostly static with some SSR | Static Web Apps Free |
| Need custom backend services | Static Web Apps Standard |
| Complex app with background jobs | App Service |
| Microservices architecture | Docker + AKS |
| Need VNet integration | App Service or Container Apps |
| Maximum control over infrastructure | Docker |
| Lowest cost for simple apps | Static Web Apps |
| CI/CD with GitHub | Static Web Apps (easiest) |
| CI/CD with Azure DevOps | App Service or Docker |
# Install Azure Static Web Apps CLI
npm install -g @azure/static-web-apps-cli
# Run locally with SWA CLI
swa start http://localhost:3000
# Deploy using Azure CLI
az webapp up --name myapp --runtime "NODE:18-lts"
# Build and run
docker build -t nextjs-app .
docker run -p 3000:3000 nextjs-app
This guide covers the essential deployment patterns for Next.js on Azure. Choose the approach that best fits your application's requirements, team expertise, and budget constraints.