Deploying a NextJS App to Azure Web Apps
Azure sometimes feels a step behind when it comes to deploying JS apps. There are certainly many different ways to deploy apps, but I was hoping for something I wouldn’t have to manage, so no VMs or Container set ups, preferably a web app. Deploying NextJS to a Web App though, is a pain.
Here’s how I did it.
Enable Standalone mode in next.js
This outputs only the necessary files and dependencies as well as a server.js file which will run the site. These will be output into a /standalone folder. You’ll also need to copy static files into this directory if serving them as well.
Add this to your next.config.js file:
module.exports = { output: ‘standalone’ }
Azure Web App Node.js containers include pm2, a process manager for node that will start and manage running the node app. We’ll run the server.js script with pm2 on the Azure Web App, which will automatically restart the script if it stops or crashes.
For this we need an ecosystem.config.js file to identify the script and set up the options:
module.exports = {
apps: [
{
name: "next-web",
script: "server.js",
watch: false,
autorestart: true,
},
],
};
Set up an ADO Pipeline that will run the build, copy any static files if needed and archive them for the release pipeline.
trigger:
branches:
include:
- main
paths:
include:
- web/*
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js'
- script: |
npm ci
npx playwright install --with-deps chromium
npm run test:e2e
npm run build
ls -alh
cp -r ./public ./.next/standalone
cp -r ./.next/static ./.next/standalone/.next/static
cp ./ecosystem.config.js ./.next/standalone/ecosystem.config.js
rm ./.next/standalone/package.json
workingDirectory: 'web'
displayName: 'npm install, build and test'
- task: CopyFiles@2
inputs:
SourceFolder: './web/.next/standalone'
Contents: '**/*'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
cleanTargetFolder: true
- task: PublishTestResults@2
displayName: 'Publish test results'
inputs:
searchFolder: 'web/test-results'
testResultsFormat: 'JUnit'
testResultsFiles: 'e2e-junit-results.xml'
mergeTestResults: true
failTaskOnFailedTests: true
testRunTitle: 'Web End-To-End Tests'
condition: succeededOrFailed()
- task: ArchiveFiles@2
displayName: 'Archive web files'
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
replaceExistingArchive: true
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: Tubular Web App'
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
Then in the release pipeline, we can create a web app deploy job and run:
pm2 start /home/site/wwwroot/ecosystem.config.js --no-daemon
That should do it.