Explained - Problems deploying a production NextJS application via Microsoft Azure DevOps Pipelines

Viewed 1423

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 Answers

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*
Related