Building a Hugo Blog from Scratch and Deploying to OpenResty

Why Hugo

The previous blog used Next.js + Docker — powerful but overkill for a personal blog. Every build required pulling Node.js dependencies, compiling TypeScript, and bundling React. Deployment needed a running Node process.

Hugo is a static site generator written in Go, with clear advantages:

  • Blazing fast builds: entire site builds in ~160ms
  • Zero runtime dependencies: generates pure static files, no Node.js needed
  • Simple deployment: just copy HTML files to a web server
  • Single binary: one hugo command does everything

Step 1: Install Hugo

1
2
3
4
5
6
# Ubuntu/Debian - install extended version (supports SCSS)
wget -q https://github.com/gohugoio/hugo/releases/download/v0.147.5/hugo_extended_0.147.5_linux-amd64.deb
sudo dpkg -i hugo_extended_0.147.5_linux-amd64.deb

# Verify
hugo version

Note: Modern themes like PaperMod require Hugo v0.146.0+. Make sure to install a recent version. The extended edition is needed for SCSS compilation.


Step 2: Create a Hugo Site

1
2
hugo new site blog
cd blog

Hugo generates this structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
blog/
├── archetypes/    # Content templates
├── assets/        # Processable assets (SCSS, etc.)
├── content/       # Blog posts
├── data/          # Data files
├── i18n/          # Translation files
├── layouts/       # Custom layout templates
├── static/        # Static assets (copied as-is)
├── themes/        # Theme directory
└── hugo.toml      # Site configuration

Step 3: Install a Theme

Choose PaperMod — one of the most popular Hugo blog themes: clean, fast, and feature-rich.

1
2
3
4
5
# Initialize git
git init

# Install theme as submodule
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

Using git submodule makes theme updates easy:

1
git submodule update --remote --merge

Step 4: Configure the Site

Edit hugo.toml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
baseURL = 'http://blog.quickpay.wang:8026/'
languageCode = 'zh-cn'
defaultContentLanguage = 'zh'
title = 'My Blog'
theme = 'PaperMod'

[pagination]
  pagerSize = 10

enableRobotsTXT = true
buildDrafts = false

[params]
  env = "production"
  description = "Personal blog - Tech notes and life records"
  author = "admin"
  defaultTheme = "auto"
  ShowReadingTime = true
  ShowShareButtons = true
  ShowPostNavLinks = true
  ShowBreadCrumbs = true
  ShowCodeCopyButtons = true
  ShowWordCount = true
  UseHugoToc = true

[markup]
  [markup.highlight]
    codeFences = true
    guessSyntax = true
    lineNos = true
    style = "monokai"

  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true

Multilingual Setup

Hugo has native multilingual support:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[languages]
  [languages.zh]
    title = '我的博客'
    languageName = '中文'
    weight = 1

    [[languages.zh.menu.main]]
      identifier = 'home'
      name = '首页'
      url = '/'

  [languages.en]
    title = 'My Blog'
    languageName = 'English'
    weight = 2

    [[languages.en.menu.main]]
      identifier = 'home'
      name = 'Home'
      url = '/'

File naming convention for multilingual content:

FilenameLanguage
hello-world.mdDefault (Chinese)
hello-world.en.mdEnglish

Step 5: Write Content

Create Markdown files in content/posts/:

1
2
3
4
5
6
7
8
9
---
title: "Post Title"
date: 2026-05-28
tags: ["tag1", "tag2"]
categories: ["category"]
summary: "Short summary shown on list pages."
---

Content here...

Common frontmatter fields:

FieldPurpose
titlePost title
datePublication date
tagsTag list
categoriesCategory
summarySummary shown on list pages
draftSet true to skip publishing
weightSort order

Step 6: Local Preview

1
hugo server -D

Visit http://localhost:1313 for live preview with auto-reload.

Flags:

  • -D: Include draft posts
  • --disableFastRender: More accurate rendering
  • --bind 0.0.0.0: Allow LAN access

Step 7: Build and Deploy

Build Static Files

1
hugo --minify

Output goes to public/ — pure static HTML/CSS/JS.

Deploy to OpenResty

Copy static files to the site directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 1. Create site directory
mkdir -p /opt/1panel/www/blog

# 2. Copy build output
cp -r public/* /opt/1panel/www/blog/

# 3. Write Nginx config
cat > /opt/1panel/www/conf.d/blog.quickpay.wang.conf << 'EOF'
server {
    listen 8026;
    listen [::]:8026;
    server_name blog.quickpay.wang;

    root /www/blog;
    index index.html;

    location / {
        try_files $uri $uri/ $uri/index.html =404;
    }

    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
}
EOF

# 4. Test config
docker exec 1Panel-openresty-sWqr nginx -t

# 5. Reload
docker exec 1Panel-openresty-sWqr nginx -s reload

One-Click Deploy Script

Create deploy.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/bash
set -e

echo "Building Hugo site..."
hugo --minify

echo "Deploying to OpenResty..."
cp -r public/* /opt/1panel/www/blog/

echo "Testing nginx config..."
docker exec 1Panel-openresty-sWqr nginx -t

echo "Reloading nginx..."
docker exec 1Panel-openresty-sWqr nginx -s reload

echo "Done! Site deployed."

Then deploy with:

1
./deploy.sh

Comparison: Hugo vs Next.js

AspectNext.js (old)Hugo (new)
Build time~30s+~160ms
Runtime depsNode.js containerNone (static)
DeploymentDocker Compose + NginxCopy files to web dir
Memory usage~200MB+0 (static files)
Server requirementsRunning Node processJust a web server
Content formatMDXMarkdown
Theme ecosystemBuild your ownRich community themes

Final Result

Blog URL: http://blog.quickpay.wang:8026/

Features:

  • Chinese/English language switching
  • Dark/light mode (auto system detection)
  • Post tags and categories
  • Code highlighting with copy button
  • Table of contents navigation
  • RSS feed
  • SEO-friendly sitemap

Quick Reference

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# New post
hugo new content posts/my-new-post.md

# Local preview (with drafts)
hugo server -D

# Build
hugo --minify

# Deploy
./deploy.sh

# Update theme
git submodule update --remote --merge