Configure a Node.js app for Azure App Service

Node.js apps must be deployed with all the required npm dependencies. The App Service deployment engine automatically runs npm install --production for you when you deploy a Git repository, or when you deploy a Zip package with build automation enabled. If you deploy your files using FTP/S, however, you need to upload the required packages manually.

This guide provides key concepts and instructions for Node.js developers who deploy to App Service. If you've never used Azure App Service, follow the Node.js quickstart and Node.js with MongoDB tutorial first.

Show Node.js version

To show the current Node.js version, run the following command in the Cloud Shell:

az webapp config appsettings list --name <app-name> --resource-group <resource-group-name> --query "[?name=='WEBSITE_NODE_DEFAULT_VERSION'].value"

To show all supported Node.js versions, navigate to https://<sitename>.scm.azurewebsites.net/api/diagnostics/runtime or run the following command in the Cloud Shell:

az webapp list-runtimes --os windows | grep NODE

To show the current Node.js version, run the following command in the Cloud Shell:

az webapp config show --resource-group <resource-group-name> --name <app-name> --query linuxFxVersion

To show all supported Node.js versions, run the following command in the Cloud Shell:

az webapp list-runtimes --os linux | grep NODE

Set Node.js version

To set your app to a supported Node.js version, run the following command in the Cloud Shell to set WEBSITE_NODE_DEFAULT_VERSION to a supported version:

az webapp config appsettings set --name <app-name> --resource-group <resource-group-name> --settings WEBSITE_NODE_DEFAULT_VERSION="~16"

Note

This example uses the recommended "tilde syntax" to target the latest available version of Node.js 16 runtime on App Service.

Since the runtime is regularly patched and updated by the platform, we don't recommend that you target a specific minor version/patch as these are not guaranteed to be available due to potential security risks.

Note

You should set the Node.js version in your project's package.json. The deployment engine runs in a separate process that contains all the supported Node.js versions.

To set your app to a supported Node.js version, run the following command in the Cloud Shell:

az webapp config set --resource-group <resource-group-name> --name <app-name> --linux-fx-version "NODE|14-lts"

This setting specifies the Node.js version to use, both at runtime and during automated package restore in Kudu.

Note

You should set the Node.js version in your project's package.json. The deployment engine runs in a separate container that contains all the supported Node.js versions.

Get port number

Your Node.js app needs to listen to the right port to receive incoming requests.

In App Service on Windows, Node.js apps are hosted with IISNode, and your Node.js app should listen to the port specified in the process.env.PORT variable. The following example shows how to do that in a simple Express app:

App Service sets the environment variable PORT in the Node.js container, and forwards the incoming requests to your container at that port number. To receive the requests, your app should listen to that port using process.env.PORT. The following example shows how to do that in a simple Express app:

const express = require('express')
const app = express()
const port = process.env.PORT || 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

Customize build automation

If you deploy your app by using Git, or by using zip packages with build automation enabled, the App Service build automation steps through the following sequence:

  1. Run custom script, if one is specified by PRE_BUILD_SCRIPT_PATH.
  2. Run npm install without any flags, which includes npm preinstall and postinstall scripts and also installs devDependencies.
  3. Run npm run build if a build script is specified in your package.json.
  4. Run npm run build:azure if a build:azure script is specified in your package.json.
  5. Run custom script if specified by POST_BUILD_SCRIPT_PATH.

Note

As is noted in the npm docs, scripts named prebuild and postbuild run before and after build, respectively, if specified. preinstall and postinstall run before and after install, respectively.

PRE_BUILD_COMMAND and POST_BUILD_COMMAND are environment variables that are empty by default. To run pre-build commands, define PRE_BUILD_COMMAND. To run post-build commands, define POST_BUILD_COMMAND.

The following example uses the two variables to specify a series of commands, separated by commas.

az webapp config appsettings set --name <app-name> --resource-group <resource-group-name> --settings PRE_BUILD_COMMAND="echo foo, scripts/prebuild.sh"
az webapp config appsettings set --name <app-name> --resource-group <resource-group-name> --settings POST_BUILD_COMMAND="echo foo, scripts/postbuild.sh"

For information about additional environment variables to customize build automation, see Oryx configuration.

For more information on how App Service runs and builds Node.js apps in Linux, see Oryx documentation: How Node.js apps are detected and built.

Configure Node.js server

The Node.js containers come with PM2, a production process manager. You can configure your app to start with PM2, with npm start, or with a custom command.

Tool Purpose
Run with PM2 Recommended - Production or staging use. PM2 provides a full-service app management platform.
Run with npm start Development use only.
Run with a custom command Either development or staging.

Run with PM2

The container automatically starts your app with PM2 when one of the common Node.js files is found in your project:

  • bin/www
  • server.js
  • app.js
  • index.js
  • hostingstart.js
  • One of the following PM2 files: process.json or ecosystem.config.js

You can also configure a custom start file with the following extensions:

  • A .js file
  • A PM2 file with the extension .json, .config.js, .yaml, or .yml

Note

Starting from Node 14 LTS, the container doesn't automatically start your app with PM2. To start your app with PM2, set the startup command to pm2 start <.js-file-or-PM2-file> --no-daemon. Be sure to use the --no-daemon argument because PM2 needs to run in the foreground for the container to work properly.

To add a custom start file, run the following command in the Cloud Shell:

az webapp config set --resource-group <resource-group-name> --name <app-name> --startup-file "<filename-with-extension>"

Run with a custom command

App Service can start your app using a custom command, such as an executable like run.sh. For example, to run npm run start:prod, run the following command in the Cloud Shell:

az webapp config set --resource-group <resource-group-name> --name <app-name> --startup-file "npm run start:prod"

Run with npm start

To start your app using npm start, just make sure a start script is in the package.json file. For example:

{
  ...
  "scripts": {
    "start": "gulp",
    ...
  },
  ...
}

To use a custom package.json in your project, run the following command in the Cloud Shell:

az webapp config set --resource-group <resource-group-name> --name <app-name> --startup-file "<filename>.json"

Debug remotely

You can debug your Node.js app remotely in Visual Studio Code if you configure it to run with PM2, except when you run it using a .config.js,.yml, or .yaml.

In most cases, no extra configuration is required for your app. If your app is run with a process.json file (default or custom), it must have a script property in the JSON root. For example:

{
  "name"        : "worker",
  "script"      : "./index.js",
  ...
}

To set up Visual Studio Code for remote debugging, install the App Service extension. Follow the instructions on the extension page and sign in to Azure in Visual Studio Code.

In the Azure explorer, find the app you want to debug, right-click it and select Start Remote Debugging. Select Yes to enable remote debugging for your app. App Service starts a tunnel proxy for you and attaches the debugger. You can then make requests to the app and see the debugger pausing at break points.

Once finished with debugging, stop the debugger by selecting Disconnect. When prompted, you should select Yes to disable remote debugging. To disable it later, right-click your app again in the Azure explorer and select Disable Remote Debugging.

Access environment variables

In App Service, you can set app settings outside of your app code. Then you can access them using the standard Node.js pattern. For example, to access an app setting called NODE_ENV, use the following code:

process.env.NODE_ENV

Run Grunt/Bower/Gulp

By default, App Service build automation runs npm install --production when it recognizes that a Node.js app is deployed through Git, or through Zip deployment with build automation enabled. If your app requires any of the popular automation tools, such as Grunt, Bower, or Gulp, you need to supply a custom deployment script to run it.

To enable your repository to run these tools, you need to add them to the dependencies in package.json. For example:

"dependencies": {
  "bower": "^1.7.9",
  "grunt": "^1.0.1",
  "gulp": "^3.9.1",
  ...
}

From a local terminal window, change the directory to your repository root and run the following commands:

npm install kuduscript -g
kuduscript --node --scriptType bash --suppressPrompt

Your repository root now has two additional files: .deployment and deploy.sh.

Open deploy.sh and find the Deployment section, which looks like this:

##################################################################################################################################
# Deployment
# ----------

This section ends with running npm install --production. Add the code section you need to run the required tool at the end of the Deployment section:

See an example in the MEAN.js sample, where the deployment script also runs a custom npm install command.

Bower

This snippet runs bower install.

if [ -e "$DEPLOYMENT_TARGET/bower.json" ]; then
  cd "$DEPLOYMENT_TARGET"
  eval ./node_modules/.bin/bower install
  exitWithMessageOnError "bower failed"
  cd - > /dev/null
fi

Gulp

This snippet runs gulp imagemin.

if [ -e "$DEPLOYMENT_TARGET/gulpfile.js" ]; then
  cd "$DEPLOYMENT_TARGET"
  eval ./node_modules/.bin/gulp imagemin
  exitWithMessageOnError "gulp failed"
  cd - > /dev/null
fi

Grunt

This snippet runs grunt.

if [ -e "$DEPLOYMENT_TARGET/Gruntfile.js" ]; then
  cd "$DEPLOYMENT_TARGET"
  eval ./node_modules/.bin/grunt
  exitWithMessageOnError "Grunt failed"
  cd - > /dev/null
fi

Detect HTTPS session

In App Service, TLS/SSL termination happens at the network load balancers, so all HTTPS requests reach your app as unencrypted HTTP requests. If your app logic needs to check if the user requests are encrypted, inspect the X-Forwarded-Proto header.

Popular web frameworks let you access the X-Forwarded-* information in your standard app pattern. In Express, you can use trust proxies. For example:

app.set('trust proxy', 1)
...
if (req.secure) {
  // Do something when HTTPS is used
}

Access diagnostic logs

To access the console logs generated from inside your application code in App Service, turn on diagnostics logging by running the following command in the Cloud Shell:

az webapp log config --resource-group <resource-group-name> --name <app-name> --docker-container-logging filesystem --level Verbose

Possible values for --level are: Error, Warning, Info, and Verbose. Each subsequent level includes the previous level. For example: Error includes only error messages, and Verbose includes all messages.

Once diagnostic logging is turned on, run the following command to see the log stream:

az webapp log tail --resource-group <resource-group-name> --name <app-name>

If you don't see console logs immediately, check again in 30 seconds.

Note

You can also inspect the log files from the browser at https://<app-name>.scm.azurewebsites.net/api/logs/docker.

To stop log streaming at any time, type Ctrl+C.

You can access the console logs generated from inside the container.

First, turn on container logging by running the following command:

az webapp log config --name <app-name> --resource-group <resource-group-name> --docker-container-logging filesystem

Replace <app-name> and <resource-group-name> with the names appropriate for your web app.

Once container logging is turned on, run the following command to see the log stream:

az webapp log tail --name <app-name> --resource-group <resource-group-name>

If you don't see console logs immediately, check again in 30 seconds.

To stop log streaming at any time, type Ctrl+C.

You can also inspect the log files in a browser at https://<app-name>.scm.azurewebsites.net/api/logs/docker.

URL rewrites

When deploying Node.js apps on Azure App Service for Linux, you might need to handle URL rewrites directly within your application. This is particularly useful for ensuring specific URL patterns are redirected to the correct endpoints without relying on web server configurations. There are several ways to accomplish URL rewrites in Node.js. One example is through the express-urlrewrite package.

Monitor with Application Insights

Application Insights allows you to monitor your application's performance, exceptions, and usage without making any code changes. To attach the Application Insights agent, go to your web app in the portal, select Application Insights under Settings, and then select Turn on Application Insights. Next, select an existing Application Insights resource or create a new one. Finally, select Apply at the bottom. To instrument your web app using PowerShell, see these instructions

This agent will monitor your server-side Node.js application. To monitor your client-side JavaScript, add the JavaScript SDK to your project.

For more information, see the Application Insights extension release notes.

Troubleshooting

When a working Node.js app behaves differently in App Service or has errors, try the following:

  • Access the log stream.
  • Test the app locally in production mode. App Service runs your Node.js apps in production mode, so you need to make sure that your project works as expected in production mode locally. For example:
    • Depending on your package.json, different packages might be installed for production mode (dependencies vs. devDependencies).
    • Certain web frameworks might deploy static files differently in production mode.
    • Certain web frameworks might use custom startup scripts when running in production mode.
  • Run your app in App Service in development mode. For example, in MEAN.js, you can set your app to development mode at runtime by setting the NODE_ENV app setting.

You do not have permission to view this directory or page

After deploying your Node.js code to a native Windows app in App Service, you might see the message You do not have permission to view this directory or page in the browser when navigating to your app's URL. This is most likely because you don't have a web.config file. (See the template and an example.)

If you deploy your files by using Git, or by using ZIP deployment with build automation enabled, the deployment engine generates a web.config in the web root of your app (%HOME%\site\wwwroot) automatically if one of the following conditions is true:

  • Your project root has a package.json that defines a start script that contains the path of a JavaScript file.
  • Your project root has either a server.js or an app.js.

The generated web.config is tailored to the detected start script. For other deployment methods, add this web.config manually. Make sure the file is formatted properly.

If you use ZIP deployment (through Visual Studio Code, for example), be sure to enable build automation. It's not enabled by default. az webapp up uses ZIP deployment with build automation enabled.

robots933456 in logs

You may see the following message in the container logs:

2019-04-08T14:07:56.641002476Z "-" - - [08/Apr/2019:14:07:56 +0000] "GET /robots933456.txt HTTP/1.1" 404 415 "-" "-"

You can safely ignore this message. /robots933456.txt is a dummy URL path that App Service uses to check if the container is capable of serving requests. A 404 response simply indicates that the path doesn't exist, but it lets App Service know that the container is healthy and ready to respond to requests.

Next steps

Or, see additional resources:

Environment variables and app settings reference