Before:
After:
The way Azure sets it up for you when you go through the steps of deploying a new Azure Web App service is objectively horrible. This is what Azure pushes to your repo:
# 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
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Set up Node.js version
uses: actions/setup-node@v3
with:
node-version: "20.x"
- uses: pnpm/action-setup@v4
with:
version: 9
- name: npm install, build, and test
run: |
npm install
npm run build --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 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID }}
- name: "Deploy to Azure Web App"
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: "app-name"
slot-name: "Production"
package: .
So what’s happening here is that we
- Build the project in GitHub actions
- Zip it
- Send it to Azure
- Unzip it
- Run it
The problem is that we’re zipping it together with node_modules
folder. So sending it and unzipping it takes a LOT of time. 🤯
I’m not sure why they recommend it this way, but after reading a bunch of poorly worded and outdated Azure guides and some short StackOverflow posts, I’ve figured out the way a Node.js app should be deployed.
Essentially we need to only send the code from GitHub to Azure and build right there. For that you don’t need to do any post-install scripts or anything, Azure App Service does it for you, here are the steps that it goes through as soon as the code reaches the service.
But it doesn’t do that by default, so you need to enable it. You do that by setting the environment variable SCM_DO_BUILD_DURING_DEPLOYMENT
to true
in Settings > Environment variables in Azure Portal.
Once we’ve done that, we now need to remove the build step from our GitHub Actions workflow and we’re done. Here is how the template looks like now. 👇
# 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
on:
push:
branches:
- main
workflow_dispatch:
jobs:
prepare:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- 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: prepare
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 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID }}
- name: "Deploy to Azure Web App"
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: "web-app"
slot-name: "Production"
package: .
There are also a few “gotchas” I’ve noticed when deploying to Azure Web App service:
- Forget yarn or pnpm. npm only. There is no way you can get those into App Service environment. And if there is, I really don’t think it’s worth the effort. Azure is using Kudu so you better know its ins and outs, I feel like, if you decide to install additional packages there.
-
HOST=0.0.0.0 PORT=8080
before your start script, which in my (Astro) case wasnode ./dist/server/entry.mjs
. You can’t pick the port. I tried. Didn’t work. - Node version
20.14
is what it’s currently running in Azure as of the moment of writing this post. It was hard to find, they don’t print it in the dropdown when you select the environment when deploying the App Service. I got a few errors because the Node version wasn’t what my app was expecting 🤷♂️
And most important tip: upgrade your service to paid. The whole build and deploy process is unbearably slow on a free plan. But, frankly, that’s not an Azure specific issue, that’s most of PaaS nowadays, isn’t it?
Hope that helps! 😎
Top comments (0)