DEV Community

Cover image for Scaling Up: How I Deployed an Azure VM Scale Set with a Load Balancer and Apache2
Amapakabo, Joseph I.
Amapakabo, Joseph I.

Posted on

Scaling Up: How I Deployed an Azure VM Scale Set with a Load Balancer and Apache2

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.

Create a resource group — subscription, name azure-vmss, and region South Africa North

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.

Create virtual network — Review + create screen showing vmss-vnet configuration and validation passed


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.

Load balancers dashboard showing Create > Standard Load Balancer option

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.

Create load balancer Basics — vmss-lb, Public, Standard SKU, Regional tier

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.

Frontend IP configuration tab with Add a frontend IP configuration button

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.

Add frontend IP configuration panel — frontend-vmss1 name, IPv4, Create new public IP

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.

Backend pools tab showing Add a backend pool option

Name it backend-vmss1, select the vmss-vnet virtual network, set backend pool configuration to NIC, and save.

Add backend pool panel — NIC configuration selected, vmss-vnet attached

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.

Inbound rules tab with Add a load balancing rule button

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.

Add load balancing rule panel — vmsslb-rule1, frontend-vmss1, backend-vmss1, TCP port 80

For the Health probe, click Create new.

Load balancing rule — health probe set to No existing probes, Create new highlighted

Name it vmsslb-health, set protocol to HTTP, port 80, path /, and interval 5 seconds. Save.

Health probe creation — vmsslb-health, HTTP, port 80, path /, 5 second interval

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.

Load balancer Review + create — validation passed, showing all settings including frontend-vmss1 and backend-vmss1

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.

vmss-lb overview showing successful deployment with frontend IP 20.87.240.113 and deployment succeeded notification

📌 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.

Virtual Machine Scale Set dashboard — empty, with Create button

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.

Create VMSS Basics tab — myvmss1, South Africa North, Flexible orchestration, Autoscaling selected

4b. Configure Autoscaling

Once you select Autoscaling, a Scaling configuration section appears. Click Configure to set up the autoscale policy.

VMSS Basics showing Autoscaling selected with Configure button highlighted

You'll land on the Scaling configuration page showing the Default condition. Click the edit (pencil) icon to modify it.

Scaling configuration page — Default condition row with edit pencil icon highlighted

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%

Edit Condition panel — default 2 instances, min 2, max 5, scale out CPU threshold 80%

Scroll down to also set the Scale in rule and query duration:

  • Scale in when CPU < 20%
  • Query duration: 10 minutes

Edit Condition — Scale in CPU threshold 20%, decrease by 1, 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.

VMSS image Ubuntu Pro 24.04 LTS, size Standard B2as v2, admin credentials

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.

VMSS Networking tab — virtual network and subnet configured

VMSS Networking — Load balancing section, Azure load balancer selected, vmss-lb and backend-vmss1 attached

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.

VMSS Management tab — Boot diagnostics set to Disable

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.

VMSS Advanced tab — allocation policy and VM applications section

Scroll down to Custom data and cloud init and paste in the following script:

Custom data field with the bash script pasted in

#!/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
Enter fullscreen mode Exit fullscreen mode

💡 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.

VMSS Review + create — validation passed, deployment initializing notification


Step 5: Get the Load Balancer's Frontend IP

Once the VMSS deploys successfully, navigate to your vmss-lb load balancer → SettingsFrontend IP configuration. You'll see the public IP address that was assigned.

vmss-lb Frontend IP configuration — frontend-vmss1 showing IP 20.87.240.113, VMSS deployment succeeded notification

Copy that IP address and paste it into your browser.

Browser address bar with 20.87.240.113 entered

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.

Azure portal search showing NSG search with Network security groups in recent services

You should see the auto-generated NSG for your VMSS. Click on it.

NSG list showing basicNsgvnet-southafricanorth-1-nic01 in the azure-vmss resource group

In the NSG blade, under Settings, click Inbound security rules.

NSG overview with Inbound security rules highlighted in the Settings section

Click + Add to add a new rule.

Inbound security rules tab with the Add button highlighted

Set the following parameters and click Add:

  • Source: Any
  • Service: HTTP
  • Destination port: 80
  • Protocol: TCP
  • Action: Allow
  • Priority: 100
  • Name: Allowport80

Add inbound security rule panel — HTTP, port 80, TCP, 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.

Browser showing

Refresh again. The load balancer routes the request to a different instance — and a different hostname appears!

Browser showing

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.

azure-vmss resource group showing all created resources — NSG, public IPs, VMSS, VMs, disks, and load balancer


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)