
2021 年冬天我把一个写了两年的技术博客从某个内容平台导出时发现评论全丢、图片一半打不开、标签结构乱成一团。真正让我难受的倒不是格式而是我突然意识到我在平台上写的东西其实随时可能变成一堆无法阅读的垃圾。后来我试过自建 WordPress、试过各种静态博客生成器最后留在身边坚持用到现在的是开源框架 Gmeek。它把博客建在 GitHub Issues 之上用 GitHub Actions 自动构建成静态站点再由 GitHub Pages 对外发布——不需要买服务器不需要维护数据库只需要一个 GitHub 账号。这篇文章就是我用 Gmeek 维护个人站点半年后的一次完整复盘聊聊我从“死了么”到“活着记”的转变也把搭建、写作、备份、踩坑的细节一次讲清楚。1. “死了么”的焦虑内容平台给的安全感其实都是租来的1.1 一场不算意外的“数据蒸发”我最初决定换地方写作不是心血来潮而是被一次真实经历吓到了。某天我照常登录一个用了三年的内容社区发现账号被限制登录原因显示“异常行为”申诉入口藏得很深解封周期以周为单位。我那三年写的一百多篇文章、几千条评论全被锁在那个账号里。当时我还能用导出功能把文章拉回来但导出的格式是平台自定义的里面嵌套着大量无意义标签图片链接指向平台域名离开平台环境根本打不开。后来我又见过很多次类似的情况某个写作 App 调整运营方向把社区版块直接下架某个博客平台宣布停止服务给用户留了三个月迁移期某个工具型产品改版后之前支持短网址全失效。这些事在网络上反复发生每次发生时都有人哀叹“我的青春没了”。我慢慢总结出一个规律只要内容存放在别人的数据库里你的话语权就是临时租用的。平台今天让你写不代表明年还让你写平台今天免费不代表它不会在某天觉得你不划算。1.2 免费平台背后的隐性成本有人会说平台给你提供了免费存储、免费带宽、免费曝光你还要什么自行车这个说法有道理但它忽略了一个前提平台的商业模式决定了它会优先保护自己的资产而不是你的资产。内容社区的核心资产就是用户生产的内容它需要这些内容带来流量、停留时长和广告收入。当某类内容不再符合平台方向或者某篇内容引发争议时平台做的第一件事往往是删除或限流而不是问作者同不同意。我不是在否定所有平台我自己现在也会在平台上发东西但心态完全不同在平台发布的是“副本”Gmeek 上的才是“底稿”。底稿掌握在自己手里副本丢了就丢了不心疼。这也是“活着记”这个名字的来历——不是某个大厂数据库里的一条记录而是在我自己能控制的目录里活着的一条条文字。你可以把它理解为平台是租来的房子Gmeek 是自建房。租的房子随时可能被房东收回自建房虽然初期要自己添砖加瓦但每一块砖都是你的。1.3 我判断一个写作系统“能活”的标准在转向 Gmeek 之前我给自己列了四条标准凡是达不到的再好看都不用判断维度常见内容平台自建 WordPressGmeek数据是否易导出经常受限可以导出天然是 Git 仓库导出就是克隆平台停止服务后能否快速迁移很难需要重新找主机换一个 Git 平台即可维护成本零需管理服务器、数据库、插件补丁构建和托管全部自动化写作体验编辑器五花八门后台较臃肿Markdown Issue极简这套标准让我不再纠结“哪个平台编辑器好看”“哪个平台推荐流量大”而是聚焦在一件事上如果三年后我想把过去写的东西完整找回来它还在不在。答案很清楚只有放在自己手里、放在开放格式里的东西才真正算数。2. 拆开看 Gmeek 的运作机制它凭什么敢承诺“活着”2.1 Gmeek 是一套“三个轮子”驱动的博客Gmeek 本质上是一个开源极简博客框架它不提供自己的服务器也不提供数据库而是把 GitHub 已有的三样东西组合起来Issues 当写作后台、Actions 当构建流水线、Pages 当静态托管。这个思路和传统的博客系统完全不一样。传统博客是“你写文章程序存数据库访问时动态渲染”Gmeek 是“你提个 Issue脚本帮你抓取 Issue 内容渲染成一堆 HTML 文件再推到静态托管服务里”。第一次听到这个组合时很多人会问Issue 不是用来报 Bug 的吗怎么还能写博客其实真正低下头看Issue 就是一个标准的 Markdown 编辑框支持标签、支持置顶、支持编辑历史、支持 API 访问而且完全免费。用它写文章本质上是用 GitHub 的“问题管理”通道做“内容管理”属于借鸡生蛋但借得很优雅。Gmeek 做的就是把这套原本给程序员协作用的工具重新包装成博客的创作和发布流程。2.2 从写完到发布一条完整的执行链路Gmeek 一次发布行为的背后大致走的是下面这条链路你在仓库的 Issues 页面点“New Issue”标题填文章标题正文写 Markdown 内容。保存 Issue 这动作会触发仓库里的 GitHub Actions 工作流。Actions 里的脚本通过 GitHub API 读取该仓库的所有 Issue把内容按发布时间排序。脚本把每篇 Issue 渲染成独立 HTML 页面同时生成首页、标签页、归档页。渲染结果提交到静态分支比如gh-pages随后 GitHub Pages 自动发布。几分钟后你的访问者通过https://你的用户名.github.io/文章路径看到新文章。这个过程的关键在于你的文章正文从来不是被某个路径写死在数据库里的而是作为一个 Issue 存在于 GitHub 的开放 API 之后。GitHub 即使哪天网页版出了问题你依然可以用命令行工具把 Issue 全部拉取到本地内容本来就是 Markdown换个渲染器马上又是一套新站点。这也是为什么我觉得“Gmeek 能活”不是一句口号而是架构自带的属性。2.3 为什么这套架构不容易“死”传统博客死掉的节点通常是这三个数据库坏了、服务器到期了、域名丢了。Gmeek 把后两个绕开了大半。服务器这块构建和托管都交给 GitHub 的免费服务你没有一台需要续费的机器数据库这块Issue 就是数据而 GitHub 天生把数据当代码管有版本历史有异地冗余。最坏的情况下GitHub 本身也关门了你的内容仍然可以完整导出因为 Git 仓库天然就是离线可用的。你可以把它推到 GitLab、Gitee或者直接本地打开阅读。当然我不认为 GitHub 是永远不会出问题的神任何第三方服务都有它的生命周期。但 Gmeek 的设计好在它的核心资产是标准格式的 Markdown 文本是人人可读的纯文本而不是某个厂商的闭源格式。这就相当于你把日记写在活页本上而不是刻在某个商场的电子屏幕里。商场关门了本子还是你的。3. 实操30 分钟搭起属于你自己的“活着记”3.1 从模板创建博客仓库在动手之前你需要先有一个 GitHub 账号这是唯一的前置条件。注册、登录之后直接在 GitHub 上搜索 “Gmeek” 模板仓库。我当时用的仓库名叫Meekdai/Gmeek你打开它之后点击页面上的 “Use this template” 按钮就能以它为底子创建自己的仓库。这里有两个细节我建议你注意第一仓库名最好直接填成你的用户名.github.io这样 Pages 的默认地址最短也最好记第二仓库可见性选 Public因为 Gmeek 的构建逻辑通常需要读取公开 Issue如果选 Private后续 Actions 访问时可能要额外配置 Token新手阶段没必要给自己添堵。创建好之后克隆到本地看一眼目录结构你会发现里面已经有一份工作流配置文件和站点配置模板接下来要做的不是从头写而是改参数。3.2 调整站点配置文件让站点先被“认领”Gmeek 的站点信息通常会集中在一个配置文件里以我当时用过的版本为例模板根目录下会有一个类似config.json的文件里面包含站名、简介、关键词、社交链接等信息。你可以打开它把默认内容替换成自己的{ siteName: 活着记, siteDescription: 在数字世界留下思想印记的私人站点, siteKeywords: 博客,Gmeek,Markdown,数字花园, author: 你的名字, socialLinks: { email: youexample.com, github: https://github.com/你的用户名, rss: /feed.xml } }这份配置在不同版本里字段会有差异千万不要照抄就完事一定要以你 fork 的模板 README 里的说明为准。我当时因为少看了一个字段说明把siteKeywords写错了格式导致首页简介一直显示空白。这种事不致命但排查起来很烦。改完配置后提交推送到远程触发一次 Actions看看站点能不能按照默认样式跑起来这是后续所有操作的基础。3.3 写下第一篇“活着记”仓库跑通后接下来就是最重要的一步写文章。回到 GitHub 仓库的 Issues 页面点击新建 Issue。不同版本的模板可能会提供不同的 Issue 模板里面有格式提示但我自己实际用的约定很简单标题就是文章标题正文就是 Markdown 全文Issues 右上角的 Labels 或 Topics 用来标记文章所属的标签。写正文时建议第一行先用一个#标题把文章题目再写一遍。这不是必须的但对阅读体验有帮助因为有些主题模板在渲染时会直接使用 Issue 标题作为页面 H1正文里的重复标题可以让文章在摘要和首页节选里看起来结构更清晰。写完保存Actions 会在后台自动构建大概两到五分钟一篇新博文就上线了。我当时的第一篇其实是“为什么我会从平台搬到这里”六百字毫无排版但看着自己的文字出现在独立域名下那种踏实感和在平台上发文章完全不一样。3.4 让站点不只是能用而是好用Gmeek 的默认主题已经比较干净但一个站点要长期用总得加一点个人印记。这里有四个小动作成本和收益比很高替换头像和站标把模板里的默认头像文件换成自己的图通常放在仓库的assets或images目录里路径以主题说明为准。置顶重点文章GitHub Issue 支持 Pin把某篇设定为置顶Gmeek 的首页脚本通常会把置顶文章排在列表最上方。给文章定义清晰的标签在 Issue 的 Labels 或 Topics 里添加你想用的词比如“读书笔记”“工具折腾”“生活记录”归档页会自动按这些词聚合。绑定自定义域名在仓库 Settings 的 Pages 区域填上你的域名再到你的 DNS 解析服务商那里加一条 CNAME 记录指向你的用户名.github.io。等待解析生效后这个站点就有了一个完全属于你的门牌号。这里多说一句域名不是必须的但如果你真的打算长期使用我强烈建议注册一个自己的域名一年也就一杯咖啡的价格。域名是你在数字世界的唯一资产虽然它也依赖注册商但它比任何平台账号都更接近“属于你”这件事。换注册商时域名可以转出而不是消失。3.5 上线前检查清单为了避免第一次部署后抓瞎我整理了下面这个检查清单每一项都花不了几分钟但能省掉很多后续麻烦检查项操作方式遇到问题时的表现Actions 是否运行成功仓库 Actions 页面看日志失败提示需要点进去读错误信息Pages 是否已开启Settings - Pages提示站点未发布站点能否通过域名访问浏览器直接打开DNS 错误 / 404文章页是否正常渲染点击首页文章链接排版错乱 / 样式丢失RSS 是否可用打开/feed.xml订阅器抓不到内容4. 用 Gmeek 写作半年后我重新理解了“记”这件事4.1 最低摩擦的写作入口把 Issue 当公开草稿箱写作这件事最大的阻力往往是“打开编辑器”这一步。以前用静态博客生成器我要在本地新建 Markdown 文件、写 front matter、推送到 git一套流程下来容易让灵感凉半截后来试过主流云笔记倒是方便但总担心哪天笔记软件转型。Gmeek 给了我一个很舒服的中间态直接在浏览器里打开 Issues 页面就像在论坛发帖一样痛快写完保存就发布短期不用管排版。更妙的是Issue 不需要一稿成文。你可以先写三行草稿扔上去隔几天再编辑编辑时所有历史版本都在不会因为手滑覆盖掉原始思路。我的很多文章都是先发一个标题就匆匆保存之后几天一点一点补充扩展像一棵慢慢长大的树。这种“半成品可见”的机制反而降低了发表的心理门槛——反正文字已经在那里了改起来不费吹灰之力。4.2 每一篇文章都是一个持续演化的版本Gmeek 给文章提供的版本能力是普通博客很难具备的。在传统博客里你发布了一篇文章再修改就是直接覆盖读者看到的是“最终版”。但在 Gmeek 里每篇文章的核心数据是一条 IssueGitHub 会忠实地记录每一次内容修改的历史。也就是说你能看到它从粗糙想法逐步演化成完整文章的脉络这本身就是很有意思的一层表达。我做了一个实验把一篇关于时间管理的文章从 800 字改成 3500 字期间经历了五次编辑标题改了两次。三个月后再回头翻看 Issue 的历史记录能看到自己思路从“如何挤时间”转变为“如何选择不做什么事”的过程。这种记录比最终文章更有价值因为它保留了“一个想法如何长大”的痕迹。对一个以写作为乐的人来说这是 Gmeek 最让我上瘾的地方。4.3 图片、附件和排版容易忽略的那些细节文字之外图片管理是静态博客绕不开的问题。我的处理原则是原图一定留在本地线上只放副本。如果你把图片直接粘贴进 IssueGitHub 会帮你托管在用户图片空间里链到 Pages 上也能显示。但这类图片 URL 携带用户编号不容易迁移。所以我后来养成一个习惯重要配图先下载到本地文件夹和文章草稿放一起发完 Issue 后再决定要不要把图片传到仓库里作为长期附件。代码块和高亮也是我重点处理的区域。Gmeek 渲染基于 Markdown所以代码块直接写三反引号就能高亮长代码块最好标注语言名称这样阅读体验干净很多。排版上我给自己定了三条规矩段与段之间空一行、列表不超过五层、每个小标题下面至少写两三句话。不是因为格式强迫症而是因为文字站点几十年后如果还能读清晰的排版是基本尊严。4.4 我踩过的四个坑任何工具用久了都会踩坑Gmeek 也不例外。这四个问题我真实遇到过写出来供你参考第一个坑是修改文章标题会导致 URL 变化。Gmeek 通常会用 Issue 标题或编号生成文章链接如果你发布后改了标题旧链接可能直接 404。所以我现在写作的习惯是标题先想清楚再发布发布后不轻易改。真遇到必须改的情况就在正文里加一段“本文原题为……”作为说明而不是依赖链接重定向。第二个坑是 Actions 偶尔不触发。公共仓库的免费额度足够但偶尔会遇到工作流排队或者 webhook 延迟你会发现发完 Issue 等了十分钟站点还是没更新。这时候不需要慌张进入仓库的 Actions 页面找到对应工作流点 “Run workflow” 手动触发一次就行。如果手动触发也不行再去看日志里是否有 API Token 过期或权限不足的报错。第三个坑是 Pages 缓存导致的旧页面残留。个别时候刷新首页看不到新文章其实是浏览器缓存。我的解决方式是 CtrlF5 强制刷新或者打开一个无痕窗口再访问。每次都强迫用户清缓存不现实对普通访客来说站点自然刷新等待几分钟就好不用太纠结。第四个坑是外链图片防盗链。有一次我在文章里引用了其他网站的一张图片在 Issue 编辑页能看到发布到 Pages 后却显示裂图。这是因为部分图片服务器设置了 referrer 检查拒绝非白名单域名加载。解决方法是先把图片下载到自己的仓库或图床再使用自己的链接。这条经验对任何博客都适用不只是 Gmeek。5. 给“活着记”加两道保险备份与长期运营5.1 第一道保险本地始终保留一份 Markdown 底稿Gmeek 虽然把内容存在 GitHub 上但我会默认“云端不是家”真正的底稿必须在自己手里。这里说的不是单纯依赖 Git 本身因为云端仓库和本地克隆实际上是同一个仓库的两个副本定期同步非常必要。我的做法是在本地建一个drafts文件夹每篇文章的源文件以“日期-标题.md”命名用 Obsidian 或者任意纯文本编辑器维护。每次发布完一篇 Issue顺手把正文复制一份到本地每次写完本地草稿再复制到 Issue 里发布。表面上看起来是多了一道工序实际上只花三十秒却能在任何极端情况下保护内容。git clone https://github.com/你的用户名/你的博客仓库.git cd 你的博客仓库 git push --mirror gitexample.com:你的名字/博客备份.git如果你更信任 Git 的完整性上面这段命令是第二层保险把整个博客仓库镜像推送到另一个 Git 托管平台仓库里的工作流和配置都能一次性带走。这样即使 GitHub 账号出了异常内容备份依然存活在另一个远端。5.2 第二道保险用脚本导出全部 Issues 做快照代码仓库里存的是配置和构建产物真正的文章正文还是在 Issue 里。所以我还额外做了一件事每个月运行一次 GitHub CLI 命令把全部 Issues 导出为结构化数据再转成本地 Markdown 文件。gh issue list \ --repo 你的用户名/你的博客仓库 \ --state all \ --limit 1000 \ --json number,title,createdAt,updatedAt,body,labels \ backup_issues.json拿到 JSON 之后再用一个小脚本批量转成 Markdown 文件存到本地。这段脚本不复杂核心思路就是遍历每条 Issue 的正文按照发布时间命名import json with open(backup_issues.json, r, encodingutf-8) as f: issues json.load(f) for issue in issues: number issue[number] created issue[createdAt][:10] title issue[title].strip().replace(/, -) filename f{created}-{number}-{title}.md with open(fbackup/{filename}, w, encodingutf-8) as f: f.write(f# {issue[title]}\n\n) f.write(issue[body] or )这个操作的意义在于Issue 即使被误删本地还能留一份可检索的快照反过来本地整理出的纯 Markdown 文件想迁移到其他任何博客系统都是零门槛。真正的“数字印记”不应该被绑定在某个产品形态上。5.3 长期运营的三条建议站点的长期运营拼的不是写作速度而是稳定。我在过程中总结出三条建议每一条都是真实踩出来的经验第一给 URL 的稳定性交足学费。发布前想清楚标题和分类发布后不做结构性调整。一个稳定的链接是内容被长期引用、被搜索引擎收录的基础改一次链接等于把之前积累的权重全部清零。第二定期检查外链和图片。每季度打开一次文章列表把失效的图片链接、失效的外部参考链接修一遍。这个过程虽然枯燥但它是“活着记”区别于“死了么”的显著标志你的内容会持续更新而不是停在某一天等着腐烂。第三记录选型理由。我建议你在本地建一个“为什么这样搭”的笔记把当初选择 Gmeek 的原因、每次改动配置的思路写下来。几年后再看你会感谢这些笔记因为技术栈一定会变化但当时的决策逻辑能帮你少走很多回头路。6. Gmeek 并不只是博客我身边出现的四种新用法6.1 家庭记忆簿把小事留下来我有个朋友看我搭好 Gmeek 之后也给自己建了一个站点但主题不是技术而是家庭记忆。她每周写一篇“生活侧写”记录孩子说过的一句话、家门口新开的面馆、一场突如其来的暴雨。Issue 的标签就成了她的时间索引想翻某个主题时直接点标签页。她说了一句话我印象很深“以前觉得发朋友圈就是在记录生活后来发现朋友圈是表演给别人的这个站点才是留给自己的。”6.2 团队内部知识库用 Issue 做 FAQ另一个场景是团队知识库。小团队不需要高大上的 Confluence仓库设为私有成员都可以提 Issue每个 Issue 就是一条知识条目用 label 标记“FAQ”“复盘”“流程”。Gmeek 构建出的站点可以给团队内部访问搜索文章、归档、回溯修改记录都方便。最让我欣赏的一点是团队离职任何成员都不会把知识带走因为它是团队的仓库而不是个人的文档。6.3 主题研究档案把卡片盒搬进 Issue如果你热衷于某个长期主题Gmeek 还能充当卡片盒。我自己的“读书笔记”就是这么做的每一本书的划线摘录和随想单独开一个 Issue标签写“书名-主题”。想写长文章的时候直接把这些 Issue 按标签串起来就变成了一篇有脉络的文章。Index 问题不再需要单独一个系统GitHub 的搜索框就能跨文章查关键词速度还很快。6.4 给十年后的自己写信最后我还在 Gmeek 上保留了一个特殊栏目标签叫“给未来”用来存放那些不适合当下公开、但希望未来某天能被重新打开的文字。因为我确信一件事一个记录系统如果能让十年前的你看到现在的你它会反过来影响你今天的决定。这些信不需要有人看只要有地方安静放着就好了。关于长期记录我自己还有一个很小的习惯每次发布完一篇新文章都顺手在首页点开自己的站点刷新一次确认它在活着然后回到本地把这篇文字的底稿放进那个永远不会背弃我的文件夹。这个动作持续了半年已经成了我生活里最稳定的一件小事。如果你也在寻找一个能让文字长久呼吸的地方Gmeek 值得一试。