Ghost 博客搭建实战:从本机部署到第一篇 Markdown 草稿

Ghost 博客搭建的本机起步:从启动站点、创建 API 集成,到导入第一篇 Markdown 草稿。

青蓝色自动化流程把本机 Markdown 文档经过校验与安全处理写入 Ghost 草稿,并以 DRAFT 状态停在人工发布闸门前,呈现 Ghost 博客搭建从本机部署到草稿验收的完整链路

这篇 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 是只供本机访问的回环地址;在没有公网隧道或代理的前提下,外部访客无法直接打开。HTTP 200 表示页面正常返回,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 显示 healthyghost 显示 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。分清三者职责,升级、排错和备份才有明确边界。

浏览器请求经 Caddy 反向代理进入 Ghost,Ghost 再读写 MySQL 的博客部署架构图

图 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 认证说明

Ghost 后台 Settings 的 Integrations 页面,Custom 标签中显示专供自动化使用的 ghost-blog-lab-cli 集成

图 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,并且 titleurl 指向你准备写入的站点。若提示凭据不存在、密钥格式错误或认证失败,就停在这里修正配置,不要继续运行文章写入命令。

🔍 深入一步:内容接口(Content API)适合公开读取文章,管理员接口(Admin API)才能创建和修改内容。把两者混用会扩大泄露后的影响范围。

如何把第一篇 Markdown 变成 Ghost 草稿?

Markdown 转换为 Lexical 后经 Admin API 写入 Draft,再执行机器回读与人工确认的内容自动化流程

图 3:写入没有报错只是中间状态,机器回读和人工检查完成后才算草稿交付。

写入前先验收环境与连接

开始前先确认三件事:Ghost 已通过 Docker 正常启动、你能进入 /ghost/ 后台、终端可以访问本机项目目录。第一篇文章使用 Markdown 作为本地正本,至少包含标题、网址路径标识(slug)、摘要和标签。之后按三个可核验节点操作:

  1. 保存 Markdown 正本;这表示文章内容已在本地可管理。
  2. 用本项目的命令行工具把 Markdown 转成 Ghost 编辑器使用的 Lexical 文档数据,再通过 Admin API 创建草稿(Draft);这表示文章已进入 Ghost 的内容库。
  3. 在后台打开该文章,确认状态为 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: 404error_type: not_found,退出码为 3;现有草稿返回 code: 200,并在 data 中给出 iduuidslugstatuspublished_at。按下面的结果选择分支:

只读查询结果 应执行的动作
code: 404error_type: not_found 走分支 A;先确认本地 Markdown 字段完整,再新建草稿
code: 200,并返回目标文章的 iduuid 走分支 B;身份一致后,才按已确认的 id 更新
code: 401/403error_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

只有回查同时返回新的 iduuid、目标 slugstatus: draftpublished_at: null,分支 A 才算完成。

分支 B:Ghost 中已经有这个 slug

使用分流检查中 code: 200 的结果,核对返回的 iduuid 和本地 frontmatter 是否一致。

⚠️ 写入提醒:下面的 posts-update 会修改现有 Ghost 草稿。只有分流检查返回的 iduuid 与本地 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 执行上述只读回查得到的关键回执(其他站点的 iduuid 会不同):

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 必须包含完整标签列表。
  • 写入后只读回查iduuid 与本地一致、statusdraftpublished_atnull,四项要同时成立。

如果提示文件不存在,先检查当前目录和 Markdown 路径;如果认证失败,回到 Integrations 检查接口地址、密钥与 profile。不要把密钥直接粘进命令。

如果你要把这一步接入自动化,第一版只串联五个关卡:读取 Markdown → 运行本地质检 → 核对文章身份后以 draft 写入 → 只读查询确认仍为 draft → 人工打开后台核对。前两项是读取与检查,中间两项是写入与机器验收,最后一项是人工验收;它们不是 Ghost 的五种文章状态。任一关失败就停止,不继续触发发布或排期。这种设计能降低误发布风险,但不是「所有错误都只会停在草稿」的保证:文章身份核对、密钥保护和人工复查仍需分别成立。

回到后台做人工字段对照

草稿创建后,仍要在 Ghost 后台检查三个地方:标题是否符合读者预期;标签是否归入正确栏目;摘要和首段是否能独立说明文章价值。Ghost 的发布设置支持为文章调整 slug、标签、访问级别、摘要和发布时间。Ghost 文章设置指南

如果标签、摘要或图片不正确,应优先修改 Markdown 正本后再回写草稿;只有后台排版问题才在 Ghost 中局部调整。这样本地文件仍是可追溯的内容来源。

对 Markdown 内容创作者,回写前后固定比较同一组字段:titleslugcustom_excerpttags、封面替代文字和正文。首次创建后还要记录并复查 ghost_idghost_uuid;后续更新时,这两个身份字段应继续指向同一篇文章,且 status 仍为 draft。任一项不一致,先回到 Markdown 修正并重新写入,不要同时在本地和后台各改一份。

发布前要检查哪些事项?

Ghost 草稿依次经过自动检查和人工批准,发布闸门才允许进入公开发布状态的流程图

图 4:自动化可以把文章推进到草稿并完成检查,但公开发布仍由人工闸门控制。

  • 标题是否同时包含主题(Ghost 博客搭建)与当前进度(本机到草稿),而不是只写「我的第一篇文章」。
  • slug 是否短、稳定,并且与主题一致。
  • 一级标签是否准确;不要因为名称不一致而制造重复标签。
  • 是否已经添加合适的封面图、替代文字和摘要。
  • 文章是否仍是草稿;本机测试文不应误发到正式站。
  • 所有外部链接是否来自可信来源,且没有泄露密钥、密码或本地文件路径。
  • 生产占位是否清零:确定正式域名后,将 feature_imageog_imagetwitter_image 和三件套 Schema 中的 localhost 地址全部替换,并由发布流程写入真实日期;只要仍有 localhostPUBLISH_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 草稿是否一致。发布和排期应保留明确的人工确认步骤,避免把流程错误直接放大到公网。

参考来源