Next JS deployment with Devops and PM2

Quick write-up for simple NextJS deployments using Azure Devops and PM2

Build pipeline

# azure-pipelines.yml

trigger:
  - main
  - develop
pool:
  name: linux
steps:
  - task: CopyFiles@2
    inputs:
      Contents: |
        **/*
        !.git/**/*        
      targetFolder: "$(Build.ArtifactStagingDirectory)"
      cleanTargetFolder: true # Optional
      overWrite: true # Optional
  - task: NodeTool@0
    inputs:
      versionSpec: "16.x"
    displayName: "Install Node.js"
  - script: |
      npm install
      npm run build      
    displayName: "npm install and build"
  - task: ArchiveFiles@2
    inputs:
      rootFolderOrFile: "$(Build.ArtifactStagingDirectory)"
      includeRootFolder: false
      archiveType: "tar"
      archiveFile: "$(Build.ArtifactStagingDirectory)/$(Build.BuildId).tar"
      replaceExistingArchive: true
  - task: PublishBuildArtifacts@1
    inputs:
      pathToPublish: "$(Build.ArtifactStagingDirectory)/$(Build.BuildId).tar"
      artifactName: drop

Configure server

Run pm2 and deployment scripts as an unprivileged user, such as “nodejs”. groupadd nodejs; useradd -G nodejs nodejs

Set up deploy scripts

# /usr/local/bin/deploy-myapp-prod.sh

#!/bin/bash
cd /var/www/myapp-prod && \
tar xf /opt/deploy/$1 -C /var/www/myapp-prod && \
npm install && npm run build && \
pm2 restart myapp
# /usr/local/bin/deploy-myapp-staging.sh

#!/bin/bash
cd /var/www/myapp-staging && \
tar xf /opt/deploy/$1 -C /var/www/myapp-staging && \
npm install && npm run build && \
pm2 restart myapp-staging

Remember to chmod a+x /usr/local/bin/deploy-myapp-staging.sh /usr/local/bin/deploy-myapp-prod.sh

Install PM2

su - nodejs

npm install pm2@latest -g

# Manually copy the source code to /var/www/myapp-prod and /var/www/myapp-staging so that we can set up the apps in PM2

# run prod app on port 3000
cd /var/www/myapp-prod
pm2 start npm --name "myapp-prod" -- start

# run staging app on port 3001
cd /var/www/myapp-staging
pm2 start npm --name "myapp-staging" -- start -- -p 3001

Set up service connection

Azure Devops: Project settings -> Service connections

  1. New service connection
  2. SSH
  3. Configure hostname etc, make sure to use SSH keys for authentication. Log in as the unprivileged user.

Set up release pipelines

Each push to develop branch should initiate a deployment to the staging environment.

Azure Devops: Pipelines -> Releases -> New release pipeline

  1. Start with an “Empty job”
  2. Add an artifact
    1. Source type: build
    2. Project: your project
    3. Source: your build pipeline
  3. Stages
    1. Start with an “Empty job”
    2. Name the pipeline “Staging”
    3. Continuous deployment trigger
      1. Enabled
      2. Build branch filters
      3. Include Build branch: develop
    4. Tasks:
      1. Securely copy files to the remote machine
        1. SSH service connection: your service connection
        2. Source folder: $(System.DefaultWorkingDirectory)/[_myapp]/drop
        3. Target folder: /opt/deploy
      2. Run shell commands on remote machine /usr/local/bin/deploy-myapp-staging.sh $(Build.BuildId).tar

Each push to main branch should initiate a deployment to the production environment.

Add a new release pipeline called “Production”.

  • Include Build branch: main
  • Shell command: /usr/local/bin/deploy-myapp-prod.sh $(Build.BuildId).tar

Add a comment