2
votes

Having issues deploying your NextJS app to Microsoft Azure WebApp Service via the Azure DevOps Pipelines?

Issues can vary from Error: Cannot find module '../build/output/log' on next start to complete pipeline failures.

Do I need a server.js? What should my package.json scripts look like? How does a proper pipeline setup look like for this use case?

The solution is rather simple, you can find it listed below:

1

1 Answers

3
votes

If you are trying to deploy a NextJS production build successfully on an Azure WebApp using Azure DevOps' Pipelines, this is how I got it sorted out:

Pipeline deployment.yml:

trigger:
- master

variables:
  azureSubscription: 'confidential'
  webAppName: $(HORIZON)
  environmentName: $(HORIZON)
  vmImageName: 'ubuntu-latest'

stages:
- stage: Build
  displayName: Build Horizon
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
      
    steps:
    - task: NodeTool@0
      inputs:
        versionSpec: '10.x'
      displayName: 'Install node.js'

    - script: yarn
      displayName: 'Install dependencies'
    
    - script: yarn export
      displayName: 'Export production build'

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

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

- stage: Deploy
  displayName: Deploy Horizon
  dependsOn: Build
  condition: succeeded()
  jobs:
  - deployment: Deploy
    displayName: Deploy
    environment: $(environmentName)
    pool: 
      vmImage: $(vmImageName)
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureWebApp@1
            displayName: 'Azure Web App Deploy: $(HORIZON)'
            inputs:
              azureSubscription: $(azureSubscription)
              appType: webAppLinux
              appName: $(webAppName)
              runtimeStack: 'NODE|10.10'
              package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
              startUpCommand: 'yarn start'

The scripts in my package.json:

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "node_modules/next/dist/bin/next start -p $PORT",
    "export": "next build && next export"
  },

And my server.js (very important):

const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const port = process.env.PORT || 3000
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare()
.then(() => {
  const server = express()

  server.get('/p/:id', (req, res) => {
    const actualPage = '/post'
    const queryParams = { title: req.params.id }
    app.render(req, res, actualPage, queryParams)
  })

  server.get('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(port, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:' + port)
  })
})
.catch((ex) => {
  console.error(ex.stack)
  process.exit(1)
})

Extra .gitignore:

// .gitnignore

build/
.next/
out/
.env
.env.build
node_modules/
jspm_packages/
/.pnp
.pnp.js
dist/
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pids
*.pid
*.seed
*.pid.lock
lib-cov
coverage
.nyc_output
bower_components
.npm
*.tgz
.yarn-integrity
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*