- HTML 52.3%
- CSS 27.5%
- JavaScript 13%
- Dockerfile 7.2%
| archetypes | ||
| assets | ||
| content | ||
| deploy | ||
| layouts | ||
| static | ||
| themes | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .gitmodules | ||
| docker-compose.yml | ||
| Dockerfile | ||
| hugo.toml | ||
| README.md | ||
Dorkytiger 的小屋
个人技术文档站,用 Hugo + PaperMod 搭建。 文档版式(面包屑 + 左侧文档树 + 本页目录)是本仓库自己的模板,不依赖主题改动。
hugo server -D # 本地预览 http://localhost:1313
hugo # 构建到 public/
docker compose up -d --build # 构建镜像并启动(推荐部署方式,见第八节)
⚠️
hugo server会写public/。 它的 baseURL 是http://localhost:1313, 所以预览过一次之后,public/里的页面就带上 localhost 链接了 —— 如果你随后 把public/传上服务器,线上就会出现「点 logo 跳到 localhost:1313」。预览请用
hugo server -D --renderToMemory(只写内存,不碰public/), 或者干脆用 Docker 部署,让构建只发生在镜像里。
---
## 一、内容结构
四级模型,目录名决定 URL:
content/ ├── _index.md 首页(hero + 专题卡片) ├── kotlin/ ① 顶层分类(= 页头导航项) │ ├── _index.md ⚠️ 必须带 cascade,见第三节 │ └── kotlin-benchmark/ ② 专题(一本书) │ ├── _index.md 专题首页(导言 + 自动章节卡片) │ ├── 00-introduction/ ③ 章 │ │ ├── _index.md 章首页(导言 + 自动小节卡片) │ │ └── 01-three-kinds-of-problems.md ④ 节 │ └── 01-metrics-and-slo/ └── c-plus-plus/ ├── _index.md └── c-plus-plus-concurrent-programming/ └── _index.md 单篇长文:专题本身即文章
**不要用 `README.md` 当章首页。** Hugo 只认 `_index.md`;`README.md` 会生成一个多余的
`/章目录/readme/` 页面。本仓库早期的 9 个 `README.md` 已全部改名。
**`_index.md` 就是索引页**,标题自动渲染、子页面自动列成卡片,不需要手写目录。
---
## 二、Front matter 约定
### 节 / 文章(普通 `.md`)
```yaml
---
title: "指标分四层,缺一层就无法定位"
weight: 2
---
| 字段 | 必填 | 说明 |
|---|---|---|
title |
是 | 侧栏、面包屑、卡片都用它 |
weight |
建议 | 排序。必须 ≥ 1:Hugo 把 weight: 0 当作"未设置"排在最后 |
章 / 专题首页(_index.md)
---
title: "指标与目标" # 侧栏和面包屑里的短标签
description: "一句话说明这个专题讲什么" # 只有专题首页需要,显示在首页卡片上
weight: 2
---
命名与排序的关系
模板按文件名/目录名的数字前缀理解顺序,所以推荐:
00-introduction/ → weight 1
01-metrics-and-slo/ → weight 2
02-...
编号 NN 对应 weight: NN+1。如果你沿用数字前缀命名,其实可以不写 weight,
但显式写上更清楚。
⚠️ 不要出现
weight: 0,它会被排到最后。
三、新增内容怎么做
加一节(最常见)
在对应章目录下新建 NN-标题.md,照第二节写好 front matter。侧栏、面包屑、上下页
链接会自动出现,不需要动任何模板。
加一章
mkdir content/kotlin/kotlin-benchmark/10-new-chapter
在该目录建 _index.md(title + weight)和若干 NN-*.md。
章列表、首页卡片自动更新。
加一个专题
mkdir content/kotlin/new-book
建 content/kotlin/new-book/_index.md:
---
title: "新专题名"
description: "首页卡片上显示的一句话简介"
weight: 20
---
加一个顶层分类(C++ / Kotlin 这一层)
这一步有个必做的动作,否则新分类不会套用文档版式:
---
# content/rust/_index.md
title: "Rust"
cascade:
type: docs # ⚠️ 必需:让整棵树继承文档模板
---
cascade 写在分类根节点上而不是 hugo.toml 全局,是为了让首页保持博客版式。
页头导航会自动多出这一项(sectionPagesMenu)。
四、版式与自动化
| 需求 | 实现位置 |
|---|---|
| 面包屑(首页 / 分类 / 专题 / 章 / 文章) | layouts/_partials/doc/breadcrumbs.html |
| 左侧文档树(章节手风琴,只展开当前章) | layouts/_partials/doc/tree.html |
| 本页目录(h2/h3,滚动高亮) | layouts/_partials/doc/toc.html + assets/js/docs.js |
| 当前位置推导(书 / 章 / 文章) | layouts/_partials/doc/path.html |
导航短标签(去掉 1.2、第 3 章 · 前缀) |
layouts/_partials/doc/label.html |
| 文章页 / 章节页版式 | layouts/docs/single.html、layouts/docs/list.html |
| 站内互链解析 | layouts/_markup/render-link.html |
| 样式(含首页) | assets/css/extended/docs.css(PaperMod 自动合并此目录) |
互链可以直接写相对路径,构建时会解析成真实 URL:
[1.2 百分位不可平均](02-percentiles-and-aggregation.md)
[第 4 章](../04-toolchain/README.md) ← 旧的 README.md 写法仍兼容
解析不到的站内 .md 链接会渲染成灰色文字而不是死链。指向 ../../code/ 的配套代码
链接保留原始相对路径(见第七节)。
主题不要直接改。 themes/PaperMod 是 git submodule,所有定制都在项目根目录的
layouts/、assets/、static/ 里,通过 Hugo 的覆盖查找顺序生效。
五、首页
首页内容全部由页面树生成,加专题/章节后无需修改 layouts/index.html:
- Hero:站点标题 +
hugo.toml的[params] description+content/_index.md正文 - 左栏「全部文档」:顶层分类 → 专题,带页面数
- 「文档专题」卡片:每个专题一张,显示层级、标题、
description、章节 chips、页数
想让卡片更好看,就给专题 _index.md 补一句 description。
六、数学公式
已配置好,直接用 $...$ 和 $$...$$:
行内:平均并发数 $L = \lambda W$。
块级:
$$
W_q = \frac{\rho}{1-\rho} \cdot \frac{1}{\mu}
$$
- KaTeX 资源已本地化在
static/vendor/katex/(608 KB,v0.16.11),不依赖 CDN - 只在内容里真的有公式的页面加载,其他页面零开销
hugo.toml里的goldmark.extensions.passthrough负责保护$x_1$、$\alpha$不被 markdown 解析器破坏
已知取舍:单 $ 是公式分隔符,所以正文里写价格要转义 —— \$5。
代码块内的 $ 不受影响。
想整体关掉公式:hugo.toml 里设 [params] math = false。
七、维护备忘
目录名和文件名一旦发布就不要改,它直接决定 URL。改名请用 aliases:
aliases: ["/kotlin/kotlin-benchmark/旧路径/"]
规模控制(超过这些数量就该考虑分层):
| 层级 | 建议上限 |
|---|---|
| 顶层分类 | ~5 |
| 每类下专题 | ~7 |
| 每专题章数 | 12–15 |
| 每章节数 | ~15 |
配套代码链接:正文里约 180 处指向 ../../code/<章>/<文件>.md,那棵代码树不在本仓库。
如果要让这些链接可用,需在部署时把 code/ 目录一并放到站点根下,或后续把这些文档
并入 content/ 一起管理。
构建产物 public/ 已加入 .gitignore,不要提交。
八、部署(Docker)
站点在镜像里构建,宿主机上的 public/ 完全不参与部署。这是刻意的:
本机 hugo server 再也无法污染线上产物。
Dockerfile 两阶段:Hugo 构建 → nginx 托管(运行镜像里没有 Hugo/源码/主题)
docker-compose.yml 单服务,只读根文件系统,健康检查,日志轮转
deploy/nginx.conf gzip、指纹资源长缓存、404 页、安全响应头
.dockerignore 排除 public/ .git/ 等
.env.example 宿主机绑定地址与端口
部署
cp .env.example .env # 按需改 BIND_ADDR / BIND_PORT
docker compose up -d --build
docker compose logs -f
默认映射到宿主机的 127.0.0.1:8080(.env 里 BIND_PORT 可改),由宿主机的
反向代理(Nginx Proxy Manager / Caddy / Traefik)再转发到 dorkytiger.top。
若要让它在局域网直接可达,把 .env 里 BIND_ADDR 改成 0.0.0.0。
容器内部监听 8080 而非 80:容器以非 root 用户(uid 101)运行,绑定 1024 以下的端口需要额外 capability。反代指向宿主端口即可,无需关心容器内端口。
TLS 由宿主机或上游代理终止。如果你想让这个容器自己管证书,deploy/nginx.conf
末尾有一段注释好的 443 server 块,打开并挂载证书即可。
更新内容
git pull
docker compose up -d --build
改完内容必须 --build,否则跑的还是旧镜像。
验证镜像里的产物
# 应为 0
docker compose run --rm --entrypoint sh web \
-c "grep -rl 'localhost:1313' /usr/share/nginx/html | wc -l"
注意事项
- 克隆后要先拉主题:
themes/PaperMod是 git submodule,没拉的话镜像会构建失败 (Dockerfile 里有检查,会明确报错而不是产出一个没主题的站):git clone --recurse-submodules <repo> # 或已克隆过: git submodule update --init --recursive - baseURL 在构建期固化:
hugo.toml里的baseURL决定页面里的绝对链接 (canonical、logo、菜单)。换域名要改配置并重新--build。 hugo server不要用来产出部署文件,它写的public/带 localhost 地址。 预览请加--renderToMemory。- 文件权限:Dockerfile 会显式
chmod 644配置类文件并chmod -R a+rX站点 目录。原因是COPY会保留构建上下文里的文件模式,而容器以 uid 101 运行 —— 若源文件是600,nginx 会因读不到自己的配置而启动失败(Permission denied)。 另外站点的静态文件(content/、static/等)保持644/755更稳妥。