这篇笔记记录了我从一台空白 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 OKcertbot.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 写进文件里。
短配置更推荐:
printfnano
3. 主题和 Hugo 版本必须一起看
很多 Hugo 主题页面能打开,不代表你的本地环境就能跑。
一定要先确认:
- 主题最低支持版本
- 是否要求
extended
4. 先打通最小链路,再追求美观
这次如果一开始就折腾主题、菜单、页面细节,排障会困难很多。
正确顺序应该是:
- Hugo 能构建
- Nginx 能访问
- DNS 正常
- HTTPS 正常
- 再换主题和美化
十二、下一步
博客虽然已经上线,但现在还只是第一阶段。接下来我准备继续补这些内容:
- 关于页
- 归档页
- 搜索页
- 头像和侧边栏资料
- 更多正式文章
这篇文章本身,就是这个博客发布出去的第一篇“建站记录”。