Ghost 博客搭建实战:从本机部署到第一篇 Markdown 草稿
Ghost 博客搭建的本机起步:从启动站点、创建 API 集成,到导入第一篇 Markdown 草稿。
这篇 Ghost 博客搭建实战只完成一个目标:把只能在本机打开的站点推进到「第一篇 Markdown 已进入后台、状态仍是草稿」的可验收阶段。你会依次确认网站和数据库正常运行、创建供自动化使用的专用接口,再把本地文章写入 Ghost;本文不会完成公网域名、HTTPS 或正式发布。
先看 2026 年 9 月 2 日的本机复核结果:
- 运行环境:Ghost 6.61.0 与 MySQL 8.0.46 正常运行;数据库处于健康状态。
- 访问边界:Ghost 只绑定在
127.0.0.1:2369。 - 页面结果:首页返回 200;5 个主题标签页返回 404,因为标签尚未关联已发布文章。
我的判断是:本课的成功标准不是「公开文章已经可见」,而是四项草稿状态成立:本地正本与 Ghost 指向同一篇文章;Ghost 保持 Draft;5 个空标签页的 404 已登记为首篇发布后的复查项;发布和排期仍被闸门阻断。
💡 通俗讲:
127.0.0.1是只供本机访问的回环地址;在没有公网隧道或代理的前提下,外部访客无法直接打开。HTTP200表示页面正常返回,404表示该地址目前没有公开页面。此阶段应看到「后台有草稿、公开文章地址仍是 404」。
要点速览
- 你会得到什么:Ghost 后台里一篇可编辑、尚未公开的 Markdown 草稿。
- 先做什么:确认首页、后台和数据库正常,再创建专用集成,最后写入并回查草稿。
- 现在不要做什么:不要配置成公开发布或排期;公网域名、HTTPS 和生产迁移不在本课完成。
- 权限怎么分:管理员账号供人使用,自定义集成(Custom Integration)供自动化工具使用;两者不应混用。
按你的起点选择阅读路径
- 零基础建站学员:先读「为什么不直接放到公网」和「三个组件的职责」,确认本机首页与后台都能打开,再继续创建集成。
- Ghost 自托管实践者:重点检查端口暴露、数据持久化、备份恢复与生产迁移条件,不要把本机可用当成上线完成。
- Markdown 内容创作者:从「如何把第一篇 Markdown 变成 Ghost 草稿」开始,重点核对正本字段、草稿身份和回写规则。
- 内容自动化入门者:从「管理员和自动化程序为什么分开授权」开始,先跑通只写 Draft 的最小链路,再考虑质检、发布或排期。
为什么第一篇 Ghost 博客不直接放到公网?
把「网站能打开」直接等同于「内容系统已经可用」,是这套流程要避免的反模式。对本文这条内容运维路径而言,文章导入、权限管理、草稿审核、发布设置和故障恢复也需要分别验收。只有在 Ghost 绑定回环地址且没有公网隧道、端口转发或反向代理时,localhost 测试才与公网隔离。若另行配置了公网入口,就应按公开环境重新检查访问控制与搜索索引风险。
本文采用 Docker Compose(容器编排工具)搭建自托管环境。以 Ghost 6 的官方 Docker 工具为例,基础安装会协调 Ghost、MySQL 与反向代理 Caddy。其他自托管方案的组件和重建步骤可能不同。若要正式自托管,仍需准备 Linux 服务器或等价托管环境、正确的域名解析与邮件服务。Ghost Docker 部署文档
如果你是从零开始,而且本机 Ghost 首页或 /ghost/ 后台还打不开,先不要继续创建集成。请先按上面的官方 Docker 文档完成本机启动;只有「首页能打开、后台能登录、数据库状态正常」三项同时成立,才进入本文后续的草稿导入流程。若其中一项不成立,就停在部署排错,不要用后面的导入命令掩盖基础环境问题。
这里不展开空服务器安装,而是给出进入内容流程前的最小验收。在 ghost-blog-lab 项目根目录运行:
docker compose -f module-1-ghost-deploy/docker-compose.yml ps
curl -I http://127.0.0.1:2369/
预期结果是 db 显示 healthy、ghost 显示 running,且首页返回 HTTP/1.1 200 OK。如果容器未运行或数据库不健康,先运行 docker compose -f module-1-ghost-deploy/docker-compose.yml logs --tail=100 ghost 检查 Ghost 日志;不进入集成和导入步骤。
💡 通俗讲:本机站点像排练室。先解决「文章写完后会去哪儿、谁有权限修改、发布前能否拦住错误」这些问题,再搬到真正的舞台。
什么是 Ghost、数据库和反向代理的职责边界?
第一次搭建时,先理解它们分别存什么、处理什么请求。Ghost 负责写作后台和网站页面,MySQL 保存文章、标签与成员。反向代理(reverse proxy)在正式上线时把访客请求安全地转给 Ghost。分清三者职责,升级、排错和备份才有明确边界。

图 1:请求入口、内容服务和数据存储各自承担不同职责。
💡 通俗讲:把网站想成一家店:Ghost 是处理内容和展示页面的前台,MySQL 是保存文章等记录的仓库,Caddy 是门口接待,先接住公网请求,再把请求交给 Ghost。出了问题时,先分清是入口、Ghost 还是数据保存出了问题。
本机部署应将 Ghost 端口绑定到回环地址,而不是直接暴露给公网。启动后可检查 Docker 的端口映射是否以 127.0.0.1 开头;只有确认没有公网隧道或代理时,才可认为该实验站不会被外部访问。正式环境应让 Caddy 或同类代理处理公网的 80/443 端口,而不是将 Ghost 的内部端口直接开放。
⚠️ 常见踩坑:修改正式域名后忘记重建 Ghost 容器,后台链接、邮件链接和规范网址(Canonical URL)都可能仍指向旧地址。Ghost 官方 Docker 文档说明,改变域名相关配置后需要重新创建相应容器。配置更新说明
⚠️ 常见踩坑:数据库凭据不能只靠「改
.env后重建」来轮换。Ghost 官方 Docker 文档明确说明:MySQL 初始化完成后,只修改DATABASE_*环境变量不会同步改变数据库内已存的账号和密码,反而会导致连接失败。需要轮换数据库凭据时,应先做好可恢复备份,再按 MySQL 的账号变更流程同步数据库和 Ghost 配置,不要只改环境变量。Ghost Docker 配置更新说明
为什么建议把管理员和自动化程序分开授权?
管理员账号用于设计站点、管理成员和人工编辑。自定义集成则以「集成」身份调用管理员接口(Admin API);它拥有 Ghost 预设的固定权限集,可执行常见发布工作流,但不等于用 Owner 或 Admin 账号取得全部用户权限。Admin API Key 不是每次请求直接使用的公开口令;服务端会用它生成最长有效 5 分钟的 JSON Web Token(JWT,签名请求令牌)。密钥只能保存在受限的服务端位置;疑似泄露时,应在 Ghost 中重新生成密钥,并同步更新使用它的工具。Ghost Admin API 认证说明

图 2:真实 Ghost 后台中的自定义集成入口;截图未包含 Admin API Key。
第一次配置时,打开 Ghost 后台的 Settings → Integrations,新增一个只供内容导入使用的自定义集成。创建后,按本机工具的要求保存管理员接口地址和 Admin API Key;它们只应进入受限的本机凭据文件,不要写进 Markdown、Git 等版本控制、公开目录或聊天记录。这一步先只验收两项:后台能看到该集成;本机工具能通过下一段的只读命令读取正确站点信息。此时尚未验证文章写入,不能据此判定草稿导入已完成。
继续导入前,先在 ghost-blog-lab 项目根目录用只读命令确认当前 profile 能连接到目标站:
python3.12 module-2-xiangyu-cms-ghost-cli/providers/ghost/ghost.py \
site-info --profile ghost-blog-lab
返回结果中应有 "code": 200,并且 title、url 指向你准备写入的站点。若提示凭据不存在、密钥格式错误或认证失败,就停在这里修正配置,不要继续运行文章写入命令。
🔍 深入一步:内容接口(Content API)适合公开读取文章,管理员接口(Admin API)才能创建和修改内容。把两者混用会扩大泄露后的影响范围。
如何把第一篇 Markdown 变成 Ghost 草稿?

图 3:写入没有报错只是中间状态,机器回读和人工检查完成后才算草稿交付。
写入前先验收环境与连接
开始前先确认三件事:Ghost 已通过 Docker 正常启动、你能进入 /ghost/ 后台、终端可以访问本机项目目录。第一篇文章使用 Markdown 作为本地正本,至少包含标题、网址路径标识(slug)、摘要和标签。之后按三个可核验节点操作:
- 保存 Markdown 正本;这表示文章内容已在本地可管理。
- 用本项目的命令行工具把 Markdown 转成 Ghost 编辑器使用的 Lexical 文档数据,再通过 Admin API 创建草稿(Draft);这表示文章已进入 Ghost 的内容库。
- 在后台打开该文章,确认状态为 Draft,并核对标题、标签、摘要和封面图。
下面的 ghost.py 是本项目仓库内的自动化工具,不是 Ghost 官方自带命令。
💡 根据你的使用环境选择路径
- 正在使用本项目:可以继续执行下面的
ghost.py命令,它已经封装 Markdown 转换、Ghost 鉴权、草稿写入和回查。 - 使用其他 Ghost 项目:不要照抄命令和路径。请按 Ghost 创建文章接口 调用 Admin API,完成同样的四步:确认身份 → 转换内容 → 写入 Draft → 读取结果并验收。
选择新建还是更新
本项目把「新建草稿」与「更新已有草稿」拆成两个写入分支。不要凭印象选择:先在 ghost-blog-lab 项目根目录运行下面的只读命令,查询目标 slug;这一步不会修改文章。
python3.12 module-2-xiangyu-cms-ghost-cli/providers/ghost/ghost.py \
posts-get my-first-ghost-blog --profile ghost-blog-lab
2026 年 9 月 3 日已用当前项目 CLI 实测两类正常分流:不存在的 slug 返回 code: 404、error_type: not_found,退出码为 3;现有草稿返回 code: 200,并在 data 中给出 id、uuid、slug、status 和 published_at。按下面的结果选择分支:
| 只读查询结果 | 应执行的动作 |
|---|---|
code: 404 且 error_type: not_found |
走分支 A;先确认本地 Markdown 字段完整,再新建草稿 |
code: 200,并返回目标文章的 id、uuid |
走分支 B;身份一致后,才按已确认的 id 更新 |
code: 401/403 或 error_type: auth |
停止;检查 Admin API Key 与 profile,不得当成文章不存在 |
error_type: network 或连接错误 |
停止;检查 Ghost、接口地址和容器状态 |
两个写入分支都显式带上 --status draft。任何不属于前两行的结果都不执行写入。
分支 A:Ghost 中还没有这个 slug
只执行新建命令;如果同名 slug 已存在,--on-exists error 会让流程停止,不会猜测或覆盖。
python3.12 module-2-xiangyu-cms-ghost-cli/providers/ghost/ghost.py \
posts-create content/ai-content-growth/20260831-my-first-ghost-blog/my-first-ghost-blog.md \
--status draft \
--on-exists error \
--profile ghost-blog-lab
新建成功后立即按 slug 只读回查,不以“创建命令没有报错”代替状态验收:
python3.12 module-2-xiangyu-cms-ghost-cli/providers/ghost/ghost.py \
posts-get my-first-ghost-blog --profile ghost-blog-lab
只有回查同时返回新的 id、uuid、目标 slug、status: draft 和 published_at: null,分支 A 才算完成。
分支 B:Ghost 中已经有这个 slug
使用分流检查中 code: 200 的结果,核对返回的 id、uuid 和本地 frontmatter 是否一致。
⚠️ 写入提醒:下面的
posts-update会修改现有 Ghost 草稿。只有分流检查返回的id、uuid与本地 frontmatter 一致时,才能把占位值换成已确认的 id 并执行。
CONFIRMED_POST_ID='REPLACE_WITH_CONFIRMED_POST_ID'
python3.12 module-2-xiangyu-cms-ghost-cli/providers/ghost/ghost.py \
posts-update "$CONFIRMED_POST_ID" \
--from-file content/ai-content-growth/20260831-my-first-ghost-blog/my-first-ghost-blog.md \
--status draft \
--profile ghost-blog-lab
更新完成后,再用只读命令回查同一个 id:
python3.12 module-2-xiangyu-cms-ghost-cli/providers/ghost/ghost.py \
posts-get "$CONFIRMED_POST_ID" --profile ghost-blog-lab
写入后核对身份与草稿状态
下面是 2026 年 9 月 3 日对本机 Ghost 执行上述只读回查得到的关键回执(其他站点的 id 和 uuid 会不同):
id: 6a963a838ff59100016037ec
uuid: c47c3232-8a25-4657-8aa5-073b37d59bf7
slug: my-first-ghost-blog
status: draft
published_at: null
未登录访问 /my-first-ghost-blog/: HTTP 404
这组结果只证明「身份是同一篇、Ghost 仍为草稿、公开地址不可见」;它不代替后面的标题、标签、摘要、封面和正文字段对照。
执行命令时,按下面五条验收:
- 新建遇到冲突就停:
--on-exists error不会生成-2或直接覆盖。 - 更新先核对身份:先用
posts-get核对 slug、id 和 uuid,再按已确认的 id 写入。 - 让工具处理版本冲突:Ghost 更新要求最新
updated_at;本项目 CLI 会在 PUT 前自动 GET 并附上该字段。 - 写入前给全标签:Ghost 更新会替换而不是合并标签关系,因此 Markdown frontmatter 必须包含完整标签列表。
- 写入后只读回查:
id和uuid与本地一致、status为draft、published_at为null,四项要同时成立。
如果提示文件不存在,先检查当前目录和 Markdown 路径;如果认证失败,回到 Integrations 检查接口地址、密钥与 profile。不要把密钥直接粘进命令。
如果你要把这一步接入自动化,第一版只串联五个关卡:读取 Markdown → 运行本地质检 → 核对文章身份后以 draft 写入 → 只读查询确认仍为 draft → 人工打开后台核对。前两项是读取与检查,中间两项是写入与机器验收,最后一项是人工验收;它们不是 Ghost 的五种文章状态。任一关失败就停止,不继续触发发布或排期。这种设计能降低误发布风险,但不是「所有错误都只会停在草稿」的保证:文章身份核对、密钥保护和人工复查仍需分别成立。
回到后台做人工字段对照
草稿创建后,仍要在 Ghost 后台检查三个地方:标题是否符合读者预期;标签是否归入正确栏目;摘要和首段是否能独立说明文章价值。Ghost 的发布设置支持为文章调整 slug、标签、访问级别、摘要和发布时间。Ghost 文章设置指南
如果标签、摘要或图片不正确,应优先修改 Markdown 正本后再回写草稿;只有后台排版问题才在 Ghost 中局部调整。这样本地文件仍是可追溯的内容来源。
对 Markdown 内容创作者,回写前后固定比较同一组字段:title、slug、custom_excerpt、tags、封面替代文字和正文。首次创建后还要记录并复查 ghost_id、ghost_uuid;后续更新时,这两个身份字段应继续指向同一篇文章,且 status 仍为 draft。任一项不一致,先回到 Markdown 修正并重新写入,不要同时在本地和后台各改一份。
发布前要检查哪些事项?

图 4:自动化可以把文章推进到草稿并完成检查,但公开发布仍由人工闸门控制。
- 标题是否同时包含主题(Ghost 博客搭建)与当前进度(本机到草稿),而不是只写「我的第一篇文章」。
- slug 是否短、稳定,并且与主题一致。
- 一级标签是否准确;不要因为名称不一致而制造重复标签。
- 是否已经添加合适的封面图、替代文字和摘要。
- 文章是否仍是草稿;本机测试文不应误发到正式站。
- 所有外部链接是否来自可信来源,且没有泄露密钥、密码或本地文件路径。
- 生产占位是否清零:确定正式域名后,将
feature_image、og_image、twitter_image和三件套 Schema 中的localhost地址全部替换,并由发布流程写入真实日期;只要仍有localhost或PUBLISH_DATE_PLACEHOLDER,就不得发布。
从本机迁移到正式网站还缺什么?
内容流程稳定后,再按优先级迁移到生产环境。先验证数据库与 Ghost 内容卷能够备份和恢复;再确定启用 HTTPS 加密的正式域名,并完成域名解析(DNS);随后配置反向代理 Caddy、邮件投递服务(SMTP)和防火墙。切换后逐项检查后台链接、文章链接和邮件链接。Ghost 官方将域名、服务器与邮件服务列为自托管的前置条件。Ghost 自托管安装概览
不要把本地 localhost 地址写进正式文章或元数据。生产地址一旦确定,应统一更新 Ghost 配置、命令行工具(CLI)凭据和工作流站点设置,再重新验证后台、文章链接和邮件链接。
对 Ghost 自托管实践者,迁移完成的判断不是「新域名能打开」,而是下面四项都通过:数据库与 content 目录已经从备份恢复验证;域名解析与 HTTPS 正常;测试邮件能够送达;使用未登录浏览器检查首页、文章地址和后台入口后,公开范围与预期一致。任一项未通过,就继续保持草稿,不进入发布。
FAQ
导航已经配置,为什么 Ghost 标签页仍然返回 404?
Ghost 只有在标签关联了已发布文章后,才会生成可访问的标签归档页。草稿不会让标签页公开,所以新站可能出现首页导航正常显示、标签入口却返回 404 的情况。第一篇文章发布后,应逐个复查这些入口。
怎样确认 Ghost 草稿没有公开?
先在 Ghost 后台或 Admin API 中确认文章状态为 Draft,并确认 published_at(发布时间字段)为空;再用未登录浏览器访问正式文章 slug(网址路径标识)。公开地址应返回 404,表示普通访客找不到该页面。后台预览能打开,只说明管理员可以检查草稿,并不表示文章已经公开。
Admin API Key 可以放在浏览器前端吗?
不可以。Admin API Key 能生成管理接口令牌,只适合受保护的服务端环境。浏览器里只应使用用于读取公开内容的 Content API Key;管理密钥应保存在受限凭据文件、密钥服务或受保护的环境变量中。
迁移或重建 Ghost 前应该备份什么?
至少要分别备份 MySQL 数据库和 Ghost content 目录。数据库保存文章、标签和成员等结构化数据,content 目录保存图片、主题与其他文件。Docker 命名卷能跨容器重建保留数据,但不能替代可恢复验证过的备份。
本机测试 Ghost 必须配置 HTTPS 吗?
仅绑定 127.0.0.1 且没有公网隧道时,本机学习环境可以先使用 HTTP。正式网站必须使用真实域名和 HTTPS,并由 Caddy 或同类反向代理处理公网流量;上线前还要检查防火墙、邮件和备份。
第一篇文章之后优先自动化什么?
先自动化 Markdown 导入,再加入元数据(标题、摘要、标签等)、结构化数据(Schema,供搜索引擎识别文章类型)、事实和链接质检,最后逐项比较本地正本与 Ghost 草稿是否一致。发布和排期应保留明确的人工确认步骤,避免把流程错误直接放大到公网。