Skip to content
CAMPUX Cloud Bootcamp Lab · Compute ← All labs
Hands-On Lab · Beginner
~20 min · a few cents · Cloud Shell
CLI · torn down at the end
App Service

The swap: content changes, nothing restarts.

One plan bills once, however many apps sit on it. One slot swap changes what production serves without a deploy, a restart, or a second of downtime. Here you prove both, on the cheapest shape that still teaches them: no VNet, no Key Vault, no pipeline — that enterprise-shaped build already exists as a capstone.

Fig. 1 · One plan, two apps, one swap
One App Service plan hosts app1 (with a staging slot swapping into production) and app2, billed as a single plan. app service plan · billed once app1 staging v2 production v1 swap: routing switches, not the code app2 no slot needed shares app1's compute
● Screen walkthrough Not yet recorded · ~5 min
Reel · 00:00 / 05:00

Victor puts two apps on one plan, deploys a changed page to a staging slot, and swaps it into production live.

Placeholder — the page below stands alone until the reel lands
Why

The plan is the machine. The app is the tenant.

An App Service plan is the compute — a set of VM instances at a given size and tier. An app is a tenant on that compute: its own code, its own settings, its own URL, but no dedicated machine of its own. Put two apps on one plan and you pay for the plan once, not twice — the plan does not know or care how many apps sit on it, only how much CPU and memory they ask of it together. A deployment slot is a different idea entirely: a full, live copy of one app, with its own URL, that can trade places with production in a single operation. This lab proves both facts by doing them, not by reading about them.

The plan is billed. The app is free.

Before you begin — one-time setup

You need a free Azure account and the Azure CLI (az), signed in with az login. First time? The 15-minute Set up your machine page covers the account, the installs, and sign-in. Prefer zero installs? Run everything in Azure Cloud Shell (Bash), preinstalled and already signed in.

Cost & honesty

Deployment slots need the Standard tier or higher — the Free and Basic tiers do not offer them at all. This lab uses an S1 plan, which is not free tier: it bills by the hour, roughly ten cents. Run start-to-finish and tear down the same session and the total is a few cents. Leave it running and it becomes a few dollars a day. The teardown section removes everything. Pairs with Class 11a — App Service Plans and Class 11b — App Service.

Step 1

A plan, a first app, a page

Create the plan, then the first app on it, then push a page small enough to write inline. The page is a bare Node server — no dependencies, so the deploy is instant.

# Windows/Git Bash: stop it mangling resource-id arguments (harmless on macOS/Linux)
export MSYS_NO_PATHCONV=1

SUFFIX=$RANDOM
RG="campux-lab-slot-rg"
LOCATION="eastus"
PLAN="campux-slot-plan"
APP1="campux-app1-$SUFFIX"

az group create --name "$RG" --location "$LOCATION" -o table

# S1 (Standard) — the smallest tier that supports deployment slots
az appservice plan create --name "$PLAN" --resource-group "$RG" \
  --sku S1 --is-linux -o table

az webapp create --name "$APP1" --resource-group "$RG" --plan "$PLAN" \
  --runtime "NODE:20-lts" -o table

mkdir -p slotlab && cd slotlab
cat > server.js <<'EOF'
const http = require('http');
http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('campux-slot-lab v1\n');
}).listen(process.env.PORT || 8080);
EOF
cat > package.json <<'EOF'
{ "name": "campux-slot-lab", "version": "1.0.0", "scripts": { "start": "node server.js" } }
EOF
zip -r ../app-v1.zip . > /dev/null && cd ..

az webapp deploy --name "$APP1" --resource-group "$RG" \
  --src-path app-v1.zip --type zip -o none
Checkpoint curl https://$APP1.azurewebsites.net (give it 30–60 seconds to warm up on the first request) returns campux-slot-lab v1.
Step 2

A second app, the same plan, one bill

Add a second app to the identical plan and deploy the same page to it. Nothing about creating it references billing — that is the point: the plan is what is metered, and it is already paid for.

APP2="campux-app2-$SUFFIX"

az webapp create --name "$APP2" --resource-group "$RG" --plan "$PLAN" \
  --runtime "NODE:20-lts" -o table

az webapp deploy --name "$APP2" --resource-group "$RG" \
  --src-path app-v1.zip --type zip -o none

# both apps, same plan ID
az webapp list --resource-group "$RG" \
  --query "[].{name:name, plan:appServicePlanId}" -o table
Checkpoint The plan column is identical for both rows. curl https://$APP2.azurewebsites.net also returns campux-slot-lab v1. Two live apps, one plan, one bill.
Step 3

A staging slot, a changed page

Add a staging slot to app1 — a full second copy of it, with its own URL — and deploy a changed page to the slot only. Production does not see this change yet.

az webapp deployment slot create --name "$APP1" --resource-group "$RG" \
  --slot staging -o table

cd slotlab
sed -i 's/campux-slot-lab v1/campux-slot-lab v2/' server.js
zip -r ../app-v2.zip . > /dev/null && cd ..

az webapp deploy --name "$APP1" --resource-group "$RG" \
  --slot staging --src-path app-v2.zip --type zip -o none
Checkpoint curl https://$APP1-staging.azurewebsites.net returns campux-slot-lab v2. curl https://$APP1.azurewebsites.net (no -staging) still returns v1 — production is untouched.
Step 4

Swap: prove production changed with no restart

A swap warms up every instance in the staging slot first, then switches the routing that decides which slot answers to the production hostname. No content is copied at request time and no deploy command touches production — the slot that was already running takes over.

az webapp deployment slot swap --name "$APP1" --resource-group "$RG" \
  --slot staging --target-slot production -o table

curl https://$APP1.azurewebsites.net
Checkpoint The same production URL, with no new command run against it besides the swap, now returns campux-slot-lab v2.
What just happened

Notice what was never run: no az webapp deploy against production, no az webapp restart, no new zip. The swap changed which already-warmed-up instances answer at the production hostname — it did not push new code to a running app and reload it. That is why Microsoft's own docs call it zero-downtime: real users mid-request are never pointed at a cold or half-restarted process. (Under the hood, App Service does restart the outgoing slot's instances as the last step of the swap, applying production's settings to it — but that happens to the slot that is now staging, after the cutover, not to the app your users are hitting.)

Step 5

Swap back, then tear down

A swap is symmetric: running the identical command again trades the two slots back, which is exactly how you roll back a bad release without a redeploy.

az webapp deployment slot swap --name "$APP1" --resource-group "$RG" \
  --slot staging --target-slot production -o table

curl https://$APP1.azurewebsites.net   # -> v1 again

# tear down — the S1 plan bills by the hour, so do not skip this
az group delete --name "$RG" --yes
az group exists --name "$RG"   # -> false
Checkpoint az group exists returns false. The plan, both apps, and the staging slot are gone.
End

What you can now honestly claim

You put two apps on one App Service plan and confirmed they share one bill. You added a staging slot to an app, deployed a changed page to it, and swapped it into production, then watched the production URL change content without a deploy or a restart command ever touching it. You know a plan is compute and an app is a tenant on that compute, and that a slot swap is a routing change against pre-warmed instances, not a live redeploy. This is the exact mechanism behind zero-downtime releases and instant rollback in production. What this lab deliberately skips — a private VNet in front of the plan, Key Vault-backed secrets, a CI/CD pipeline that triggers the swap automatically — is the enterprise-shaped version of the same idea, already built as the Campux Retail capstone deploy.