Automate Your Next.js Deployment to Azure: A Step-by-Step Guide
Introduction
Ever struggled with deploying a Next.js application to Azure Web Apps? You're not alone. While there are various guides available, many either lack depth or don't follow best practices. This article aims to fill that gap by providing a comprehensive, step-by-step guide to deploying your Next.js application on Azure, the right way.
We'll cover everything from setting up your development environment to the actual deployment, including essential configurations and GitHub Actions. Along the way, we'll also touch on some advanced tips for future improvements. This guide assumes you have a basic familiarity with Next.js and Azure App Services.
Ready to dive in?
Prerequisites
Before diving into the deployment process, make sure you have the following set up:
- Azure Subscription: Required to create and manage resources on Azure. Sign up here if you don't have one.
- GitHub Account: Necessary for setting up GitHub Actions for CI/CD. Create an account here.
- Node.js 18.x LTS: The runtime environment for your Next.js application. Download here.
- Yarn: A package manager that will help you manage project dependencies. Get it here.
- Azure CLI: Command-line tool to interact with Azure services. Install instructions.
- Git: Version control system to manage your codebase. Download here.
Creating the Next.js Project
Note: The current version of Next.js at the time of writing this article is 13.5.4
. CLI commands and configurations may change in future versions, so be sure to check the official Next.js documentation for the most up-to-date information.
Initializing the Project
Let's start things off by creating a new Next.js project. I'll show you two ways to do this with yarn, based on how you started your project.
# If you are starting from scratch
yarn create next-app project-name
# If you started with a repository, do this inside
yarn create next-app .
For this article, I've chosen specific settings while creating the application. However, these choices won't affect the overall guide.
√ Would you like to use TypeScript? ... Yes
√ Would you like to use ESLint? ... Yes
√ Would you like to use Tailwind CSS? ... Yes
√ Would you like to use `src/` directory? ... Yes
√ Would you like to use App Router? (recommended) ... Yes
√ Would you like to customize the default import alias (@/*)? ... No
Configuring Next.js Settings
The first step in our deployment journey is to configure the Next.js application to generate a standalone folder. This folder will contain only the essential files needed for a production-ready deployment, including specific files from the node_modules
directory.
To achieve this, add the following setting to your next.config.js
file:
module.exports = {
output: 'standalone',
}
What does this configuration do?
- Standalone Folder: The setting will generate a
.next/standalone
folder that can be deployed independently, eliminating the need to installnode_modules
. - Minimal Server.js: Alongside the standalone folder, a minimal
server.js
file is also generated. This can be used as an alternative to thenext start
command.
Additional Notes
- The minimal
server.js
does not include thepublic
and.next/static
folders by default. These are typically managed by a Content Delivery Network (CDN). - If you still wish to include these folders, you can manually copy them to
standalone/public
andstandalone/.next/static
. Once copied, theserver.js
file will serve these automatically.
Configuring Azure Resources
Using Azure CLI
To set up the necessary Azure resources, we'll be using the Azure Command Line Interface (CLI). Below are the commands you'll need to run, along with explanations for each:
# Log in to your Azure account
az login
This command ensures you are logged into your Azure account. If you're not logged in, it will prompt you to do so.
# Create a new resource group in a specific location (West Europe in this case)
az group create --location westeurope --resource-group next-azure-deployment-rg
This command creates a new resource group named next-azure-deployment-rg
in the westeurope
location.
# Create an App Service plan
az appservice plan create --resource-group next-azure-deployment-rg --name nextazuredeployplan --is-linux --sku P1V3
Here, we create an App Service plan named nextazuredeployplan
using a Linux host and a specific SKU (P1V3
).
# List available runtimes for the web app
az webapp list-runtimes
This command lists all the available runtimes that you can use for your web app.
# Create a web app with a Node.js runtime
az webapp create --resource-group next-azure-deployment-rg --name nextazuredeploywebapp --plan nextazuredeployplan --runtime "NODE:18-lts"
This creates a new web app named nextazuredeploywebapp
, using the previously created App Service plan and a Node.js runtime.
# Retrieve the publishing profile for the web app
az webapp deployment list-publishing-profiles --name nextazuredeploywebapp --resource-group next-azure-deployment-rg --xml
Finally, this command retrieves the publishing profile for your web app in XML format, which you'll need for deploying your app.
Using Azure Portal
While this guide won't cover using the Azure Portal in detail, if you're not familiar with it, I recommend sticking with the Azure CLI steps above. Alternatively, you can seek help from a colleague or refer to Microsoft Learn for more in-depth tutorials.
Setting Up GitHub Actions
In this section, we'll walk you through setting up GitHub Actions to automate the deployment of your Next.js application to Azure. We'll cover adding secrets to GitHub, configuring the workflow file, and making adjustments for environment variables and package management.
Adding Secrets to GitHub
First, let's add the necessary secrets to your GitHub repository:
- Copy the output from the last Azure CLI command (
az webapp deployment list-publishing-profiles
) to your clipboard. - Navigate to
Settings > Secrets and variables > Actions
in your GitHub repository. - Click on
New repository secret
, name itAZURE_WEBAPP_PUBLISH_PROFILE
, and paste the copied publish profile into the value field.
Configuring the Workflow File
Initial Setup
You can either navigate to the Actions
tab in your GitHub repository and choose the "Deploy Node.js to Azure Web App" workflow, or you can manually create a workflow file named azure-webapps-node.yml
inside a folder called .github/workflows
in your repository.
File Structure
Here's a preview of what your workflow file will look like:
on:
push:
branches: [ "main" ]
workflow_dispatch:
env:
AZURE_WEBAPP_NAME: your-app-name
AZURE_WEBAPP_PACKAGE_PATH: '.'
NODE_VERSION: '14.x'
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- 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@v3
with:
name: node-app
path: .
deploy:
permissions:
contents: none
runs-on: ubuntu-latest
needs: build
environment:
name: 'Development'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v3
with:
name: node-app
- name: 'Deploy to Azure WebApp'
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
Environment Variables
Update the following environment variables in your workflow file:
AZURE_WEBAPP_NAME
: Should match the name of the web app you created using Azure CLI.AZURE_WEBAPP_PACKAGE_PATH
: Change from'.'
to'./.next/standalone'
.NODE_VERSION
: Change from'14.x'
to'18.x'
.
Switching from NPM to Yarn
Replace the NPM steps in your build job with Yarn. Here's how:
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: yarn install, build, and copy files
run: |
yarn --frozen-lockfile
yarn build
cp -R ./public ./.next/standalone/public
cp -R ./.next/static ./.next/standalone/.next/static
Adjusting Artifact Paths
After switching from NPM to Yarn, you'll also need to adjust the paths for both uploading and downloading artifacts in your GitHub Actions workflow. This ensures that the correct build artifacts are used during the deployment process.
Updating Upload-Artifact Step
Locate the Upload artifact for deployment job
step in your workflow file and update the path
parameter. Here's how you can change it:
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v3
with:
name: node-app
path: ./.next/standalone # Updated path
Updating Download-Artifact Step
Similarly, find the Download artifact from build job
step and update it to align with the new upload path. Here's the updated code:
- name: Download artifact from build job
uses: actions/download-artifact@v3
with:
name: node-app # Make sure this name matches the upload step
path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} # Updated path
By making these adjustments, you ensure that the correct build artifacts are uploaded and downloaded, facilitating a smooth deployment process. The path
parameter in the download step is particularly important because the next step, azure/webapps-deploy@v2
, uses the env.AZURE_WEBAPP_PACKAGE_PATH
to upload the files to the web app.
Final Workflow File
After making all the changes, your final workflow file should look like this:
on:
push:
branches: ["main"]
workflow_dispatch:
env:
AZURE_WEBAPP_NAME: nextazuredeploywebapp
AZURE_WEBAPP_PACKAGE_PATH: "./.next/standalone"
NODE_VERSION: "18.x"
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: "yarn"
- name: yarn install, build, and copy files
run: |
yarn --frozen-lockfile
yarn build
cp -R ./public ./.next/standalone/public
cp -R ./.next/static ./.next/standalone/.next/static
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v3
with:
name: node-app
path: ./.next/standalone
deploy:
permissions:
contents: none
runs-on: ubuntu-latest
needs: build
environment:
name: "Development"
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v3
with:
name: node-app
path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
- name: "Deploy to Azure WebApp"
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
Deploying to Azure
Changing the Startup Command
Alright, let's dive in. You might recall that our application no longer boots up with the usual npm run start
or next start
. Instead, we're using a server.js
file. By default, Azure Web App is set to kick things off with npm run start
. We need to switch that up to node server.js
.
To make this change, fire up your Azure CLI and run this single command:
az webapp config set --resource-group next-azure-deployment-rg --name nextazuredeploywebapp --startup-file "node server.js"
Once you hit enter, the Web App will restart and come to life with our new startup command. Neat, right?
Verifying Deployment
Time to see our hard work in action. Head over to your Azure Web App. You can find it at a URL that looks something like this: http://nextazuredeploywebapp.azurewebsites.net/
.
And there you have it! Your Next.js app is live and kicking. Plus, every time you commit changes to your main branch, GitHub Actions will take the stage, triggering a new workflow and deploying an updated version of your app.
Further Reading and Next Steps
Optimize Your Workflow
Next.js Caching
After running next build
in a workflow like this, Next.js will hint that CI-caching could speed up the build process. Check out the Next.js documentation to see how you can easily integrate this into your GitHub Action.
Zip Your Artifacts
If you've noticed, uploading artifacts can be time-consuming. A quick fix? Zip all the files and upload a single zip-file as an artifact. In my casual tests, this slashed the upload and download times from minutes to mere seconds.
CDN for Static Files
If your app is heavy on static files, consider using a Content Delivery Network (CDN). You can pack your public and static files into a separate artifact and upload them to a CDN in a different stage. Where to host your CDN? That's your call, Azure or elsewhere. I'll dive into this in a future article.
What's Next?
Explore Azure Features
Azure has a plethora of services that can further enhance your app. From databases to AI services, the sky's the limit.
Dive Deeper into Next.js
Next.js is a powerful framework with features like server-side rendering and static site generation. The more you know, the better your app will be.
Automate Testing
If you're serious about your project, automated testing is a must. Look into setting up a CI/CD pipeline that includes a testing stage.
Wrapping It Up
And there you have it! We've journeyed through the intricacies of Next.js and Azure, and now you should have a fully deployed Next.js application running smoothly on Azure Web Apps. Not only that, but you've also set up an automated deployment pipeline using GitHub Actions.
By diving deep into the Next.js documentation, we've uncovered a straightforward yet powerful way to get your Next.js app up and running on Azure. Along the way, we've also touched on workflow optimizations and future steps you can take to further enhance your project.
So, what's next? The ball's in your court. Whether it's optimizing your workflow, exploring Azure's vast array of services, or diving deeper into Next.js, the road ahead is full of possibilities.