How I deployed a Next.js app to an Azure App Service

Share on:

Deploying the this website to Azure App Service was a relatively straightforward process I still ran in to some few issues which it why I decided this should be my first post. Here's a step-by-step guide on how I accomplished it.

Step 1: Creating the Next.Js app

First I ran the npx create-next-app@latest to create a new Next.Js application. These are the settings I used:

Next Config

Then I was lazy again and used ChatGPT a bunch to create the application it self.

Step 2: Set Up Azure App Service

Next, I took the lazy option again and created an Azure App Service using the Azure Portal instead of creating a bicep template for it.

These was the configurations that I used: App Service Config

Then I used the build in option to automatically setup continuous deployment from GitHub. This will generate a YAML file that contains all the GitHub Actions to build and deploys the code to the Azure App Service. This process also automatically adds the client-id, tenant-id and subscription-id as secrets such that GitHub Actions needs to deploy code. App Service Config

Step 3: GitHub Actions

This the GitHub Action YAML file that Azure generated

# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - app-boletio-prod

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js version
        uses: actions/setup-node@v3
        with:
          node-version: '20.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present

      - name: Zip artifact for deployment
        run: zip release.zip ./* -r

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v4
        with:
          name: node-app
          path: release.zip

  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: 'Production'
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
    permissions:
      id-token: write #This is required for requesting the JWT

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v4
        with:
          name: node-app

      - name: Unzip artifact for deployment
        run: unzip release.zip
      
      - name: Login to Azure
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_7D5A50CF708D480280E09F1B1BCE49AF }}
          tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_27309C20B33D4BBDB9F0471F8DD159F3 }}
          subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_C438AEEA429340BB869882F6D65A7F00 }}

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v3
        with:
          app-name: 'app-boletio-prod'
          slot-name: 'Production'
          package: .

I didn't make any changes to the pipeline event though the process could be optimized a fair bit, but it looked like it would work (or so I thought... 😅).

Step 4: Getting the application to run

Until now the process of getting the application deployed to an Azure App Service was pretty straightforward but at this point I started running in to some minor problems.

First, I needed to add some startup configurations the both the App service and the application it self. First I created a ecosystem.config.js to the root of my Next.js project.

module.exports = {
  apps: [
    {
      name: "boletio",
      script: "node_modules/next/dist/bin/next",
      args: "start",
      env: {
        PORT: process.env.PORT || 3000,
      },
      watch: false,
      autorestart: true,
    },
  ],
};


Then I added this pm2 --no-daemon start ecosystem.config.js as a Startup Command for the Azure App service

App Service Startup I also set the App Service to Always on to On while I was here

However the application wasn't able to start App Service Error

I went to the Log steam to get an idea of what the error was.

App Service Log

As seen in the log message Could not find a production build in the '.next' directory it looks like it couldn't find a build in the .next folder.

2024-08-02T10:49:51.2364108Z 10:49:51 0|boletio  |   â–² Next.js 14.2.5
2024-08-02T10:49:51.2364108Z 10:49:51 0|boletio  |   - Local:        http://localhost:8080
2024-08-02T10:49:51.2374667Z 10:49:51 0|boletio  |  ✓ Starting...
2024-08-02T10:49:52.1090972Z 10:49:52 0|boletio  | Error: Could not find a production build in the '.next' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id
2024-08-02T10:49:52.1262233Z 10:49:52 0|boletio  |     at setupFsCheck (/home/site/wwwroot/node_modules/next/src/server/lib/router-utils/filesystem.ts:157:13)
2024-08-02T10:49:52.1262488Z 10:49:52 0|boletio  |     at initialize (/home/site/wwwroot/node_modules/next/src/server/lib/router-server.ts:93:21)
2024-08-02T10:49:52.1262519Z 10:49:52 0|boletio  |     at Server.<anonymous> (/home/site/wwwroot/node_modules/next/src/server/lib/start-server.ts:293:28)
2024-08-02T10:49:52.1477259Z 2024-08-02T10:49:52: PM2 log: App [boletio:0] exited with code [1] via signal [SIGINT]
2024-08-02T10:49:52.1562441Z 10:49:52 PM2        | App [boletio:0] exited with code [1] via signal [SIGINT]
2024-08-02T10:49:52.1575668Z 2024-08-02T10:49:52: PM2 log: App [boletio:0] starting in -fork mode-
2024-08-02T10:49:52.1586106Z 10:49:52 PM2        | App [boletio:0] starting in -fork mode-
2024-08-02T10:49:52.1887666Z 2024-08-02T10:49:52: PM2 log: App [boletio:0] online
2024-08-02T10:49:52.1974425Z 10:49:52 PM2        | App [boletio:0] online

I went ahead and changed the GitHub Actions workflow to include the missing files. I change this action:

      - name: Zip artifact for deployment
        run: zip release.zip ./* -r

To this:

      - name: Zip artifact for deployment
        run: zip -r release.zip .next static package.json posts

That not only made the build and deploy process much faster but also made it possible for the Next.js app to start. HURRAH🥳🎉