Deploy NextJS 13 to Azure App Service with GitHub Action

Didik Mulyadi
6 min readJun 16, 2023

--

Photo by Christopher Gower on Unsplash

Hello guys!

In this article, we are going to deploy the Next.js application into Azure App Service.

Why do we deploy it to the Azure App Service and not to the Vercel? It’s because company needs. The client’s company wanna deploy the app in the same cloud project / infrastructure.

This article is an old way, you can still read this article for the details, for an efficient way please check this

App Service

Introducing

App Service is a fully managed service with built-in infrastructure maintenance, security patching, and scaling. So we don’t need to worry about high-volume transactions or visitors.

Besides that, it already has built-in continuous integration, continuous delivery (CI/CD), and zero-downtime deployments. So we can easily integrate it with source control management e.g. GitHub.

The above feature that I mentioned is a basic feature, others cloud service e.g. Google / AWS also providing the same feature.

We can find the App Service by going to portal.azure.com and then searching the App Service in the search bar. You will see the UI below. Click it you will be redirected to a new page.

Portal Azure Home

Create an App Service

On this page, I have 2 app services. We will show you the create form, but I will not really create it.

Let’s click the “+ Create” button and choose “+ Web App”.

App Service Creation Form

Let’s choose Public with Code, Node 18 LTS as runtime, Linux as Operating System, and the name here is “didik-medium”.

App Service “didik-medium”

Let’s focus on the menu.

App Service

In this article, we will only use 3 menus.
Overview: used for showing the app service condition/status.
Deployment Center: used for integrating the app service with source control management
Configuration: used for adding an env or custom startup script.

Deployment Center

Basically, we just need to fill Organization, Repository, and Branch that we want to use

Deployment Center with GitHub

It will automatically create a new workflow in your GitHub repository. Here is the workflow file

name: Build and deploy Node.js app to Azure Web App - didik-medium

on:
push:
branches:
- development
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

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

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

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

deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

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

- name: 'Deploy to Azure Web App'
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: 'didik-medium'
slot-name: 'Production'
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_**** }}
package: .

There are 2 jobs “build” and “deploy”, the reason the build job uses upload and the deploy job uses download is to send the build file between jobs.

and the reason why they split the job into 2 parts is because when the deploy error we should not do a build again.

GitHub

Go to the workflow file, we should modify the workflow to make the GitHub Action faster.

Modify Workflow

We will modify the workflow for some points

1. Converting 2 jobs into 1 job

We don't need 2 jobs just because we don't want to do build again when the deploy job is an error. The build time vs. uploading and downloading time has a big difference, in this case, the build time is shorter than uploading and downloading.

So we will remove the uploading and downloading process.

2. We need to create .env file

With this Next.js can use the environment variable. I store the value in the GitHub variable, for the sensitive data we can use GitHub secret.

I have tried to use the configuration variable, but the environment variable is still undefined , even if I use a public runtime configuration.

3. The directory or file with the prefix . (dot) will be skipped

So we need to add 1 step to create a zip file and mention the .next and .env files before the azure uploading process.

4. Add path’s ignore

We don’t want unneeded deployment. make sure we are skipping the files.

Here’s the final workflow

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

on:
push:
branches:
- development
paths-ignore:
- "README.md"
- ".husky"
- ".github/**"
- "**/*.csv"
- .env*
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Node.js version
uses: actions/setup-node@v1
with:
node-version: "18.x"

- name: create env file
env:
NEXT_PUBLIC_API_BASE_URL: ${{ vars.DEV_NEXT_PUBLIC_API_BASE_URL }}
run: |
touch .env
echo NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL >> .env
echo NODE_ENV=development >> .env

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

- name: Zip all files for upload between jobs
# IMPORTANT: .next is a hidden folder and will NOT be included in the zip unless we specify it
# To fix: /home/site/wwwroot/node_modules/.bin/next: 1: ../next/dist/bin/next: not found
run: zip next.zip ./* .next .env -qr

- name: "Deploy to Azure Web App"
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: "didik-medium" # IMPORTANT: Use your data
slot-name: "Production" # IMPORTANT: Use your data
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_6ED781 }} # IMPORTANT: Use your data
package: next.zip

Source Code

For production, Azure App Service recommended to use PM2.

The Azure App Service is already provided the pm2, but to trigger the Azure app service to use the pm2, we should create a new file ecosystem.config.js in the root.

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

Azure App Service — Configuration

We are gonna change the start-up script to use the pm2

pm2 --no-daemon start  ecosystem.config.js
Configuration — General Settings

--

--