Building a blog with Hugo and Azure
I recently set up this blog using Hugo and Azure Static Web Apps, and I wanted to share the process. This approach provides a fast, secure, and cost-effective (free!) way to run a technical blog. Here’s how I did it from scratch.
Why This Stack?
- Hugo: Really fast static site generator with great Markdown support and documentation on how to integrate with Azure Statis Web Apps
- PaperMod theme: Clean design with dark mode and code highlighting etc
- Azure Static Web Apps: Free tier available, easy deployment, and global CDN
Prerequisites
- Git
- Azure account
Step 1: Install Hugo
First, let’s get Hugo installed. I created a project folder and downloaded Hugo:
# Create project directory
mkdir my-blog
cd my-blog
# Download and extract Hugo (Windows example)
# For other platforms, see the Hugo documentation
curl -L https://github.com/gohugoio/hugo/releases/download/v0.125.7/hugo_0.125.7_windows-amd64.zip -o hugo.zip
mkdir bin
unzip hugo.zip -d bin
Step 2: Create a New Hugo Site
Now we can use Hugo to create the initial site structure:
./bin/hugo new site . --force
Step 3: Install the PaperMod Theme
PaperMod is perfect for a tech related blog with built-in dark mode and code highlighting. It’s also just so clean:
git init
git submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
Step 4: Configure Hugo
Create a hugo.toml
configuration file (or edit the existing one) to set up your blog:
baseURL = "/"
languageCode = "en-us"
title = "My Security Blog"
theme = "PaperMod"
# Enable syntax highlighting for code blocks
pygmentsUseClasses = true
pygmentsCodeFences = true
# Enable Archive functionality
[taxonomies]
category = "categories"
tag = "tags"
series = "series"
# Enable search page
[outputs]
home = ["HTML", "RSS", "JSON"]
[params]
# Default theme "auto", "light", "dark"
defaultTheme = "dark"
disableThemeToggle = false
# Enable search
enableSearch = true
enableRobotsTXT = true
# Show reading time on posts
ShowReadingTime = true
# Show table of contents
ShowToc = true
TocOpen = false
# Show post navigation (prev/next)
ShowPostNavLinks = true
# Show breadcrumbs
ShowBreadCrumbs = true
# Show code copy buttons
ShowCodeCopyButtons = true
# Show word count
ShowWordCount = true
# Show last modified date
ShowLastMod = true
[params.homeInfoParams]
Title = "My Security Blog"
Content = """
A technical blog focused on cybersecurity, penetration testing, and secure coding practices.
"""
[[menu.main]]
identifier = "categories"
name = "Categories"
url = "/categories/"
weight = 10
[[menu.main]]
identifier = "tags"
name = "Tags"
url = "/tags/"
weight = 20
[[menu.main]]
identifier = "archives"
name = "Archives"
url = "/archives/"
weight = 30
[[menu.main]]
identifier = "search"
name = "Search"
url = "/search/"
weight = 40
Step 5: Create Required Directories and Pages
We need to create a few key files for the navigation elements:
# Create archives page
mkdir -p content/archives
echo '---
title: "Archives"
layout: "archives"
url: "/archives/"
summary: "archives"
---' > content/archives/index.md
# Create search page
mkdir -p content/search
echo '---
title: "Search"
layout: "search"
---' > content/search/index.md
Step 6: Create Your First Post
Now let’s create your first blog post:
./bin/hugo new posts/welcome-post.md
Then edit the created file at content/posts/welcome-post.md
to add your content. Here’s a simple example that includes code blocks and formatting:
+++
title = 'Welcome to My Security Blog'
date = 2025-04-02T12:00:00+01:00
draft = false
tags = ["security", "introduction"]
categories = ["General"]
description = "Welcome to my blog focused on all things cyber!"
+++
# Welcome to My Security Blog
Hello and welcome to my cybersecurity blog! I'm excited to share tutorials, practical tips, and my real-world security experiences with you.
## What to Expect
This blog will focus on various cybersecurity topics including:
- Penetration testing
- Vulnerability research
- Digital forensics
## Sample Code Block
Here's an example of how code blocks will appear on this blog:
```python
#!/usr/bin/env python3
import socket
import sys
def simple_port_scan(target, port_range):
"""Simple port scanner function"""
open_ports = []
for port in range(port_range[0], port_range[1] + 1):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((target, port))
if result == 0:
open_ports.append(port)
sock.close()
return open_ports
if __name__ == "__main__":
target = "127.0.0.1" # Example target
ports = simple_port_scan(target, (1, 1024))
print(f"Open ports on {target}: {ports}")
Happy hacking (ethically, of course lol;)
## Step 7: Test Site Locally
Now let's test to make sure everything is working:
```bash
./bin/hugo server -D
Visit http://localhost:1313/
in your browser to see your site.
Step 8: Build Your Site for Production
Once you’re happy with your site, build it for production:
./bin/hugo
This creates all the static files in the public
directory.
Step 9: Configure Azure Static Web Apps
Create a staticwebapp.config.json
file in your project root to handle routing properly:
{
"trailingSlash": "auto",
"routes": [
{
"route": "/assets/*",
"headers": {
"cache-control": "public, max-age=31536000, immutable"
}
}
],
"responseOverrides": {
"404": {
"rewrite": "/404.html"
}
}
}
Step 10: Create Azure Resources
Now we’ll create the necessary resources in Azure:
# Login to Azure
az login
# Create a resource group
az group create --name my-blog-rg --location westeurope
# Create the Static Web App
az staticwebapp create --name my-blog --resource-group my-blog-rg --location westeurope --source /path/to/your/blog --output-location public --branch master
During this process, you’ll be given a token. Save it securely - you’ll need it for future deployments.
Step 11: Set Up Secure Credential Management
Create a .env
file to store your deployment token securely:
touch .env
echo "AZURE_DEPLOYMENT_TOKEN=your-token-here" >> .env
echo ".env" >> .gitignore
Then create a deployment script (saves a lot of time in the future):
touch deploy.sh
echo '#!/bin/bash
source .env
./bin/hugo
swa deploy ./public --deployment-token $AZURE_DEPLOYMENT_TOKEN --env production' > deploy.sh
chmod +x deploy.sh
Step 12: Deploy Your Blog
Finally, deploy your site using the Azure Static Web Apps CLI:
# Install the Azure Static Web Apps CLI
npm install -g @azure/static-web-apps-cli
# Deploy using your script
./deploy.sh
Your site will be available at the URL provided after deployment completes (usually something like https://[random-name].[region].azurestaticapps.net
).
Maintaining Your Blog
To add new posts, simply:
- Create a new markdown file:
./bin/hugo new posts/my-new-post.md
- Edit the file with your content
- Run
./deploy.sh
to rebuild and deploy your site
Conclusion
You now have a fully functioning, secure, and modern blog running on Azure Static Web Apps. This setup gives you:
- Free hosting
- Fast page loads because of Azure’s global CDN
- Secure HTTPS by default
- Easy content management through Markdown
- Version control through Git
- Dark mode ;)
If you run into any issues, check the Hugo documentation or Azure Static Web Apps documentation.
And you are done!