DEV Community

Cover image for I Built My First Azure Virtual Machine From Scratch and Hosted a Website Using Apache2 🌐
David Cletus
David Cletus

Posted on

I Built My First Azure Virtual Machine From Scratch and Hosted a Website Using Apache2 🌐

Building My First Azure Virtual Machine From the Ground Up πŸš€

In this post, I'll walk you through exactly how I went from an empty Azure subscription to a live Ubuntu VM running Apache, complete with every screen I clicked through. No skipped steps, no "and then magic happens."

By the end, you'll be able to do this yourself in under 20 minutes. Let's go! πŸ‘‡

πŸ’¬ Heads up: This is a follow-along tutorial. Grab a coffee, open a second tab with your Azure portal, and build alongside me.


βœ… What You'll Build

  • [x] A Resource Group to hold everything together
  • [x] A Virtual Network (VNet) with a custom address space
  • [x] A Subnet inside that VNet
  • [x] A Virtual Machine (Ubuntu 24.04 LTS) deployed into that subnet
  • [x] SSH access into the VM from VS Code's terminal
  • [x] A live Apache2 web server you can visit in your browser
  • [x] A clean teardown so you don't get billed for forgetting it πŸ’Έ

🧰 Prerequisites

  • An Azure account (free tier works fine)
  • A terminal β€” I used VS Code's integrated terminal (bash/Git Bash)
  • 15–20 minutes and a bit of curiosity

πŸ—ΊοΈ The Architecture (in plain English)

Resource Group  (the folder that holds everything)
   └── Virtual Network   (10.0.0.0/16)
         └── Subnet      (10.0.0.0/24)
               └── Virtual Machine  (Ubuntu 24.04, public IP, SSH + HTTP open)
Enter fullscreen mode Exit fullscreen mode

Resource Group first, then network, then subnet, then the actual VM goes into that subnet. Let's build it top-down.


Step 1: Create a Resource Group πŸ“

A Resource Group is basically a folder in Azure β€” it keeps every resource for this project (network, VM, disks, IP) organized together so you can manage or delete them as one unit.

Firstly I login into the Azure Homepage via https://portal.azure.com/

From the Azure homepage, I searched for Resource Groups and hit Create.

I named mine VM-RG-Test and picked France Central as my region β€” pick whatever region is closest to you.

While on the Review + Create page, I clicked Create after reviewing the details of the Resource Group.

A few seconds later β€” done! βœ…

πŸ’‘ Tip: Whatever region you pick here is the one you should use for every resource in this tutorial. Mixing regions adds latency and (sometimes) extra cost.


Step 2: Create a Virtual Network (VNet) 🌐

Next, your VM needs a network to live in. I searched for Virtual networks

and clicked Create.

I assigned it to my VM-RG-Test resource group and named it VM-Virtual-Network, same region as before.

On the Address space tab, Azure gives you a default 10.0.0.0/16 range with a default subnet already attached. I deleted that default subnet β€” I want to create my own with a name that actually makes sense β€” then hit Review + create.

Validation passed, so I clicked Create.

Once the deployment finished, I clicked Go to resource.


Step 3: Add a Subnet 🧩

A VNet without a subnet is just an empty container β€” the subnet is where your VM's network card actually attaches.

Inside the VNet, I went to Settings β†’ Subnets.

And clicked Create on the subnet page.

I named it VM-Sub-Net, left the default 10.0.0.0/24 range (251 usable IPs β€” plenty for one VM), and clicked Add.

And there it is β€” one ready-to-use subnet.


Step 4: Create the Virtual Machine πŸ–₯️

This is the main event. I searched for Virtual machines

And clicked Create, then selected the first option, which is Virtual Machine.

4.1 β€” Basics: name, region, resource group

I dropped this VM into my existing VM-RG-Test resource group and named it VM-Test.

4.2 β€” Image, size, and authentication

For the image, I went with Ubuntu Server 24.04 LTS - x64 Gen2, kept the size small (Standard_B1ls) to keep costs minimal, and chose SSH public key as the authentication method instead of a password β€” much safer.

4.3 β€” Username, SSH key, and inbound ports

I set my username to VM-David, let Azure generate a new SSH key pair for me, named the key VM-Test_key, and allowed inbound traffic on HTTP (80) and SSH (22) β€” HTTP so I can later view my website, SSH so I can connect and manage the server.

⚠️ Allowing SSH from "any IP" is fine for a quick test like this, but for production you'd lock it down to your own IP address using the Advanced networking controls.

4.4 β€” Disks

I kept the default Standard SSD for the OS disk β€” good enough for a test server, no need for Premium SSD here.

4.5 β€” Networking: plugging it into our VNet/Subnet

This is where everything we built earlier comes together β€” I selected my VM-Virtual-Network and VM-Sub-Net, let Azure create a new public IP, and chose HTTP (80), SSH (22) as the allowed inbound ports.

I also checked "Delete public IP and NIC when VM is deleted" β€” a small checkbox that saves you from leaving orphaned (and billable!) resources behind later.

4.6 β€” Review + Create

Validation passed (estimated cost: under $8/month for this tiny config πŸ™Œ), so I hit Create.

Azure immediately prompted me to download the private key β€” this is your only chance to grab it, since Azure never stores the private half of the key pair.

And there's my .pem key, safely sitting in my Downloads folder.

πŸ” Don't lose this file. Without it, you can't SSH into your VM. Treat it like a password.

A minute or two later, the deployment finished.


Step 5: Confirm the VM Is Alive βœ…

On the VM's overview page, I could see its public IP, private IP, region, and size β€” and the status read Running.


Step 6: SSH In and Install Apache πŸ”§

Time to actually log into the server. I opened VS Code's terminal then swicthed to Bash from Powershell(you can use Git-bash also), navigated to my Downloads folder (where the key lives), locked down the key's permissions, and connected.

# 1. Go to where the key was downloaded
cd Downloads

# 2. Restrict permissions on the key (required by SSH)
chmod 400 VM-Test_key.pem
# (chmod 600 also works on most systems)

# 3. Connect to the VM using the key, username, and public IP
ssh -i VM-Test_key.pem VM-David@98.66.137.84

# 4. Update package lists
sudo apt update

# 5. Install Apache2 web server
sudo apt install apache2
Enter fullscreen mode Exit fullscreen mode

The first time you connect, SSH will ask if you trust the host's fingerprint β€” type yes to continue. Running whoami afterward confirmed I was logged in successfully.

After updating, I installed Apache2:


Step 7: See It Live in the Browser 🌍

With Apache installed and running, I opened a browser and typed in the VM's public IP address. And there it was β€” the Apache2 default "It works!" page, served straight from my own Azure VM.

This was honestly the most satisfying part β€” seeing a page load from infrastructure I built myself, end to end. πŸŽ‰


Step 8: Clean Up β€” Delete the Resource Group 🧹

Azure bills you for running resources, so once I was done testing, I deleted the whole resource group. Because everything (VM, disk, NIC, public IP, SSH key, VNet) was inside VM-RG-Test, one deletion removes it all.

πŸ‘€ Spot the extra one? There's also a NetworkWatcherRG sitting in that list. Azure creates this automatically the first time you use any network diagnostics feature in a region β€” I didn't make it on purpose. It doesn't cost anything by itself, but since I was cleaning house, I deleted it too.

Azure shows you exactly what's about to be deleted β€” 7 resources in my case β€” and makes you type the resource group's name to confirm. No accidental deletions here.

A couple minutes later, it was gone β€” clean slate, zero ongoing cost.


🎯 Recap

Step What Happened
1 Created a Resource Group (VM-RG-Test)
2 Created a Virtual Network (VM-Virtual-Network, 10.0.0.0/16)
3 Added a Subnet (VM-Sub-Net, 10.0.0.0/24)
4 Deployed an Ubuntu VM into that subnet with an SSH key
5 Connected via SSH from VS Code's terminal
6 Installed and ran Apache2
7 Visited the live page in a browser
8 Deleted everything to avoid surprise charges

πŸ—£οΈ Over to you

Drop a comment below πŸ‘‡ β€” and if this helped you, a like/follow goes a long way! πŸ™Œ

Top comments (0)