<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel</title>
    <description>The latest articles on DEV Community by Daniel (@danielops).</description>
    <link>https://dev.to/danielops</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3888618%2F545acbeb-febf-4e57-b507-61fdadf3ceb1.png</url>
      <title>DEV Community: Daniel</title>
      <link>https://dev.to/danielops</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielops"/>
    <language>en</language>
    <item>
      <title>My First Terraform Workflow on Azure.</title>
      <dc:creator>Daniel</dc:creator>
      <pubDate>Sat, 11 Jul 2026 14:52:26 +0000</pubDate>
      <link>https://dev.to/danielops/my-first-terraform-workflow-on-azure-12aa</link>
      <guid>https://dev.to/danielops/my-first-terraform-workflow-on-azure-12aa</guid>
      <description>&lt;p&gt;My cloud tutor gave me a scenario for a fictional company, Hagital Cloud Solutions, with one clear goal: use Terraform to provision a Resource Group in Azure and run the complete Infrastructure as Code workflow not just apply and call it a day.&lt;/p&gt;

&lt;p&gt;That means:&lt;br&gt;
terraform init - terraform fmt - terraform validate - terraform plan terraform apply&lt;/p&gt;

&lt;p&gt;...and then, just as importantly, terraform destroy to tear it all down cleanly.&lt;br&gt;
It's a small task on the surface (one resource, one resource group), but the point wasn't the resource itself it was building the muscle memory for the workflow every Terraform engineer runs on every project, big or small.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why the Full Workflow Matters&lt;/strong&gt;&lt;br&gt;
Before jumping into the code, here's the mental model I'm working with for each command:&lt;br&gt;
init — downloads the provider plugins (in this case, azurerm) and sets up the backend. Nothing gets touched in Azure yet.&lt;/p&gt;

&lt;p&gt;fmt — auto-formats the .tf files to a consistent style. Cheap to run, keeps code review painless.&lt;/p&gt;

&lt;p&gt;validate — checks the syntax and internal logic of the config without talking to Azure. Catches typos and structural mistakes early.&lt;/p&gt;

&lt;p&gt;plan — the dry run. Terraform compares your config against the real state of Azure and tells you exactly what it intends to do.&lt;/p&gt;

&lt;p&gt;apply — actually executes that plan and creates the resources.&lt;/p&gt;

&lt;p&gt;destroy — the reverse of apply; tears down everything Terraform is tracking in state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Terraform Code&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  main.tf
&lt;/h1&gt;

&lt;p&gt;terraform {&lt;br&gt;
  required_providers {&lt;br&gt;
    azurerm = {&lt;br&gt;
      source  = "hashicorp/azurerm"&lt;br&gt;
      version = "~&amp;gt; 4.80.0"&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;provider "azurerm" {&lt;br&gt;
  features {}&lt;br&gt;
  subscription_id = "Can't put my azure account's subscription id here for security reasons"&lt;br&gt;
}&lt;/p&gt;

&lt;h1&gt;
  
  
  Resource Group
&lt;/h1&gt;

&lt;p&gt;resource "azurerm_resource_group" "Terraform-RG" {&lt;br&gt;
  name     = "Terraform-RG"&lt;br&gt;
  location = "westeurope"&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;I went with West Europe as the region nothing scenario-specific requiring it, just a solid, low-latency default for this exercise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running the Workflow&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. terraform init&lt;/strong&gt;&lt;br&gt;
Downloaded the azurerm provider and initialized the working directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fphbjbhh009pxim51fm6i.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fphbjbhh009pxim51fm6i.PNG" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. terraform fmt&lt;/strong&gt;&lt;br&gt;
Formatted the file no changes needed since I kept the indentation clean from the start, but good habit to run it anyway.&lt;br&gt;
&lt;strong&gt;3. terraform validate&lt;/strong&gt;&lt;br&gt;
Config passed validation no syntax errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn9i0fhpgfkhgi9p7wtbr.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn9i0fhpgfkhgi9p7wtbr.PNG" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. terraform plan&lt;/strong&gt;&lt;br&gt;
Terraform showed exactly one resource to be added: azurerm_resource_group.hagital. This is the step I always slow down on reading the plan output line by line before typing yes anywhere.&lt;br&gt;
&lt;strong&gt;5. terraform apply&lt;/strong&gt;&lt;br&gt;
Confirmed the plan, and got the message every Terraform engineer wants to see which is:&lt;br&gt;
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.&lt;br&gt;
As you can see in my screensnip below...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw8tfncq9i2s6flr00lif.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw8tfncq9i2s6flr00lif.PNG" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Verified in the azure portal&lt;/strong&gt;&lt;br&gt;
Jumped into the Azure Portal and confirmed Terraform-rg showing up under Resource Groups state file matched reality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwar1bddu83zc9qqddx0t.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwar1bddu83zc9qqddx0t.PNG" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. terraform destroy&lt;/strong&gt;&lt;br&gt;
Since this was a learning exercise not something meant to stay running and accumulate costs i tore it down:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiuwc6bo80dp2976o9v3s.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiuwc6bo80dp2976o9v3s.PNG" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Still verified in the azure portal again:&lt;/strong&gt;&lt;br&gt;
The screenshot below confirms that the item is no longer available in the portal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmtpbeejyleiggoqq52ab.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmtpbeejyleiggoqq52ab.PNG" alt=" " width="799" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reflection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one ran clean end-to-end no errors, no surprises. That's partly luck and partly because the exercise is deliberately scoped small: one resource, one provider, no dependencies to trip over.&lt;/p&gt;

&lt;p&gt;But "it worked" isn't really the takeaway I want to hold onto. What stuck with me was the discipline of the workflow fmt and validate before plan and never applying without reading the plan output first. Those are the habits that matter once the configs get bigger and the blast radius of a mistake gets larger.&lt;/p&gt;

&lt;p&gt;Next up: I want to extend this into a small VNet + subnet setup on top of the same Resource Group, so I can start tying the networking concepts I've been studying (NSGs, subnet segmentation) into the same IaC workflow.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>azure</category>
      <category>devops</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>How I Hosted a Static Website on Azure Blob Storage with Custom DNS, Zero Servers, Under $2/Month</title>
      <dc:creator>Daniel</dc:creator>
      <pubDate>Sat, 04 Jul 2026 17:24:09 +0000</pubDate>
      <link>https://dev.to/danielops/how-i-hosted-a-static-website-on-azure-blob-storage-with-custom-dns-zero-servers-under-2month-74g</link>
      <guid>https://dev.to/danielops/how-i-hosted-a-static-website-on-azure-blob-storage-with-custom-dns-zero-servers-under-2month-74g</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I challenged myself with a real-world Azure scenario something beyond just spinning up a VM and calling it a day. So I set myself to host a fully functional marketing website for a fictional Lagos real estate startup on Azure, with no servers, no maintenance overhead, and as little cost as possible.&lt;/p&gt;

&lt;p&gt;Here's exactly what I built, every decision I made.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbgcrvq0fu4ovmwxukrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbgcrvq0fu4ovmwxukrc.png" alt=" " width="799" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Architecture Design Before Deployment&lt;/p&gt;

&lt;p&gt;Before touching a single Azure resource, I mapped out the layout. This is a habit I'm building early because deploying without a clear design in mind is how you end up with resources in the wrong place and configurations you have to undo.&lt;/p&gt;

&lt;p&gt;User types &lt;a href="http://www.lagosproperties.com" rel="noopener noreferrer"&gt;www.lagosproperties.com&lt;/a&gt;&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
Azure DNS Zone (CNAME record)&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
Storage Account — $web container&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
index.html / about.html / contact.html&lt;/p&gt;

&lt;p&gt;The key design decision here was deliberately avoiding compute. No VM means no OS to patch, no web server to configure, and no idle compute costs running 24/7. For a static marketing site, Blob Storage is the right tool — cheap, reliable, and maintenance-free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task 1 — Setting Up the Storage Account + Static Website&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, I created a StorageV2 storage account in the South Africa North region. because South Africa North is geographically closest to Lagos, which means lower latency for Nigerian visitors loading the site. a small detail, but the kind of thing that matters in production.&lt;/p&gt;

&lt;p&gt;I also used Standard LRS (Locally Redundant Storage) for redundancy, Azure keeps three copies of the data within the same datacenter. For a marketing site, that's sufficient. A banking system would warrant something stronger like GRS, but over-engineering redundancy here would just drive-up cost unnecessarily.&lt;/p&gt;

&lt;p&gt;With static website hosting enabled, Azure provisions a special $web container and assigns a public endpoint automatically. I built three HTML pages; Home, About, and Contact plus a custom 404 error page and uploaded all four to the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The site went live immediately at:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://lagosproertiesstg.z1.web.core.windows.net/" rel="noopener noreferrer"&gt;https://lagosproertiesstg.z1.web.core.windows.net/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpc59qul4q2xhpu5l5u8d.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpc59qul4q2xhpu5l5u8d.PNG" alt=" " width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Homepage&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6sgxwikrcn33j1n0zxz.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6sgxwikrcn33j1n0zxz.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;About page&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9thjf19n6d1vm2z0j3a6.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9thjf19n6d1vm2z0j3a6.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contact page&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fblaa67wmfm4in8njrimh.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fblaa67wmfm4in8njrimh.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;$web container showing all 4 files&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task 2 — Azure DNS Zone + CNAME Record&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A storage endpoint URL works but lagosproertiesstg.z1.web.core.windows.net isn't something you'd put on a business card. I needed a proper custom domain.&lt;/p&gt;

&lt;p&gt;So i created a public Azure DNS Zone for lagosproperties.com and configured a CNAME record pointing &lt;a href="http://www.lagosproperties.com" rel="noopener noreferrer"&gt;www.lagosproperties.com&lt;/a&gt; to the storage endpoint&lt;/p&gt;

&lt;p&gt;One thing worth noting and something I learned during this project is that a CNAME record cannot sit at the root/apex of a domain (lagosproperties.com with no www). DNS standards don't allow it because the root domain must have SOA and NS records, which can't coexist with a CNAME. The www subdomain is where the CNAME lives. For the bare root domain to also work in production, an Alias record would be needed instead.&lt;/p&gt;

&lt;p&gt;Azure automatically assigned four nameservers to the zone:&lt;/p&gt;

&lt;p&gt;ns1-09.azure-dns.com&lt;br&gt;
ns2-09.azure-dns.net&lt;br&gt;
ns3-09.azure-dns.org&lt;br&gt;
ns4-09.azure-dns.info&lt;/p&gt;

&lt;p&gt;In a real deployment, I'd update these at the domain registrar to complete the delegation. But for this project, I verified the CNAME resolves correctly by querying Azure's nameservers directly using this command:&lt;/p&gt;

&lt;p&gt;nslookup &lt;a href="http://www.lagosproperties.com" rel="noopener noreferrer"&gt;www.lagosproperties.com&lt;/a&gt; ns1-09.azure-dns.com&lt;/p&gt;

&lt;p&gt;I chose to verify it this way at my own discretion, because anyone trying to load &lt;a href="http://www.lagosproperties.com" rel="noopener noreferrer"&gt;www.lagosproperties.com&lt;/a&gt; in a browser right now wouldn't see it yet without the registrar delegation. Querying the nameserver directly proves the Azure side of the configuration works perfectly!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh9ejzgx24dqbd27t4aiy.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh9ejzgx24dqbd27t4aiy.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fun3xkrrtxxzg5chrlz5t.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fun3xkrrtxxzg5chrlz5t.PNG" alt=" " width="799" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task 3 — Blob Versioning + Rollback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the part I found most valuable from a real-world perspective. Enabling blob versioning on the storage account means every time a file is overwritten, Azure silently keeps the previous version in the background automatically, with no extra configuration.&lt;/p&gt;

&lt;p&gt;Why does this matter? Because mistakes happen. A content update with a typo, a broken layout, an accidental overwrite these are inevitable. "Being careful" doesn't scale as a strategy. Versioning is what gives you a fast recovery path when something goes wrong, regardless of why it went wrong.&lt;/p&gt;

&lt;p&gt;To demonstrate this, I:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uploaded the original homepage — Version 1&lt;/li&gt;
&lt;li&gt;Edited the heading and re-uploaded the same file — Version 2 (now live)&lt;/li&gt;
&lt;li&gt;Confirmed both versions existed in Azure with different timestamps.&lt;/li&gt;
&lt;li&gt;Rolled back by restoring Version 1 as the current live blob.&lt;/li&gt;
&lt;li&gt;Refreshed the live site original content restored instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q6mtf4eyuejnwcc8vf5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q6mtf4eyuejnwcc8vf5.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Data Protection page showing versioning enabled&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsgvpt88pa6grzdgav9al.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsgvpt88pa6grzdgav9al.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
live site — Version 1 (original heading)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgn8lbn3e5q2pgsgiunz4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgn8lbn3e5q2pgsgiunz4.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
live site — Version 2 (updated heading)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvtlxauu2l40tfpsvefaz.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvtlxauu2l40tfpsvefaz.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Versions of the two timestamps in the azure portal&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz0428qbd3jzf4fr8wleu.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz0428qbd3jzf4fr8wleu.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
 live site after rollback — Version 1 restored&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task 4 — What Does This Actually Cost?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One thing I always want to understand when building on Azure is what I'm actually paying for and why. So I ran the numbers through the Azure Pricing Calculator based on realistic assumptions for a small real estate site:&lt;br&gt;
Assumption                        Value&lt;br&gt;
─────────────────────────────────────────────────&lt;br&gt;
Expected visitors/month           ~5,000&lt;br&gt;
Average pages viewed per visit    3&lt;br&gt;
Total read operations/month       ~10,000&lt;br&gt;
Write operations/month            ~1,000&lt;br&gt;
Data retrieval/month              ~100 MB&lt;br&gt;
DNS zone                          1&lt;br&gt;
DNS queries/month                 ~10,000&lt;/p&gt;

&lt;p&gt;Estimated monthly total: $1.14&lt;/p&gt;

&lt;p&gt;That's the entire hosting cost for a production-ready marketing website DNS included. No compute, no idle server running overnight, no maintenance window. This is the core value proposition of serverless static hosting, and running these numbers myself made it genuinely concrete rather than just a talking point.&lt;/p&gt;

&lt;p&gt;Below are images showing the Storage + DNS line items breakdown &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7x0dhln84nacsut6zjeg.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7x0dhln84nacsut6zjeg.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft120gbw0jkxepuri2b4c.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft120gbw0jkxepuri2b4c.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What started as a simple "host a static site" challenge turned into a genuinely useful exercise in thinking through architecture, cost, DNS, and data resilience together not as separate topics, but as one connected system. The result is a serverless, sub-$2/month website that a real Lagos startup could deploy from day one with confidence.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>cloud</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How i Created a Linux VM, SSHed Into it and Installed Apache2 on Azure</title>
      <dc:creator>Daniel</dc:creator>
      <pubDate>Mon, 27 Apr 2026 11:10:32 +0000</pubDate>
      <link>https://dev.to/danielops/how-i-created-a-linux-vm-sshed-into-it-and-installed-apache2-on-azure-5dge</link>
      <guid>https://dev.to/danielops/how-i-created-a-linux-vm-sshed-into-it-and-installed-apache2-on-azure-5dge</guid>
      <description>&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;In this project, I created a Linux virtual machine on Microsoft Azure, connected to it securely using SSH, and installed Apache2 to host a web server.&lt;/p&gt;

&lt;p&gt;This is part of my journey as an aspiring cloud engineer, learning cloud technologies, Linux, and DevOps.&lt;/p&gt;

&lt;p&gt;Step 1: Creating a Virtual Machine on Azure&lt;/p&gt;

&lt;p&gt;I logged into the Azure Portal and created a new virtual machine using Ubuntu as the operating system.&lt;/p&gt;

&lt;p&gt;Key configurations included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resource group creation&lt;/li&gt;
&lt;li&gt;Selecting a region&lt;/li&gt;
&lt;li&gt;Choosing a VM size&lt;/li&gt;
&lt;li&gt;Setting up authentication (SSH key)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwgm06sd2jcv80nm82pk.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwgm06sd2jcv80nm82pk.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2: Connecting to the VM via SSH&lt;/p&gt;

&lt;p&gt;After the VM was successfully created, I connected to it using SSh [Secure shell hosting] from my Gitbash terminal.&lt;/p&gt;

&lt;p&gt;Command used:&lt;br&gt;
ssh username@public-ip-address&lt;/p&gt;

&lt;p&gt;Once connected, I had full access to the Linux environment running on virtual machine as shown below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0flxy0kzku8i5cdakkv.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0flxy0kzku8i5cdakkv.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: Updating the System&lt;/p&gt;

&lt;p&gt;Before installing any software, I updated and upgraded the package list with the commands; &lt;/p&gt;

&lt;p&gt;sudo apt update&lt;br&gt;
&amp;amp;&lt;br&gt;
sudo apt ugrade&lt;/p&gt;

&lt;p&gt;This ensures that all packages are up to date and upgraded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwva8xx6fzuih4n11xt.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwva8xx6fzuih4n11xt.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 4: Installing Apache2&lt;/p&gt;

&lt;p&gt;Next, I installed Apache2 using the following command:&lt;/p&gt;

&lt;p&gt;sudo apt install apache2 -y&lt;br&gt;
The -y flag automatically confirms installation prompts.&lt;/p&gt;

&lt;p&gt;After installation, Apache2 started running on the server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4ge7131quhi5i1rejwp.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4ge7131quhi5i1rejwp.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 5: Verifying Apache2 Installation&lt;/p&gt;

&lt;p&gt;To confirm Apache2 was working, I opened a browser and visited my VM’s public IP address.&lt;/p&gt;

&lt;p&gt;The default Apache2 welcome page was displayed, confirming a successful installation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ejala97vkw2gvw9fgnw.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ejala97vkw2gvw9fgnw.PNG" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;In this project, I successfully:&lt;/p&gt;

&lt;p&gt;Created a Linux virtual machine on Azure&lt;br&gt;
Connected to it using SSH&lt;br&gt;
Installed and ran Apache2&lt;/p&gt;

&lt;p&gt;This project strengthened my understanding of cloud computing, remote server management, and basic web server deployment.&lt;/p&gt;

&lt;p&gt;I look forward to building more projects as I continue my journey in cloud engineering.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>linux</category>
      <category>azure</category>
    </item>
  </channel>
</rss>
