22 C
Tuesday, April 16, 2024

Deploy Next.js App on Azure App Service with Azure DevOps Pipelines

In this requirement. We are using CI / CD from the GitHub repository and creating an artifact and Deploying Next Js on Azure App Service, which is using “Strapi – Open source Node.js Headless CMS” from an Azure App Service using Azure DevOps Releases. We are using staging slots, and upon Approval, it will deploy to the production slot.

Good to know – CI/CD are Continuous Integration, Continuous Delivery, and Continuous Deployment

Pipelines are integrated with GitHub Repo for CI / CD and are deployed to UAT (Azure App Service Staging Slot ), where they can be tested. If it gets approved (Approval Gates), After testing, it will be deployed to the Production Slot in the same App Service. Otherwise, the change can be rejected.

Ran the node js locally using [ npm run dev / npm run build ], which went through successfully.

A similar article for CI / CD is done for Azure App Service using Azure DevOps.

Azure DevOps Pipelines for App Service with GitHub – Azure365Pro.com
Azure DevOps Pipelines for Static Web Apps with GitHub – Azure365Pro.com

As you see below, we are using a Git hub source repository. First Deploys to a Standard UAT (Azure App Service Staging Slot ). If an approver approves changes, It’s deployed to a Production Azure App Service.

Now you can see the releases happening on UAT and Production environments without any manual steps. Let’s see how to implement the same.

Create New Pipeline – Choose Github (YAML) as I use GitHub in my case

Choosing Node.js Express Web App on Linux on Azure

Using below YAML – azure-pipelines.yml

# Node.js Express Web App to Linux on Azure
# Build a Node.js Express app and deploy it to Azure as a Linux web app.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript

- main


  # Azure Resource Manager connection created during pipeline creation
  azureSubscription: 'd4200000-0000-0000-0000-9bb00007d505'

  # Web app name
  webAppName: 'az-az365pro-pr-fc-web-rg'

  # Environment name
  environmentName: 'az-az365pro-pr-fc-web-rg'

  # Agent VM image name
  vmImageName: 'ubuntu-latest'

- stage: Build
  displayName: Build stage
  - job: Build
    displayName: Build
      vmImage: $(vmImageName)

    - task: NodeTool@0
        versionSpec: '18.x'
      displayName: 'Install Node.js'

    - script: |
        npm install
        npm run build --if-present
#       npm run test --if-present
      displayName: 'npm install, build and test'

    - task: ArchiveFiles@2
      displayName: 'Archive files'
        rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
        includeRootFolder: false
        archiveType: zip
        archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
        replaceExistingArchive: true

    - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
      artifact: drop

Having two files in my repo

Added ecosystem.config.js. We need to start this separately

  module.exports = {
  apps: [
      name: "az-az365pro-web",
      script: "./node_modules/next/dist/bin/next",
      args: "start -p " + (process.env.PORT || 3000),
      watch: false,
      autorestart: true,

if bin location is in different place


  module.exports = {
  apps: [
      name: "az-az365pro-web",
      script: "./node_modules/.bin/next",
      args: "start -p " + (process.env.PORT || 3000),
      watch: false,
      autorestart: true,

As you can see below, running this store my build to Azure Artifact where Azure Releases can use them.

Now as by build is released with the zip sent to my drop location
Now my source in releases is artifact

The startup command set to

pm2 start /home/site/wwwroot/ecosystem.config.js --no-daemon

if pm2 start command doesn’t exist – build will fail

root@fff533200d57:/home/site/wwwroot# npm run dev
npm info using npm@9.6.4
npm info using node@v18.16.1

> fulcrum-dashboard@0.1.0 dev
> next dev

throw err;

Error: Cannot find module ‘../build/output/log’
Require stack:
– /home/site/wwwroot/node_modules/.bin/next
at Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)
at Module._load (node:internal/modules/cjs/loader:922:27)
at Module.require (node:internal/modules/cjs/loader:1143:19)
at require (node:internal/modules/cjs/helpers:110:18)
at Object. (/home/site/wwwroot/node_modules/.bin/next:6:54)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) {
requireStack: [ ‘/home/site/wwwroot/node_modules/.bin/next’ ]


Staging is set to a Staging slot.

Staging slot is created for use – Add Slot – Staging

In my next stage Production Slot is chosen with the same settings

Now my App Service URLs are ready with below

Staging – (Upon Approval, Goes to Prod)
Production –

Continous Deployment Trigger is enabled so that UAT is pushing on every new build.

Satheshwaran Manoharan
Satheshwaran Manoharanhttps://www.azure365pro.com
Award-winning Technology Leader with a wealth of experience running large teams and diversified industry exposure in cloud computing. From shipping lines to rolling stocks.In-depth expertise in driving cloud adoption strategies and modernizing systems to cloud native. Specialized in Microsoft Cloud, DevOps, and Microsoft 365 Stack and conducted numerous successful projects worldwide. Also, Acting as a Technical Advisor for various start-ups.

Related Articles


Please enter your comment!
Please enter your name here

× How can I help you?