webapps-deploy: Deployment is *very* slow (nodeJS with publish profile)

I’m using a workflow file adapted from “build and deploy a Node.js Web app to Azure using publish profile”.

(side remark) : I had issues with symbolic links too (see https://github.com/Azure/webapps-deploy/issues/54#issuecomment-694259266)

Upon analysis, it seems that the whole copy of the node_modules folders to the webroot of the webapp takes ages (10-12 minutes for the Azure Webapp deploy step !) It’s very inefficient to process and send many little files over network.

=> Shouldn’t the final npm install (and npm run build – I’m using typescript) commands be executed on the target machine instead of a github action container ?

IMHO, the workflow should be like this for the “nodeJS with publish profile” scenario :

  • the github action should only send source files to the targeted runtime machine
  • npm install, npm run build and npm start should be done on the targeted runtime machine
  • It’s up to the developer to ensure that is app is building correctly before calling azure/webapps-deploy

Related questions :

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 38
  • Comments: 87 (8 by maintainers)

Commits related to this issue

Most upvoted comments

@sfabriece you can provide directly zip as a input to action or folder location which will be zipped in action

- name: 'Run Azure webapp deploy action using publish profile credentials'
      uses: azure/webapps-deploy@v2
      with:
        app-name: <app-name>
        publish-profile: ${{ secrets.azureWebAppPublishProfile }}
        package: <provide path of your folder or zip file>

Closing the issue as the question has been addressed and for performance we already have npm install in the sample workflow which optimises the time

Our deployments are taking over 30 mins!

This is an incredibly poor experience for my first attempt at deploying a project to Azure.

My builds are taking longer than 10 minutes!

Netlify manages this in under 1 minute, on the FREE tier.

The workflow described above runs in ~10 minutes, out of which the deploy part is the longest.

On my second attempt, I configured the workflow to deploy and run from ZIP, which is much faster - total time ~3 minutes (~1:45s build, ~30s deploy)

Step 1, in Azure configure the app to run from package, and disable the Oryx build (Github actions builds everything anyway)

WEBSITE_RUN_FROM_PACKAGE=1
SCM_DO_BUILD_DURING_DEPLOYMENT=false

Step 2, use this Github workflow that creates a zip after build (including node_modules)

# 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

    steps:
      - uses: actions/checkout@v2

      - name: Set up Node.js version
        uses: actions/setup-node@v2
        with:
          node-version: '16'
          cache: 'npm'

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

      # Zip artifacts to speed things up
      - name: Zip artifact for deployment
        run: zip release.zip ./* -qr

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        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 }}

    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: 'my-app'
          slot-name: 'Production'
          publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_38C3A64C5A4A49F792EA62F5DCBC13ED }}
          package: release.zip

An added benefit to this method is that you can download the Action artifacts later if you need to reproduce the environment exactly for debugging, as the Github job artifact named node-app here is exactly what is running on the server. There is no chance that the server would download a newer npm version or anything like that.

Plus it might reduce cold starts

Thanks @AmrutaKawade !

Ideally, hints could be given in documentations and workflow files (for example: https://github.com/Azure/actions-workflow-samples/blob/master/AppService/node.js-webapp-on-azure.yml), as every nodeJS app have the same behavior on this, and people using the actions-workflow-samples will encounter the same issue.

@stefanoz-cloud sure

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

on:
  push:
    branches:
      - main
  workflow_dispatch:

env:
  AZURE_WEBAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root
  NODE_VERSION: '16.x' # set this to the node version to use

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: npm install, build
        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@v3
        with:
          name: node-app
          path: release.zip

  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: unzip artifact for deployment
        run: unzip release.zip
      - name: 'Deploy to Azure WebApp'
        id: 'id'
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'NAME'
          slot-name: 'SLOT'
          publish-profile: 'SECRET'
          package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}

+1. Very slow deployments.

The deployment is slow when there are many files to deploy. When uploading a Zip file it’s been extracted on the target App service (Linux). This takes a while. I think this is just a serial operation. We’re deploying about ~10.000 static web files (html, js, css) to our App service and it takes ~20min. It’s an output of a Gatsby build.

The only way to speed up deployment time is to decrease the artifact size.

Not sure how other platforms handle it, but I’ve only seen this delay deploying to Azure.

Vercel, digitalocean and a few others do not have this issue, same codebase.

I was using Azure Devops to build and package my NextJS app with fully acceptable build times (~1 minute with caching). Before zipping I removed all files except the ones needed (including not deleting node_modules). I agree that npm install should be run on the target machine but I never managed to get it working using .deployment and deploy.sh files, so I opted for the easy route and adding node_modules to the bundle. This had no effect on deploy times which were still horrendous. It could take 20 minutes or more using the Zip deploy.

However when I changed the WEBSITE_RUN_FROM_PACKAGE application setting to 1 my deploys took 20 seconds. So for everyone banging their heads: go to configuration and set:

WEBSITE_RUN_FROM_PACKAGE=1

Seconding this one I am having the same problem and the solutions provided above do not seem to be working. I am using the standard file automatically generated by the Azure portal as well.

Hi guys i was able to fix the issue with zipping artifact and unzipping you only need to add the following to your GitHub workflow file :

after npm install , build , test

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

Inside upload artifact change path from . to “release.zip”

after download artifact add

- name: unzip artifact for deployment . run: unzip release.zip

And thats it this should improve deployment time by a lot =)

Could somebody please explain how this can be solved?

I am using the latest version of the script automatically installed by the Azure portal when using the deployment center. It seems to be running npm install on Github then uploading every file individually. Did anyone find a way to (A) zip them up and unzip them on the target webapp or (B) just do the npm install on the target webapp (although that would stop you from doing any other steps using github actions like testing)?

Takes 30+ min for my deploy step on Azure, just < 5min on DigitalOcean app service. Sucks that microsoft hasn’t provided any guidance here.

I came across a solution for this accidentally. Basically split the workflow into 2 jobs: build & deploy (just like the default workflow template does), but copy between them only the needed files without node_modules. To do this, I create a zip at the end of the build and unzip it at the beginning of the deploy step.

I had to do this zipping anyway because Github was complaining that I was moving more than 10000 files between the jobs. https://stackoverflow.com/a/69402502/281252 https://github.com/actions/upload-artifact#too-many-uploads-resulting-in-429-responses

With this workflow, a Node+Angular app takes 1m for build and ~8m for deployment since Oryx/Kudu runs npm install and build again. Perhaps with a custom build, we might be able to pass it a zip with node_modules and everything built, and just run it without duplicating the build steps… Though there are some node_modules.tar.gz improvements that we might lose

Example workflow

# 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

    steps:
      - uses: actions/checkout@v2

      - name: Set up Node.js version
        uses: actions/setup-node@v2
        with:
          node-version: '16'
          cache: 'npm' # github actions can cache node_modules automatically to speed the build by about ~45s

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

      # Zip artifacts to speed things up
      - name: Zip artifact for deployment
        run: zip release.zip ./* -qr -x "node_modules/*" "dist/*"
      # We exclude node_modules and dist because the Azure Oryx/Kudu deploy runs npm install and rebuilds everything anyway...
      # If we zip them we just increase the size of the zip file and it's not needed

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        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 }}

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

      - name: Unzip artifact for deployment
        run: |
          unzip -q release.zip
          rm release.zip

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

@sfabriece you can provide directly zip as a input to action or folder location which will be zipped in action

- name: 'Run Azure webapp deploy action using publish profile credentials'
      uses: azure/webapps-deploy@v2
      with:
        app-name: <app-name>
        publish-profile: ${{ secrets.azureWebAppPublishProfile }}
        package: <provide path of your folder or zip file>

@sfabriece custom deloyment scripts should work. Do not include node_modules in zip. Instead add .deployment and deploy.cmd / deploy.sh files at root of your repo with npm install command. it will execute npm install on target machine

some examples https://docs.microsoft.com/en-us/azure/app-service/configure-language-nodejs?pivots=platform-windows#run-gruntbowergulp https://github.com/projectkudu/kudu/wiki/Deployment-hooks#easiest-way-to-get-started-with-a-custom-deployment-script https://github.com/projectkudu/kudu/wiki/Custom-Deployment-Script

going by the deployment logs in azure, a lot of time taken is azure not being very smart about handling zip deploys, and the workflow staying connected during that. WHen you deploy a zip file it puts it in a /tmp directory, then extracts it to /tmp/zipdeploy/extracted, THEN copies the files over to your destination. it would be a whole lot faster if it just extracted into the target directly

Website_Run_From_Package is fine if your app can run from inside the zip file. If you’re using Incremental Static Regeneration to build new pages on the fly from data stores, etc. then this won’t work for you, and you’ll need to unpack the Zip file at one end or the other.

As far as I can tell this is still an issue for greenfield projects. I am just trying out Azure for the first time today, but I am mostly choosing defaults for everything.

  1. I created a GitHub repo with no code in it.
  2. I created a node web app in Azure, connected to my repo. Azure created a deployment action for me(everything default except I changed the default branch to a branch called ci, since I didn’t want the app to redeploy for every change to main)
  3. I installed a node framework that comes with a bunch of node_modules out of the box. Hello world works on my local machine. I merge the code to the ci branch and it automatically creates a deploy job.
  4. Deploys now take 15 minutes+, and I can see from the logs that thousands of files from node_modules are being processed.

In the end I switched to Azure DevOps pipeline. cut deployment time from approx… 1 hr. to about 8 min. Good job GitHub actions…

Here is the workflow which runs fast.

name: FE on Azure App Service CI/CD

on:
  workflow_dispatch:
  push:
    branches:
      - main
      - develop
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - main
      - develop

env:
  AZURE_WEBAPP_NAME: fe-app-dev
  AZURE_WEBAPP_PACKAGE_PATH: build
  NODE_VERSION: 20.x

permissions:
  contents: read

jobs:
  build-and-deploy:
    name: Build and deploy
    runs-on: ubuntu-latest
    env:
      NODE_ENV: production
      VITE_MY_VAR: ${{ secrets.MY_VAR }}
    environment:
      name: dev
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js version
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: yarn install and build
        run: |
          yarn install --frozen-lockfile --production=false
          yarn build

      - name: Deploy to Azure Web App
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v3
        with:
          app-name: ${{ env.AZURE_WEBAPP_NAME }}
          publish-profile: ${{ secrets.AZURE_WEB_APP_PUBLISH_PROFILE }}
          package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}

I was using Azure Devops to build and package my NextJS app with fully acceptable build times (~1 minute with caching). Before zipping I removed all files except the ones needed (including not deleting node_modules). I agree that npm install should be run on the target machine but I never managed to get it working using .deployment and deploy.sh files, so I opted for the easy route and adding node_modules to the bundle. This had no effect on deploy times which were still horrendous. It could take 20 minutes or more using the Zip deploy. However when I changed the WEBSITE_RUN_FROM_PACKAGE application setting to 1 my deploys took 20 seconds. So for everyone banging their heads: go to configuration and set: WEBSITE_RUN_FROM_PACKAGE=1

this solve the delay to deployment but adding this, breaking application and getting this error - code: ‘MODULE_NOT_FOUND’,

Screenshot04454_1

fixed this issue by changing the zip command to

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

after it, the app runs perfect and deploying is fast too

commenting to keep reference. having a similar situation of ~15min build time even with a zipped directory.

i tried excluding the node_modules folder from the zip and then doing npm install in the startup command. i verified it was targeting the correct directory and that it was correctly installing modules but the apiapp just says there was an error.

may try using a bash script in the /home directory, as some others in the thread suggest, but i cant see why that would work as opposed to a startup command. maybe the order of operations is such that the CI/CD does its build and runs the api.js file then runs the startup command? but still when the api fails wouldnt it retry and then work? why does it continue to fail?

Hi Everyone, we’ve made changes to the workflow file created by default from the portal. Also, for others who are already using the workflow file you can add 2 steps as mentioned in this comment. Please reply on the other issue #229 if you’re still facing the issue.

Making the zip file from GitHub build process and doing unzip on the Azure side seems to work much better. I used the steps as mentioned by MoatezNG above.

In upload artifacts section include only build/ directory

  • name: Upload artifact for deployment job

      uses: actions/upload-artifact@v2
      with:
        name: node-app
        path: build/
    

Similar issue (https://github.com/Azure/webapps-deploy/issues/229) is going on, we will be fixing this soon. Closing this issue for now and the progress can be tracked from the mentioned issue.

Any update on this issue? I stopped the process after more than 3hs. I’m using the default file created by Azure Portal.

Finally got it working. Build is now ~5m 51s down from ~13m 3s. Still not ideal as my app is pretty small, but I think it’s as good as I’m going to get for a Node app given all the packages they usually pull in.

Issue ended being a mix of node_modules unnecessarily included in the zip, package.json & package-lock.json not being included in the zip (Onyx couldn’t tell what Node version to use), and finally the zip folder containing a folder of files instead of just the files (release.zip/build/... vs. release.zip/...).

I think the main issue/confusion is that the sample yml provided attempts to copy over the directory contents after npm has already installed all the dependencies. I would suggest that it’s updated to not do that.

Since I didn’t have any unit tests to run, I no longer run npm install|build|test and skip straight to the zip & deploy. Those with tests could probably just run > rm node_modules or something so it’s not zipped & copied.

For those using React my final deployment yml looked like below:

# 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 & deploy

on:
  push:
    branches:
      - master
  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: '14.x'

      # Zip artifacts to speed things up
      - name: Zip artifact for deployment
        run: zip -r release.zip *

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        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 }}

    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: 'my-appservice-name'
          slot-name: 'Production'
          publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_XXX }}
          package: release.zip

Yes, as @0gust1 mentioned, you can also use npm ci. When using npm ci you should remove the cache: 'npm' configuration, because it always performs a clean install so caching node_modules only wastes time.

Besides the functional difference between npm ci and npm install, in my experience caching and npm install works just as fast if not faster than npm ci. ymmv

I have this same issue. Express server REST API and a React Client front end. The node_modules would be best built on the server rather than added in the zip deployment. Though I’m not really seeing much of a resource on how to do that. Any suggestions?

Can someone point to an example on how to setup npm install as post deployment step. Not much luck with google. This was possible with Azure DevOps release task but can’t make it work with GitHub action. Tried the suggestion from @AmrutaKawade without success. looks like the Kudu is skipping .deployment and .deploy.sh files for some reason. This is written in logs

Kudu sync from: '/tmp/zipdeploy/extracted' to: '/home/site/wwwroot'
Ignoring: .deployment
Ignoring: deploy.sh

Having extremely slow deployment if entire node_modules is packaged (more than 40 min).