App Service configuration and networking — startup, build, deployment auth, outbound IPs, VNet integration, Key Vault references

How an App Service app is configured to run and reach other services — Linux startup for Node.js (PORT, PM2, startup command), build automation settings, deployment methods and basic authentication, slot-sticky settings, inbound and outbound IP addresses (and Premium V4's lack of stable ones), regional VNet integration with outboundVnetRouting, access restrictions, health check, managed identity endpoints and Key Vault reference syntax, identity and refresh.

markdownreferencev176WR-H9ZXstage: buildtrust: officialservice: app-serviceinterface: iacinterface: cliApp Service Node.js startupprocess.env.PORTpm2 start --no-daemonaz webapp config set --startup-fileSCM_DO_BUILD_DURING_DEPLOYMENTPRE_BUILD_COMMANDPOST_BUILD_COMMANDOryx buildaz webapp deploybasicPublishingCredentialsPoliciesdeployment slot setting stickyoutboundIpAddressespossibleOutboundIpAddressesPremium V4 outbound IP not stableregional VNet integration /28 /26 MPSJMicrosoft.Web/serverFarms delegationoutboundVnetRouting allTrafficvnetRouteAllEnabled legacyWEBSITE_VNET_ROUTE_ALLaccess restrictionshealthCheckPathIDENTITY_ENDPOINT IDENTITY_HEADER@Microsoft.KeyVault(SecretUri=)@Microsoft.KeyVault(VaultName=;SecretName=)keyVaultReferenceIdentityKey Vault reference 24 hoursaz webapp list-runtimes

Created 21 Oct 2026 · Modified 23 Oct 2026 · 1 version

App Service configuration and networking

Running a Node.js app on Linux

  • Listen on process.env.PORT. App Service sets it and forwards requests there. (Custom containers: 80 or 8080, or WEBSITES_PORT.)
  • Startup. With a start script, the container runs npm start (Microsoft calls it development use). For production, set a startup command that runs PM2 in the foreground: az webapp config set -g <rg> -n <app> --startup-file "pm2 start dist/server.js --no-daemon". After Node 14 the container no longer starts PM2 on its own.
  • Runtime versions change with Node's release cycle; read them live: az webapp list-runtimes --os-type linux.
  • Build automation (Oryx) runs for Git deployments, and for zip deployments only with SCM_DO_BUILD_DURING_DEPLOYMENT=true: npm install, then npm run build and npm run build:azure if defined, with PRE_BUILD_COMMAND / POST_BUILD_COMMAND hooks. Without it, the zip must already contain node_modules and compiled output. App Service runs Node in production mode: test NODE_ENV=production locally.

Deploying, and authenticating the deployment

  • az webapp deploy -g <rg> -n <app> --src-path app.zip --type zip uses the Kudu publish API with your Entra sign-in. It's the scripted default.
  • Basic auth (publish profiles, FTP, Web Deploy user and password) is controlled by basicPublishingCredentialsPolicies/scm and /ftp. Turn both off; deploy with Entra ID or GitHub Actions OIDC (patterns/github-actions-to-azure.md). FTP basic auth needs SCM basic auth on.
  • Slots (the number allowed depends on the tier: App Service limits) let you deploy to staging, warm it, then swap. A setting marked deployment slot setting stays with the slot; everything else swaps with the code.

Outbound and inbound addresses

NeedRead it with
Addresses the app uses nowaz webapp show -g <rg> -n <app> --query outboundIpAddresses -o tsv
Every address it could use, across tiers in the same deploymentaz webapp show -g <rg> -n <app> --query possibleOutboundIpAddresses -o tsv
  • The current set changes when you scale between tier families (Basic/Standard/Premium, PremiumV2, PremiumV3); the possible set can grow when Microsoft adds tiers. Allow-list the possible set.
  • Premium V4 provides no stable set of outbound IPs, by design: the portal shows "Dynamic" and the CLI and ARM return empty strings. Don't allow-list it by IP; use VNet integration with private endpoints or service endpoints (or, the hub's reading, route egress through a NAT gateway you control).
  • Same-region storage accounts never see these public IPs (reference/storage/network-and-firewall.md).
  • The app has one inbound IP. It can change if you delete and re-create the app in another resource group, delete the last app in a resource group and region and re-create it, or delete an IP-based TLS binding. Restrict inbound with access restrictions (a priority-ordered list of IP ranges and VNet subnets through service endpoints), or a private endpoint for the app.

Regional VNet integration

  • An unused subnet, /28 or larger (/26 or larger when several plans join it, called MPSJ), delegated to Microsoft.Web/serverFarms, in the same region. Its size can't change later: leave room for scale-out and platform upgrades.
  • Routing: outboundVnetRouting.allTraffic: true (recommended) sends all outbound traffic, including image pulls and content share access, through the VNet; applicationTraffic covers app traffic only. The older vnetRouteAllEnabled site setting and WEBSITE_VNET_ROUTE_ALL app setting still work. Both properties exist in Microsoft.Web 2025-03-01.
  • With route-all on and no service endpoint, private endpoint or route, calls to storage accounts in other regions can fail.

Managed identity and Key Vault references

  • The app reaches its identity through IDENTITY_ENDPOINT and IDENTITY_HEADER; the SDKs read them (guides/managed-identity/use-managed-identity.md).
  • Key Vault reference as an app setting value: @Microsoft.KeyVault(SecretUri=https://<vault>.vault.azure.net/secrets/<name>) or @Microsoft.KeyVault(VaultName=<vault>;SecretName=<name>). Without a version, the app picks up new versions within 24 hours; any config change refetches at once.
  • References use the system-assigned identity unless you set keyVaultReferenceIdentity to a user-assigned identity's resource ID (az webapp update -g <rg> -n <app> --set keyVaultReferenceIdentity=<identity-resource-id>). That identity needs Key Vault Secrets User and a network path to the vault.
  • Don't depend on the app's public outbound IPs in the vault firewall: use VNet integration with route-all and a private endpoint, or a service endpoint.

Health check

There's no default path. Set one (healthCheckPath) that checks the app itself quickly and without authentication. Instances are pinged every minute; after 10 failed requests (non-2xx) an instance leaves the load balancer, and returns when healthy. Run at least two instances for it to route around a bad one: a single unhealthy instance stays in rotation and is replaced after an hour of failed pings.

Errors: reference/app-service/errors.md. Single-tenant hosting in your VNet: reference/app-service/app-service-environment.md.

Read this document as markdown — the same bytes an agent receives.