A hands-on walkthrough of setting up an Azure Virtual Machine Scale Set (VMSS) from scratch — complete with a Load Balancer, autoscaling, custom Apache2 web pages, and NSG configuration. Each step is screenshotted.
TL;DR: In this post, I walk through how I set up a complete Azure VM Scale Set (VMSS) environment — resource group, virtual network, load balancer with a health probe and inbound rule, autoscaling configuration, Apache2 deployed via cloud-init custom data, and NSG rules to let traffic through. The end result? A live, load-balanced web page rotating across multiple VM instances. Let's go. ☁️
Why VMSS?
A single VM is fine for learning, but it's not how real production workloads run. The moment your app starts getting actual traffic, you need something that can scale out automatically and stay available even when an instance goes down.
That's exactly what Azure Virtual Machine Scale Sets (VMSS) solve. VMSS lets you run a group of identical VMs that scale in and out based on demand — and when paired with an Azure Load Balancer, traffic gets distributed evenly across all running instances.
This is the setup I put together, step by step.
Prerequisites
- An active Microsoft Azure account
- Basic familiarity with the Azure portal
- A browser and some patience 😄
Step 1: Create a Resource Group
Everything starts here. The resource group is the logical container that holds every resource we're about to create — VNet, Load Balancer, VMSS, NSG, all of it.
Search for Resource Groups in the portal, click Create, and fill in your subscription, a name (azure-vmss), and region. I used South Africa North throughout.
Click Review + create, then Create.
Step 2: Create the Virtual Network (VNet)
Next up is the network that our scale set instances will live inside.
Search for Virtual Network, click Create, attach it to the azure-vmss resource group, name it vmss-vnet, and set the same region. Go through the tabs — leave Security defaults as-is, configure your address space, skip Tags — and click Create.
Step 3: Create the Load Balancer
This is the piece that sits in front of our VMs and distributes incoming traffic. Without it, VMSS doesn't have a single public entry point to route requests through.
Search for Load Balancers and select Create > Standard Load Balancer from the dropdown.
3a. Basics
Name it vmss-lb, set the type to Public, SKU to Standard, and tier to Regional. Make sure it's in the same resource group and region.
Click Next: Frontend IP configuration.
3b. Add a Frontend IP Configuration
The frontend IP is the public-facing address your load balancer listens on. Click + Add a frontend IP configuration.
Name it frontend-vmss1, leave IP version as IPv4, and click Create new under Public IP address to generate a new one. Save, then save again.
3c. Add a Backend Pool
The backend pool is the group of VMs the load balancer will distribute traffic to — our VMSS instances will register here automatically.
Click Next: Backend pools, then + Add a backend pool.
Name it backend-vmss1, select the vmss-vnet virtual network, set backend pool configuration to NIC, and save.
3d. Add an Inbound Load Balancing Rule
The load balancing rule tells the balancer how to distribute incoming traffic to the backend pool. Click Next: Inbound rules, then + Add a load balancing rule.
Set the rule name to vmsslb-rule1, select frontend-vmss1 as the frontend IP, backend-vmss1 as the backend pool, protocol TCP, and port 80.
For the Health probe, click Create new.
Name it vmsslb-health, set protocol to HTTP, port 80, path /, and interval 5 seconds. Save.
Save the rule, then save again to confirm all settings.
3e. Review + Create
With everything configured, navigate to Review + create. Confirm the validation passes and click Create.
Once deployment completes, click Go to resource to view your new load balancer. You'll also see a success notification in the top-right bell icon.
📌 Take note of your frontend IP address. You'll need it later to test the setup in your browser. Mine was
20.87.240.113.
Step 4: Create the VM Scale Set (VMSS)
With the networking and load balancer in place, it's time to create the scale set itself.
Search for Virtual Machine Scale Set in the portal and click Create.
4a. Basics
Fill in the details: subscription, resource group (azure-vmss), scale set name (myvmss1), region (South Africa North), and availability zones 1, 2, 3.
Set Orchestration mode to Flexible and Scaling mode to Autoscaling.
4b. Configure Autoscaling
Once you select Autoscaling, a Scaling configuration section appears. Click Configure to set up the autoscale policy.
You'll land on the Scaling configuration page showing the Default condition. Click the edit (pencil) icon to modify it.
In the Edit Condition panel, set the parameters like this:
- Scale mode: Autoscaling
- Default instance count: 2
- Minimum: 2
- Maximum: 5
- Scale out when CPU > 80%
Scroll down to also set the Scale in rule and query duration:
- Scale in when CPU < 20%
- Query duration: 10 minutes
Save twice to apply and return.
4c. Select Disk Image, Size, and Admin Credentials
Scroll up to the image and size section. I used Ubuntu Pro 24.04 LTS - x64 Gen2 with size Standard B2as v2. Set your admin username (vmssengine) and password.
4d. Networking Tab
Click on Networking (or hit Next until you reach it). Confirm the virtual network and subnet are selected. Scroll down to the Load balancing section and select Azure load balancer, then pick vmss-lb as your load balancer and backend-vmss1 as the backend pool.
4e. Management Tab
In the Management tab, set Boot diagnostics to Disable — this prevents an unnecessary storage account from being auto-created. Leave everything else as default.
4f. Advanced Tab — The Cloud-Init Script
This is where the magic happens. The Advanced tab has a Custom data field that accepts a cloud-init script. Whatever you paste here runs automatically on every VM instance when it boots — no manual SSH required.
Scroll down to Custom data and cloud init and paste in the following script:
#!/bin/bash
# Update package index
sudo apt-get update -y
# Install Apache2
sudo apt-get install apache2 -y
# Enable Apache2 to start on boot
sudo systemctl enable apache2
# Start Apache2 service
sudo systemctl start apache2
# Get hostname
HOSTNAME=$(hostname)
# Create custom webpage
cat <<EOF | sudo tee /var/www/html/index.html
<!DOCTYPE html>
<html>
<head>
<title>Azure VMSS Demo</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
text-align: center;
padding-top: 100px;
}
h1 { color: #0078D4; font-size: 50px; }
p { font-size: 24px; }
</style>
</head>
<body>
<h1>Hello from $HOSTNAME</h1>
<p>Azure VM Scale Set Instance</p>
</body>
</html>
EOF
# Restart Apache2 to ensure changes are loaded
sudo systemctl restart apache2
💡 This script installs Apache2, grabs the VM's hostname, and writes it into a custom HTML page. This is how we'll later confirm which instance is serving each request.
4g. Review + Create
Click through Tags and head to Review + create. Confirm the summary looks right — Ubuntu Pro 24.04 LTS, Standard B2as v2, Autoscaling, South Africa North — and hit Create.
Step 5: Get the Load Balancer's Frontend IP
Once the VMSS deploys successfully, navigate to your vmss-lb load balancer → Settings → Frontend IP configuration. You'll see the public IP address that was assigned.
Copy that IP address and paste it into your browser.
If the page doesn't load — don't panic. It just means the Network Security Group (NSG) hasn't been configured to allow HTTP traffic yet. We'll fix that next.
Step 6: Fix the NSG — Allow HTTP Traffic
The VMSS creates a default NSG attached to the network interfaces of each instance, but HTTP (port 80) isn't open by default. We need to add an inbound rule.
Search for NSG (or Network security groups) in the portal search bar.
You should see the auto-generated NSG for your VMSS. Click on it.
In the NSG blade, under Settings, click Inbound security rules.
Click + Add to add a new rule.
Set the following parameters and click Add:
- Source: Any
- Service: HTTP
- Destination port: 80
- Protocol: TCP
- Action: Allow
- Priority: 100
- Name: Allowport80
Step 7: Verify in the Browser — Load Balancing in Action! 🎉
Go back to your browser and refresh. This time, the custom Apache page should load — showing the hostname of whichever VM instance handled your request.
Refresh again. The load balancer routes the request to a different instance — and a different hostname appears!
That right there is load balancing working as intended. Two different VM instances, same IP, same Apache page — but different hostnames proving requests are being distributed across the scale set.
Snapshot: All Resources Created
Here's a final look at the azure-vmss resource group showing everything that was provisioned end-to-end.
What We Built — At a Glance
| Resource | Name | Purpose |
|---|---|---|
| Resource Group | azure-vmss | Container for all resources |
| Virtual Network | vmss-vnet | Private network for VM instances |
| Load Balancer | vmss-lb | Distributes public traffic to backend VMs |
| Frontend IP | frontend-vmss1 | Public entry point (20.87.240.113) |
| Backend Pool | backend-vmss1 | Pool of VMSS instances receiving traffic |
| Health Probe | vmsslb-health | Monitors instance health via HTTP port 80 |
| LB Rule | vmsslb-rule1 | Routes port 80 traffic to backend pool |
| VM Scale Set | myvmss1 | Auto-scaling group of Ubuntu VMs |
| NSG | basicNsgvnet-... | Controls inbound/outbound traffic to instances |
Key Takeaways
- Build the load balancer before the VMSS — the VMSS networking tab lets you attach directly to an existing load balancer during creation. Much cleaner than doing it after.
- Cloud-init custom data is powerful. No SSH needed. The script runs at boot time on every instance — perfect for installing software across an entire scale set.
- NSGs are the silent gatekeepers. Even with everything configured correctly, traffic won't flow until the NSG explicitly allows it. Always check here first if the browser isn't loading.
-
The hostname trick is a great sanity check. Embedding
$(hostname)in the web page makes it immediately obvious when the load balancer is routing to different instances. - Autoscaling keeps costs in check. With min 2, max 5 instances and CPU thresholds at 80% (scale out) and 20% (scale in), the setup only spins up extra capacity when it actually needs it.
What's Next?
From here, you could:
- Add HTTPS using an Azure Application Gateway or Let's Encrypt certificates
- Set up Azure Monitor alerts and autoscale diagnostics
- Deploy a real application instead of the static Apache page
- Explore Azure Spot Instances within the scale set to cut compute costs significantly
- Configure custom scaling schedules for predictable traffic patterns
This was one of the most satisfying setups I've done so far in my cloud engineering journey — watching the browser rotate between two different hostnames on refresh just hits different. 😄
If you followed along and ran into any issues, drop a comment. Happy to help.
Keep building. ☁️
#azure #cloudengineering #devops #vmss #loadbalancer #linux #apache #autoscaling #100daysofcloud #beginners





































Top comments (0)