从 0 到上线:我如何搭建自己的 Hugo 博客

记录一次从空白 Ubuntu 服务器开始,完成 Hugo、Nginx、HTTPS 和主题部署的全过程。

这篇笔记记录了我从一台空白 Ubuntu 服务器开始,搭建并发布自己 Hugo 博客的全过程。

这次没有走现成面板,也没有用一键脚本,而是一步一步把域名、Nginx、HTTPS、Hugo 和主题都接起来。中间踩了不少坑,但也因此把整个链路摸清了。

一、准备工作

开始之前,服务器上已经跑着一个现有站点 sub2api,域名是 chen-api.cloud。因此这次部署博客时,有一个约束条件很明确:

  • 不能影响现有的 sub2api
  • 博客要部署到新子域名 blog.chen-api.cloud

这个前提决定了后面的 Nginx 配置不能覆盖原站点,只能新增一个独立 server

二、先把 Hugo 站点跑起来

一开始最先遇到的是两个典型问题。

第一个问题是命令本身输错了。我先输入了大写的 HUGO,系统直接报错:

HUGO: command not found

后来确认应该使用小写命令:

hugo

第二个问题是配置文件 config.toml 写坏了。因为我最开始用 heredoc 写文件时,把 EOF 缩进了,结果它真的被写进了文件里,Hugo 解析时报错:

unmarshal failed: toml: expected character =

修正之后,最小配置大概是这样:

baseURL = "https://blog.chen-api.cloud/"
locale = "zh-cn"
title = "CHEN 的博客"

这里最大的教训是:如果不熟悉 shell heredoc,短文件直接用 printf 或者 nano 会稳很多。

三、给博客准备最小内容

为了先验证整条发布链路,我没有一开始就上复杂主题,而是先做了一个最小首页。

最早的问题并不是 Nginx,而是 Hugo 内容文件 _index.md 写坏了。比如:

  • YAML 头信息没有正确闭合
  • 每行前面多了空格
  • 文件末尾多写了一行 EOF

这会导致 Hugo 报错:

EOF looking for end YAML front matter delimiter

修正之后,一个最小可用首页类似这样:

---
title: "首页"
---

这是我的 Hugo 博客,已经部署成功。

等 Hugo 能正常构建出 index.html 之后,才适合继续往下接 Nginx。

四、Nginx 配置必须与现有业务并存

由于服务器上已经有 sub2api,部署前先检查了当前站点配置:

  • sites-enabled 里已有 sub2api
  • 它处理的是 chen-api.cloud
  • 没有占用 blog.chen-api.cloud

这说明博客可以安全新增一个站点,而不必修改原服务。

博客对应的 Nginx 配置逻辑非常简单:

server {
    listen 80;
    listen [::]:80;
    server_name blog.chen-api.cloud;

    root /var/www/blog;
    index index.html;

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

这里有个关键判断过程。当时博客域名第一次访问并不是 404,而是:

curl: (6) Could not resolve host: blog.chen-api.cloud

这说明问题根本不在 Nginx,而在 DNS。

五、域名解析是最容易忽略的一步

之后用 nslookup 检查,发现子域名一开始是 NXDOMAIN,也就是根本没有这条记录。

必须先去 DNS 后台添加:

  • 类型:A
  • 主机记录:blog
  • 记录值:服务器公网 IP

等 DNS 生效之后,再次检查:

nslookup blog.chen-api.cloud

已经能正确解析到服务器 IP,这时候再访问 HTTP,才真正走到了 Nginx。

六、403 的根因不是权限,而是没生成首页

DNS 生效之后,博客域名开始返回 403 Forbidden

这一步很容易误判成目录权限问题,但实际排查后发现根因是:

  • /var/www/blog 目录里没有 index.html
  • Hugo 因为内容文件错误,根本没有生成首页

也就是说,403 只是结果,真正的问题还是构建失败。

这次部署最大的经验之一就是:先看生成目录里有没有实际产物,再怀疑 Nginx。

七、HTTPS 比部署本身更顺利

http://blog.chen-api.cloud 能返回 200 OK 后,HTTPS 基本就是顺水推舟了。

直接使用 certbot --nginx 申请并挂载证书。过程中系统提示这个域名已经存在证书,于是选择了“重新安装已有证书”,而不是重新签发。

最终验证结果:

  • https://blog.chen-api.cloud 返回 200 OK
  • certbot.timer 已启用
  • 证书会自动续期

到这里,博客已经算正式上线。

八、换主题时又踩到了 Hugo 版本坑

博客最初虽然可用,但只是一个临时首页,不像正式博客。后来我选择切换到 Hugo Theme Stack

主题装上之后,新的问题出现了:

function "hash" not defined

排查后发现并不是主题坏了,而是服务器自带 Hugo 版本太旧:

  • 当前版本:0.92.2
  • Stack 主题最低要求:0.157.0

所以必须升级 Hugo。

九、普通版 Hugo 还不够,必须用 extended

升级到新版本之后,构建又报了另一个错误:

TOCSS: failed to transform "/scss/style.scss"

这说明我虽然把 Hugo 升级了,但装的是普通版,不是 extended 版。

Stack 主题会编译 SCSS,因此必须安装官方 extended 版本。

替换完成后,最终版本变成:

hugo v0.161.1+extended

到这一步,主题才真正构建成功,博客首页也切换成了卡片式布局。

十、最终结果

现在这套博客已经具备了基础可用状态:

  • 域名:blog.chen-api.cloud
  • Web 服务:Nginx
  • 证书:Let's Encrypt
  • 博客引擎:Hugo
  • 主题:Stack
  • 自动续期:certbot.timer

而且整个过程没有影响服务器上原来的 sub2api 服务。

十一、这次部署里最有价值的几个经验

最后总结几条我这次最有体感的经验:

1. 不要过早怀疑 Nginx

访问异常不一定是 Nginx 配错了,也可能是:

  • DNS 没生效
  • Hugo 没生成文件
  • 内容文件格式写坏

2. heredoc 对新手并不友好

cat <<EOF 看起来方便,但实际非常容易因为缩进、结尾位置或者误输入,把 EOF 写进文件里。

短配置更推荐:

  • printf
  • nano

3. 主题和 Hugo 版本必须一起看

很多 Hugo 主题页面能打开,不代表你的本地环境就能跑。

一定要先确认:

  • 主题最低支持版本
  • 是否要求 extended

4. 先打通最小链路,再追求美观

这次如果一开始就折腾主题、菜单、页面细节,排障会困难很多。

正确顺序应该是:

  1. Hugo 能构建
  2. Nginx 能访问
  3. DNS 正常
  4. HTTPS 正常
  5. 再换主题和美化

十二、下一步

博客虽然已经上线,但现在还只是第一阶段。接下来我准备继续补这些内容:

  • 关于页
  • 归档页
  • 搜索页
  • 头像和侧边栏资料
  • 更多正式文章

这篇文章本身,就是这个博客发布出去的第一篇“建站记录”。

使用 Hugo 构建
主题 StackJimmy 设计