零成本博客实战①:三个仓库、两条命令,全站不花一分钱
一句话结论:三个仓库(源码 / 发布 / 图床)+ 两条命令(
git push、博客园同步),全站 0 成本,图片和评论都不用买服务。
写了十年 Java,笔记散在 CSDN、博客园、掘金、语雀里。最后发现最难的不是写,而是找回来——同一个知识点,四个平台各有一半。
于是决定自己搭一套:内容自己管、发布自动化、一分钱不花。这套方案已经稳定跑了几个月,每天写、每天发,没出过岔子。
一、为什么不留在大平台
| 方案 | 成本 | 数据归属 | 定制能力 | 迁移成本 |
|---|---|---|---|---|
| 博客园 / 掘金 / 知乎 | 免费 | 平台 | 几乎没有 | 高 |
| CSDN | 免费 | 平台 | 低 | 高 |
| WordPress + 主机 | ¥300+/年 | 自己 | 强 | 中 |
| Notion / 语雀 公开页 | 免费 | 平台 | 弱 | 中 |
| GitHub Pages + 静态站 | ¥0 | 自己 | 强 | 低 |
这些平台我基本都用过一轮。最早在 CSDN 写,那会儿确实省事,发出去就有人看;后来广告越堆越多,正文被挤在中间,排版、外链、甚至导出都开始受限,写起来越来越像在给别人打工,最后整站搬走。
平台给的流量是真的,但内容被广告包围、被规则牵着走也是真的。这也是我最后决定自己搭的根本原因:内容的所有权和呈现方式,得握在自己手里。
静态博客的本质只有一句话:Markdown 文件 + 构建脚本 + 免费托管。GitHub 三样都能给。
代价也要说清楚:没有后台、没有数据库、评论要外挂、全文搜索得自己做。对技术博客来说,这些都不是硬伤。
二、整体架构:三个仓库各司其职

| 仓库 | 职责 | 谁在写 |
|---|---|---|
blog-build |
VuePress 源码、文章、站点配置 | 你 |
your-name.github.io |
构建产物,Pages 托管 | CI 自动推 |
blog-images |
文章配图与封面 | 你 |
为什么不合成一个仓库:发布仓库会被 CI 反复覆盖写,和源码混在一起后 git log 会变成一锅粥;图床单独放,图片可以无限往历史里堆,不影响站点构建。
三、第一步:本地跑起来
# Node 20+,pnpm 9+
pnpm create vuepress-theme-hope my-blog
cd my-blog
pnpm install
pnpm docs:dev # 本地预览 http://localhost:8080
pnpm docs:build # 产出 src/.vuepress/dist
目录约定是关键:源码放 src/,构建产物进 src/.vuepress/dist,草稿放 draft/。
{
"scripts": {
"docs:dev": "vuepress-vite dev src",
"docs:build": "vuepress-vite build src"
}
}
draft/ 不参与构建,是这套方案最舒服的地方:草稿写多久都不会上线。想发的时候,把文件 git mv 到 src/ 对应分类,permalink 早就定好,链接永远不会变。
Windows 下如果
pnpm docs:build报ERR_PNPM_IGNORED_BUILDS,直接用node_modules\.bin\vuepress-vite.cmd build src绕过 pnpm 的构建脚本限制。
四、第二步:发布仓库与 CI
4.1 建发布仓库
新建一个名为 <用户名>.github.io 的公开仓库(比如 your-name/your-name.github.io),什么都不用放。Pages 默认从 main 分支根目录提供服务。
4.2 配一个最小权限的 PAT
CI 要往另一个仓库推代码,默认的 GITHUB_TOKEN 权限不够,必须用 Personal Access Token:
- Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Repository access 只勾
your-name.github.io - Permissions 只给 Contents: Read and write(别给 admin,也别给全仓库)
- 生成后,在
blog-build仓库的 Secrets 里存成PERSONAL_TOKEN
这里是最容易翻车的一步:secret 名字写错、token 过期、权限给少了,CI 都会在 deploy 阶段失败,而报错信息很含蓄。第一次跑 CI 一定要盯着日志。
4.3 workflow
name: deploy
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm docs:build
- uses: peaceiris/actions-gh-pages@v4
with:
personal_token: ${{ secrets.PERSONAL_TOKEN }}
external_repository: your-name/your-name.github.io
publish_branch: main
publish_dir: src/.vuepress/dist
推一次 main,2~3 分钟后线上就更新了,整个过程不用碰服务器。
4.4 两个必做的细节
.nojekyll:GitHub Pages 默认跑 Jekyll,会忽略下划线开头的目录。VuePress 产物里有 _assets 这类路径,不加这个空文件会白屏。
touch src/.vuepress/public/.nojekyll
base 路径:项目页(/repo-name/)必须配 base: "/repo-name/";用户页保持 base: "/"。配错的典型症状是页面能开、样式全丢。
五、第三步:图床用 Git 仓库
用仓库当图床,好处是不花钱、不担心图床跑路、图片和文章一起版本化。
https://cdn.jsdelivr.net/gh/your-name/blog-images-1@main/blog/05.ai/03.rag/01/xxx.png
三条实践约定:
- 路径跟文章对齐:
blog/{分类}/{序号}/xxx.png,日后一眼知道属于哪篇 - 先推图,再推文:图没上传就发文,线上就是一片 404
- 文件名语义化:
git-reset.png比image-20261030-2213.png有用得多
还有一条反例:别把图放进站点 public/。那会一起打进构建产物——我踩过,一张 6.8MB 的 GIF 直接把 dist 从 3.6MB 顶到 11MB。
5.1 仓库放不下了怎么办
一个仓库装几百张图很轻松,但长期写、图又多,迟早会到 1GB 量级。解法是把图床做成可横向扩展的编号序列:
blog-images-1 ← 前 N 年的图
blog-images-2 ← 满了开下一个
blog-images-3 ← …
blog-images-n
规则很简单:按年份或分类分库,路径结构保持一致,只换 raw URL 前缀。老图不用迁移,老链接永久有效,成本还是 0。
六、第四步:评论用 giscus
静态站没有后端,评论走 giscus,把 GitHub Discussions 当评论库——免费、不用备案、还能用 GitHub 登录。
comment: {
provider: "Giscus",
repo: "your-name/your-name.github.io",
repoId: "R_xxxxxxxxxxxx",
category: "Announcements",
categoryId: "DIC_xxxxxxxxxxxx",
}
坑在于 categoryId:它是 Discussions 分类的 node id,不是分类名。在 giscus.app 填仓库后会自动带出来;如果自己瞎填,评论区会静默不渲染,控制台也不报错。
还要记得:发布仓库必须开启 Discussions,giscus 应用要装上并有权限。
七、第五步:导航与分类
两级分类够用:一级按知识域(Java / Spring / AI / Tools…),二级按主题。每个目录一个 README.md 作为落地页:
---
title: 工程工具
date: 2026-10-30
categories: [tools]
tags: [tools]
permalink: /pages/xxxxxx/
hidden: true
index: false
article: false
---
article: false 很关键——不加的话目录页会混进文章列表。
导航两处要一起改:
// navbar.ts —— 顶部
{ text: "Tools", icon: "mdi:toolbox", prefix: "/06.tools/", children: [...] }
// sidebar.ts —— 侧边栏,children: "structure" 自动跟随目录
{ text: "工程工具", prefix: "06.tools/", collapsible: true, children: "structure" }
八、permalink 与 404
主题会给每篇文章生成 permalink: /pages/xxxxxx/,真实页面都在这个路径下;原始目录路径(/06.tools/01.git/03-xxx.html)在静态产物里并不存在,直接访问会 404。
两种解法:
- 全站只用 permalink 做链接——简单,但分享出去的语义化链接就废了
- 生成跳转页——写个小插件,在
onGenerated阶段按filePathRelative推导原始路径,生成带meta refresh+canonical+noindex的 HTML
我用第二种,50 来个跳转页,体积可以忽略。两种链接都能开,SEO 也只认 permalink。
九、成本清单:确实是 0
| 项目 | 免费额度 | 够不够用 |
|---|---|---|
| GitHub Pages | 公开仓库,1GB 站点、100GB/月流量 | 博客绰绰有余 |
| GitHub Actions | 公开仓库免费不限量;私有仓库 2000 分钟/月 | 一次构建 ~20 秒 |
| 图片存储 | 每个仓库约 1GB,可开 images-2 ~ n 横向扩 |
按年分库,几年都够 |
| 域名 | <user>.github.io 免费 |
想自定义再买,¥60/年 |
| 评论 | giscus(Discussions) | 免费 |
| 搜索 | 主题内置 slimsearch | 免费 |
唯一的隐性成本是构建时间:文章多了以后 vuepress build 会从 15 秒涨到 1 分钟,但跑在 CI 上,感知不到。
十、踩过的坑
- CI deploy 失败:八成是
PERSONAL_TOKEN没配或权限不足,fine-grained token 一定要给目标仓库Contents: write - 文件编码:Windows 上搬来的 Markdown 可能是 GBK 或带 BOM,构建时 frontmatter 解析失败、中文乱码,发之前统一转 UTF-8 无 BOM
- 图片拖垮构建:大图直接进
public/会让 dist 膨胀数倍,所有文章配图走图床 - 评论区不显示:
categoryId填错、仓库没开 Discussions,两个原因占九成 - 改目录名导致老链接 404:permalink 写在 frontmatter 里,跟着文件走;先定 permalink,再动目录
十一、这套方案适合谁
适合:想长期写、在意内容所有权、愿意折腾一次配置的技术人。
不适合:需要后台编辑器、多人协作审核、复杂 SEO 运营的场景——那种情况老老实实上 WordPress 或付费 CMS。
配置一次之后,每天只需要:写 Markdown → 推图 → git push,剩下的交给 CI。
作者原创声明
- 作者:amyaxy
- 首发:amyaxy.github.io · 2026-10-30
- 原文:/pages/c866eb/
- 许可:CC BY-NC-SA 4.0,本文为原创,转载请注明出处

浙公网安备 33010602011771号