Welcome to my blog! Rather than just saying “hello,” I figured the first post should actually be useful — so here’s a full breakdown of how this site was built and deployed.
The Tech Stack
| Layer | Tool |
|---|---|
| Static site generator | Hugo |
| Theme | PaperMod |
| Hosting | GitHub Pages |
| CI/CD | GitHub Actions |
| Dev environment | GitHub Codespaces |
| Custom domain | blog.brianli.net via DNS CNAME |
| TLS | Let’s Encrypt (auto-provisioned by GitHub) |
Total cost: $0 (aside from the domain itself).
Why Hugo?
Hugo is a static site generator written in Go. It takes Markdown files and turns them into a fully static HTML site — no database, no server-side runtime, no attack surface. Builds are fast (we’re talking milliseconds for small sites) and the output is just files you can host anywhere.
The alternative I considered was Next.js, but that felt like overkill for a personal blog. Hugo keeps things simple.
The Theme: PaperMod
PaperMod is a clean, minimal Hugo theme with good defaults out of the box: dark/light mode toggle, reading time estimates, code copy buttons, breadcrumbs, and fast load times. It’s added as a Git submodule, which means the theme lives in its own repo and is pinned to a specific commit:
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
Using a submodule keeps the theme code out of this repo while still locking it to a known-good version.
Hosting: GitHub Pages + GitHub Actions
The entire build and deploy pipeline is a single GitHub Actions workflow (.github/workflows/hugo.yml). Every push to main triggers it:
- Build job — installs Hugo Extended v0.145.0, checks out the repo (including the PaperMod submodule), and runs
hugo --minifyto produce the static site in./public/. - Deploy job — takes the built artifact and pushes it to GitHub Pages via the official
actions/deploy-pagesaction.
No manual FTP, no S3 bucket, no Netlify account needed. Push to main and the site is live within ~30 seconds.
Custom Domain Setup
GitHub Pages serves the site at lbrian357.github.io by default, but I wanted it at blog.brianli.net. The setup is straightforward:
- Add a
CNAMEfile to thestatic/folder containingblog.brianli.net— Hugo copies it to the root of the built site, and GitHub Pages reads it automatically. - In the DNS settings for
brianli.net, add a CNAME record: - In the GitHub repo’s Settings → Pages → Custom domain, enter
blog.brianli.net. - Check Enforce HTTPS — GitHub auto-provisions a TLS certificate via Let’s Encrypt once DNS propagates.
DNS propagation can take anywhere from a few minutes to 48 hours depending on the registrar and TTL settings.
The Dev Environment
Everything was built inside a GitHub Codespace — a cloud-hosted VS Code instance with a full Linux environment. No local installs required. GitHub Copilot helped scaffold the initial config and workflow file.
What’s Next
This is post one. Future posts will cover whatever I’m working on — engineering, projects, and things I find interesting. If you want to follow along, the source for this site is public at github.com/lbrian357/brianli-blog.