Web App Needs a Staging Environment? Create a Deployment Slot

Published on:

CloudTrips needs to test a release at a live Azure URL before it reaches users. An App Service deployment slot is a separate running version of the web app with its own hostname. A swap warms the staged version and moves it into the production slot without redeploying it there.

Slots share the App Service plan’s compute resources; they are not separate security or infrastructure boundaries. Standard, Premium, or Isolated tiers are required. The Premium v3 P0V3 tier shown in this lab supports deployment slots.

Create the App Service

This trip is standalone. Search for App Services, select Create > Web App, and enter:

Subscription: CloudTrips TEST
Resource group: rg-cloudtrips-slots-test-weu
Name: app-cloudtrips-slots-dmytro-test-weu
Publish: Code
Runtime stack: Node 24 LTS
Operating System: Linux
Region: West Europe
Linux Plan: Create new
Plan name: asp-cloudtrips-slots-test-weu
Pricing plan: Premium v3 P0V3
Zone redundancy: Disabled

The web-app name must be globally unique. If Azure adds a suffix to its default domain, use the generated hostname returned by Azure rather than constructing one from the app name. P0V3 is billable until the resource group is deleted, so complete the lab and clean up promptly.

Select Review + create > Create.

Create Web App page showing the standalone Node 24 Linux app on Premium v3 P0V3

Deploy the Production Version

Create this local structure:

cloudtrips-slots/
└── production/
    ├── package.json
    └── server.js

Use this package.json:

{
  "name": "cloudtrips-slots-production",
  "version": "1.0.0",
  "scripts": {
    "start": "node server.js"
  }
}

Use this server.js:

const http = require('http');
const port = process.env.PORT || 8080;

http.createServer((request, response) => {
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  response.end('<h1>CloudTrips Production</h1><p>Version 1</p>');
}).listen(port);

From cloudtrips-slots, package and deploy it to the default production slot:

(cd production && zip ../production.zip package.json server.js)

az webapp deploy \
  --resource-group rg-cloudtrips-slots-test-weu \
  --name app-cloudtrips-slots-dmytro-test-weu \
  --src-path production.zip \
  --type zip

Create the Staging Slot

Open the web app and select Deployment > Deployment slots > Add:

Name: staging
Clone settings from: app-cloudtrips-slots-dmytro-test-weu

Select Add, wait for creation to finish, and open the slot. Cloning copies configuration such as the runtime stack and app settings; it does not create a second App Service plan. The app name in this dropdown represents the production slot. Do not clone settings would create staging without copying that production configuration.

Deployment slots page showing the production and staging slots

Deploy and Test the Staging Version

Create staging/package.json with the same structure as production but use cloudtrips-slots-staging as its name. Create staging/server.js:

const http = require('http');
const port = process.env.PORT || 8080;

http.createServer((request, response) => {
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  response.end('<h1>CloudTrips Staging</h1><p>Version 2 ready</p>');
}).listen(port);

Package and deploy only to staging:

(cd staging && zip ../staging.zip package.json server.js)

az webapp deploy \
  --resource-group rg-cloudtrips-slots-test-weu \
  --name app-cloudtrips-slots-dmytro-test-weu \
  --slot staging \
  --src-path staging.zip \
  --type zip

Retrieve both generated hostnames and test them:

PROD_HOST=$(az webapp show \
  --resource-group rg-cloudtrips-slots-test-weu \
  --name app-cloudtrips-slots-dmytro-test-weu \
  --query defaultHostName --output tsv)

STAGING_HOST=$(az webapp show \
  --resource-group rg-cloudtrips-slots-test-weu \
  --name app-cloudtrips-slots-dmytro-test-weu \
  --slot staging \
  --query defaultHostName --output tsv)

curl --fail "https://${PROD_HOST}"
curl --fail "https://${STAGING_HOST}"

Production should show Version 1; staging should show Version 2.

Browser showing Version 2 running at the staging slot hostname

Swap Staging into Production

Return to Deployment slots and select Swap:

Source: staging
Target: production

Review the changes, then select Swap. App Service applies the production configuration to staging, warms the source slot, switches routing, and moves the former production version into staging. Long-running processes can be interrupted when workers recycle, so applications must handle restarts safely.

Swap pane showing staging as the source and production as the target

Run the same tests again:

curl --fail "https://${PROD_HOST}"
curl --fail "https://${STAGING_HOST}"

Production should now show Version 2, while staging contains the previous Version 1. The hostnames did not move—the application content did. If Version 2 fails validation, swapping the same slots again restores Version 1.

Browser showing Version 2 at the production hostname after the slot swap

Settings marked Deployment slot setting remain attached to their slot during swaps. Use that option for environment-specific values such as staging API endpoints or connection strings; secrets should be stored in Key Vault or another appropriate secret store.

Clean Up

Delete the isolated resource group promptly to stop P0V3 charges:

az group delete --name rg-cloudtrips-slots-test-weu --yes

Confirm that az group exists --name rg-cloudtrips-slots-test-weu returns false.