6 min read

Ghost 博客部署及问题处理

我之前的网站,是部署在 Cloudflare 上的静态网页。每次的发布的流程是先在本地编写内容,然后上传到 Github,触发 Cloudflare 构建,根据内容生成静态页面。这太麻烦了。如果需要上传图片,还需要额外的图床管理流程。我一直很不喜欢,我甚至认为,这是我文章更新频率过低的原因之一(其实是懒🤔)。

直到我发现了 Ghost 系统,它简洁优雅,专注于文章编写。我本地测试之后发现这就是我想要的,因此我把网站迁移过来。

Ghost 简介

先介绍一下 Ghost 是什么,它是一个开源的内容管理系统(CMS),专注于为创作者、博主和出版者提供简洁、高效的博客和网站搭建平台。它使用 Node.js 构建,使用 Markdown 为核心写作方式;同时也支持卡片、HTML、等高级排版方式。界面干净、性能优异,特别适合注重内容创作而非复杂功能的用户。

自带电子邮件通信会员系统,是 Ghost 系统的特色之一,这允许用户直接订阅网站,后续可以收到更新通知。因此我也希望你能订阅我的网站,绝无消息打扰。

使用 Ghost 博客的主要好处:

  1. 专注写作体验
    Ghost 的后台编辑器基于 Markdown,界面清爽无干扰,专注于内容本身。
  2. 高性能与轻量
    基于 Node.js,Ghost 启动快、资源占用低,页面加载速度远超许多传统 CMS(如 WordPress),对 SEO 和用户体验更友好。
  3. 原生支持会员与订阅
    Ghost 内置会员系统,可轻松设置免费或付费订阅、邮件通讯(Newsletter),非常适合打造个人品牌或知识付费内容,不过目前 Ghost 正式版只支持国外的支付系统,好在 Beta 版已经有支持微信、支付宝的计划。
  4. 现代化技术栈
    Ghost 采用现代化 Web 技术,支持 API(Headless 模式),便于与前端框架(如 React、Vue)集成,适合有开发能力的程序员进行深度定制。
  5. 安全与维护简单
    Ghost 核心功能精简,插件生态克制,减少了安全漏洞和兼容性问题。官方提供托管服务(Ghost(Pro)),美元支付,体验略贵,好在支持自建部署。
  6. 开源且社区活跃
    作为开源项目,在 Github 上有 5.3万 Star,Ghost 有活跃的社区和持续更新,你既可以免费自建,也可以选择官方托管服务获得更省心的体验。

如果你有编程能力,Headless 模式的 API 可以让你随心所欲,你可以通过 API,控制 Ghost 的一切,为以后可能的博客迁移,提供了支持。我这次的内容迁移就是写个脚本,生产 Ghost 要求的 JSON 格式直接导入。

Ghost 生产环境安装

官方提供了多种方式,我建议选择 Docker 安装方式。非常简单,10 分钟即可配置完成,会自动申请配置 HTTPS 证书。

💡
Docker 安装有坑,如果 Docker 启动服务遇到问题,可以看文章末尾的问题排查。
  1. 克隆 Ghost Docker Compose 仓库。
git clone https://github.com/TryGhost/ghost-docker.git /opt/ghost && cd /opt/ghost

这是一个包含了 Caddy、Ghost、Mysql 的 Docker Compose。

  1. 复制并修改配置文件
cp .env.example .env
cp caddy/Caddyfile.example caddy/Caddyfile
vim .env

主要修改 .env 中域名、Mysql 用户名密码、邮件 SMTP(非常重要) 信息。

    1. 域名为 Ghost 网站自定义的域名,例如 mysite.com
    2. ADMIN_DOMAIN:(可选)为你的 Ghost 管理员单独建立一个域名,例如 admin.mysite.com
    3. DATABASE_ROOT_PASSWORD :Mysql ROOT 用户密码
    4. DATABASE_PASSWORD:Ghost 使用的 Mysql 用户密码
    5. SMTP 邮件部分:所选择的 SMTP 服务的配置,这非常重要(参见 :邮件配置文档 

3.配置 DNS 解析到你的机器

4.Docker Compose 启动。

# 后台启动
$ docker compose up -d
# 如果遇到启动失败,可以不要后台启动,观察启动日志
$ docker compose up

5.访问 `https://mysite.com/ghost` 进入 Ghost 后台配置信息。

如果一切顺利,你的 Ghost 博客已经安装完成了。

但人生总有意外,如果你遇到了问题可以向下看。

Ghost Dcoker 启动失败

使用 docker compose up 启动服务,发现 Mysql 总是启动失败, Ghost 容器连接 Mysql 不断报错如下:

[2025-11-26 21:33:58] INFO Ghost server started in 1.55s
[2025-11-26 21:33:59] ERROR connect ECONNREFUSED 192.168.144.2:3306

connect ECONNREFUSED 192.168.144.2:3306

"Unknown database error"

Error ID:
    500

Error Code:
    ECONNREFUSED

----------------------------------------

Error: connect ECONNREFUSED 192.168.144.2:3306
    at /var/lib/ghost/versions/6.8.1/node_modules/knex-migrator/lib/database.js:57:19
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1637:16)

[2025-11-26 21:33:59] WARN Ghost is shutting down
[2025-11-26 21:33:59] WARN Ghost has shut down
[2025-11-26 21:33:59] WARN Your site is now offline

排查分析后,发现原因是:我购买的云服务器配置过低,内存只有 1 G,Mysql 启动会因为内存不足启动失败。

解决方案:

  1. 查看机器内存,若无 swap 内存,则增加 SWAP 内存。
# 创建 swap 文件
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 永久生效(可选)
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

free -h 查看内存,最后一行的 swap 是增加的 1 G 内存。

$ free -h
              total        used        free      shared  buff/cache   available
Mem:          896Mi       720Mi       123Mi       1.0Mi       196Mi       175Mi
Swap:         1.0Gi       144Mi       879Mi
  1. 限制 Mysql 内存占用,增加启动时间。

编辑 compose.yml 中的 db 配置,为 Mysql 增加 commandmem_limit 限制内存占用。同时修改 healthcheck 增加健康检查时间。

修改后的 docker compose db 配置如下:

  db:
    image: mysql:8.0.44@sha256:f37951fc3753a6a22d6c7bf6978c5e5fefcf6f31814d98c582524f98eae52b21
    restart: always
    expose:
      - "3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD:?DATABASE_ROOT_PASSWORD environment variable is required}
      MYSQL_USER: ${DATABASE_USER:-ghost}
      MYSQL_PASSWORD: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}
      MYSQL_DATABASE: ghost
      MYSQL_MULTIPLE_DATABASES: activitypub
    mem_limit: 400m   # 限制容器最多使用 400MB 内存
    command: >
      --innodb-buffer-pool-size=32M
      --innodb-log-file-size=16M
      --key-buffer-size=4M
      --max-connections=10
      --table-definition-cache=100
      --table-open-cache=100
      --performance_schema=0
      --skip-name-resolve
    volumes:
      - ${MYSQL_DATA_LOCATION:-./data/mysql}:/var/lib/mysql
      - ./mysql-init:/docker-entrypoint-initdb.d
    healthcheck:
      test: mysqladmin ping -p$$MYSQL_ROOT_PASSWORD -h 127.0.0.1
      interval: 10s
      start_period: 120s
      start_interval: 10s
      retries: 120
    networks:
      - ghost_network

mem_limitcommandhealthcheck 为修改部分。

这时,你的 docker 就可以成功启动了,Mysql 启动较慢,耐心等待。