海外云服务器搭建 Discourse 论坛完整教程:自建社区讨论区 + 邮件收发 + S3 备份(2026最新版)
Meta Description: 海外云服务器自建 Discourse 论坛完整教程:从零部署这套开源社区系统,覆盖服务器选型、官方一键脚本与手写 app.yml 双路线、PostgreSQL/Redis/Sidekiq 单容器架构、SMTP 邮件配置与"必须用子域名"红线、Nginx 反代 unix socket 共存方案、S3 兼容对象存储备份、插件挂载与一键升级,另含自建 vs 官方托管成本对比表、服务器配置推荐表、服务商价格参考表与 12 条常见问题 FAQ。
> 关键词:Discourse 论坛搭建、自建社区系统、海外云服务器论坛、Discourse 安装教程、Discourse Docker 部署、Discourse 邮件配置、Discourse Nginx 反代、Discourse 自动备份、开源论坛程序、Discourse app.yml
前言:为什么要自己搭一个论坛,而不是再开一个群
一句话答案:Discourse 是开源圈里最"能长期活下去"的社区系统——它把讨论按话题结构化沉淀、天生被搜索引擎收录、支持直接用邮件参与讨论,而官方又只提供 Docker 一种安装方式,所以理论上你只需要一台 2 核 2G 的海外云服务器加一条官方命令,就能跑起一个不属于任何平台的社区。
这件事的价值,在于它解决的是"群聊"解决不了的问题:
- 群聊的内容会消失,论坛的内容会积累。 一个 500 人的 Telegram 群里,正确答案只出现过一次,三天后没人再找得到;而在论坛里,"如何配置反向代理"是一个可以被搜索、被链接、被顶起来的话题,三年后仍然在给人省时间。 - 群聊没有 SEO,论坛本身就是内容资产。 论坛的每一个话题都是一个独立 URL,可以被 Google 收录、被外部引用。对做独立站的人来说,这是唯一一种"用户自己帮你生产内容"的 SEO 方式——你写 100 篇博客,用户能帮你写 1000 个话题。 - 群聊赶人,论坛留人。 群是同步的(必须在线)、有压迫感的(消息 999+);论坛是异步的(想回再回)、有沉淀感的。社区要长大,必须让新人有"先搜索再提问"的入口,群聊给不了这个入口。 - 邮件是论坛的杀手级入口。 Discourse 最被低估的能力是全流程邮件交互:用户可以订阅某个话题、只靠收邮件跟完全部讨论,甚至直接回一封邮件就完成发帖。这是封闭群聊永远做不到的"异步+外部"参与方式。
如果你手上已经有一台海外云服务器在跑 WordPress 独立博客 或 Typecho 轻量博客,想给读者加一个"能对话、能沉淀"的地方;或者你在 Shopify 独立站 上卖货,想建一个用户互助区来降低客服成本——这篇教程就是为这两种需求写的。推荐用阿里云国际版 / AWS / 腾讯云国际版部署,通过 5.chengzicloud.cloud 购买享折扣;由于 Discourse 对磁盘 IOPS 和内存的敏感度远高于普通博客站,选机器时请把"SSD 云盘 + 2GB 内存起步"当成硬门槛而不是可选项。
本文给出可直接复制执行的完整命令,以 Ubuntu 22.04 / Debian 12 为主线,并单独说明 CentOS 7 / RHEL 的差异。内容顺序是:先划边界(Discourse 和你已经装过的东西各管什么)→ 再讲官方硬性要求与三条"官方明确说做不到"的红线 → 服务器配置与价格参考 → 两条完整部署路线(官方一键脚本 / 手写 app.yml)→ 邮件配置 → Nginx 反代共存 → 备份与升级 → 成本对比 → 12 条常见问题。
一、先分清边界:Discourse 和站内已有方案各管什么
自建圈最大的困惑不是"怎么装",而是"这些东西看起来都能承载讨论,到底该用哪个"。下表先把边界切干净,避免你把 Discourse 当成 WordPress 插件式的轻量东西,也避免你选完才发现自己根本不需要它:
| 方案 | 它回答的问题 | 前提条件 | 与本文的关系 | |---|---|---|---| | Discourse | 怎么让一群人围绕话题长期讨论,内容可搜索、可沉淀、可邮件参与 | 一台独占 80/443 的海外云服务器(≥2GB 内存);一个独立(子)域名;一个能发信的 SMTP | 本文主角 | | WordPress / Typecho 博客 | 怎么单向发布内容,让搜索引擎收录 | 一台服务器 + PHP + MySQL,可与其他站点共存 | 边界表对比:发布层(我写)vs 讨论层(大家写),两者互补不替代 | | Telegram / 微信群 | 怎么即时沟通、临时协作 | 无 | 适合"通知与闪聊",不适合沉淀;Discourse 的话题可以反过来把结论分享回群里 | | Nextcloud | 怎么自建私有云盘、共享文件 | 一台服务器 + Docker | 需求是"存文件"选它;需求是"讨论内容"选 Discourse | | Gitea / GitLab | 怎么托管代码、跑 CI,顺便开 Issue 讨论 | 一台服务器 + Docker | GitLab 的 Issue 可以当轻量讨论区,但它是开发者视角的,不适合面向普通用户的社区 | | 宝塔面板 | 怎么图形化建站、管数据库、一键装 PHP | 一台服务器 | ⚠️ 官方明确不支持用宝塔/cPanel/Plesk 安装 Discourse,原因见第二节 | | Rocket.Chat / 自建 IM | 怎么自建实时聊天工具 | 一台服务器 + Docker | 同属"协作家族",但它是同步聊天,与 Discourse 的异步论坛是两件事 |
一句话选型:要"大家能提问、能被搜到、新人能自学" → 用 Discourse;要"我发布、读者只看" → 用 WordPress/Typecho;要"实时拉个群说事" → 用 Telegram/Rocket.Chat。 三者不是替代关系,独立站运营的成熟形态通常是"博客(发布)+ 论坛(讨论)+ 群(通知)"三件套:博客里每篇文章底部挂一句"相关问题欢迎到论坛讨论",论坛里的话题又能反哺博客的选题。
还有一条必须提前说清的诚实边界:Discourse 不适合"只有三五个人的小团队"。它的官方最低配置是 1 核 1G + swap,但那是"能跑"的极限,不是"能舒服用"的配置;它包含了 Rails、Sidekiq 后台任务、PostgreSQL、Redis 四个服务,空闲状态下的内存占用就接近 1GB。如果你的场景是一周几十条消息,用 Telegram 群 + 一个静态站就够了,上 Discourse 属于杀鸡用牛刀。它的甜蜜区是"已经有几十到几千个活跃用户、内容需要被检索和归档"。
二、官方硬性要求,以及三条"官方明确说做不到"的红线
Discourse 的官方安装文档(discourse/discourse 仓库的 docs/INSTALL.md 与 docs/INSTALL-cloud.md)写得非常坦白,其中有三句话属于"照着抄就能避开大坑"的类型,本节先把它们单独拎出来。
2.1 官方硬性要求
下表是官方文档给出的最低与推荐配置,以及每一条背后的原因:
| 项目 | 官方最低 | 官方推荐 | 为什么 |
|---|---|---|---|
| CPU | 1 核("现代单核") | 2 核 | 编译资产、跑 Sidekiq 后台任务、Rails 多进程都要吃 CPU;重建容器时单核会非常慢 |
| 内存 | 1 GB(必须配 swap) | 2 GB 以上 | Rails + Sidekiq + PostgreSQL + Redis 四件套同容器运行,空载即接近 1GB |
| 磁盘 | 10 GB | 20 GB 以上 | 系统 + Docker 镜像 + 数据库 + 用户上传的图片附件;镜像本身就有数 GB |
| 系统 | 64 位 Linux,且内核支持 Docker | Ubuntu LTS | 官方文档与社区排错几乎全按 Ubuntu 写,Debian 12 同样可用 |
| 域名 | 一个独立的(子)域名 | forum.example.com | DISCOURSE_HOSTNAME 只接受域名,不接受裸 IP;整站按根路径设计,不能装在 example.com/forum 这种子目录下 |
| 邮件 | 一套能正常发信的 SMTP | 第三方邮件服务商 | 官方明确说"要让 Discourse 正常工作,必须配好邮件" |
如果不用 Docker 而想裸装,官方给出的软件清单是:PostgreSQL(大版本需与官方 Docker 镜像内的一致)、Redis 7、Ruby 3.2+。看到这个清单你就该明白为什么官方只支持 Docker——你不仅要装齐这四个服务,还要自己管 Sidekiq 和 Rails 进程、自己配 Nginx。
2.2 红线一:官方只支持 Docker 安装,宝塔 / cPanel / Plesk 一律不支持
官方原文的措辞相当直接(来自 docs/INSTALL.md):
> 唯一被官方支持的安装方式是 Docker。 你必须有一台支持 Docker 的 64 位 Linux 服务器,并且有 SSH 权限。很遗憾,我们不支持任何其他安装方式,包括 cPanel、Plesk、Webmin 等面板。
这条对我们站内的读者特别重要:你没法像装 WordPress 那样,在 宝塔面板 里点一下"一键部署 Discourse"。 官方给出的理由也值得一读——托管 Rails 应用很复杂,即使你服务器上已经装好了 PostgreSQL、Redis 和 Ruby,你仍然要操心 Sidekiq 与 Rails 进程的守护、以及 Nginx 的配置;用 Docker,官方那套调优过的配置直接放进一个容器里,还附赠一个网页 GUI,升级只需点一下按钮。
实操结论:如果你已经在服务器上装了宝塔,Discourse 仍然能装,但要按本文第五节的 Nginx 反代方案——让 Discourse 躲在容器里、监听 unix socket,把 80/443 让给宝塔的 Nginx 或你已有的站点。另一种做法是给论坛单独开一台机器,最省心。
2.3 红线二:邮件不是可选项,而且发信域名必须用子域名验证
这是整篇教程里最容易踩、后果最隐蔽的一条。官方邮件文档(docs/INSTALL-email.md)的第一句就是:
> 我们强烈建议使用专业的邮件发送服务。邮件服务器的搭建与运维即使对资深系统管理员来说也非常困难,这套复杂的邮件配置里任何一环出错,结果都是邮件发不出去,或者更糟——时好时坏地发。
紧接着是那句黑体警告:
> 请注意:在任何邮件服务商那里,你必须验证并使用子域名,例如 discourse.example.com。如果你只验证了主域名(例如 example.com),邮件将无法被正确配置。
原因在于 Discourse 发信时用的是"论坛主机名"作为发件域与 Return-Path。你如果只把主域名加进服务商的白名单,返回路径上的 DKIM/SPF 对不上,Gmail 和 Outlook 会直接把验证邮件丢进垃圾箱甚至拒收——而你看到的现象是"用户收不到激活邮件",不是任何一条报错。正确做法:在邮件服务商后台把 discourse.example.com(不是 example.com)作为发信域,并配上该子域的 SPF、DKIM 记录。
官方还提醒必须处理退信(bounce):用第三方邮件服务时,你要么开启 VERP,要么在服务商后台配置 webhook 回调,把退信事件推给 Discourse。否则 Discourse 会一直往已经失效的邮箱发信,发信域的声誉会被拖垮,最后牵连所有正常邮件。
2.4 红线三:80 / 443 被占用时,官方安装向导会直接失败
官方的进阶文档("在同一台机器上运行其他网站")开头就是一句警告:当你机器上已经有别的服务在监听 80 或 443 时,你无法使用 ./discourse-setup 来安装 Discourse,必须手工复制并编辑 samples/standalone.yml 再引导容器。
这条把很多人挡在了门口——尤其是那些"已经用宝塔跑着一个 WordPress 站、想在同一台机器上加个论坛"的读者。别担心,这是完全可行的,只是要走"外置 Nginx + unix socket"的路线,第五节会给出逐行可复制的配置。
三、服务器配置推荐与价格参考
这一节要先纠正一个直觉:论坛比博客重得多,但它重的地方不是"流量",而是"常驻内存"。 Discourse 官方给出的最低配置是"1 核 1G + swap",注意这个 swap 是必需项而不是优化项——官方在硬件要求里把 swap 和内存并列写在一起。原因是容器里同时跑着 PostgreSQL 和 Redis,而这套技术栈在内存不足时的表现不是"变慢",而是在重建镜像或重建索引时被内核 OOM Killer 杀掉,你看到的现象是 ./launcher rebuild 跑到一半失败、容器起不来。所以选机器时请按下面这张表对号入座,别按"我的博客 1 核 1G 就够了"来推。
3.1 服务器配置推荐(按社区规模)
| 使用场景 | 推荐配置 | 为什么够用 / 为什么不够 |
|---|---|---|
| 个人测试、内部小圈子(< 50 人,日均几十帖) | 2 核 2G + 50GB SSD,并配 2GB swap | 官方推荐档的下限。1 核 1G 也能跑,但每次升级重建都会提心吊胆,且后台任务(发 digest、处理图片)会明显拖慢 |
| 正式社区起步(几百用户,日活几十) | 2 核 4G + 80GB SSD | 本站给绝大多数读者的默认答案。PostgreSQL 的 db_shared_buffers 官方口径是"最多占内存的 25%",4G 机器能给它 1GB 缓存,话题列表和搜索的响应会稳定一个档次 |
| 活跃社区(数千用户,图片附件多) | 4 核 8G + 160GB SSD,上传走 S3 兼容对象存储 | 内存给后台任务和数据库,磁盘瓶颈用"附件外置到对象存储"解决(见 5.6 节,可对接站内 MinIO 自建 S3) |
| 多社区 / 多站点(一个容器托管多个论坛) | 4 核 8G 起 | Discourse 原生支持 multisite:一套代码 + 一套数据库,按域名路由到不同社区,比给每个论坛开一台机器省得多 |
| 高可用 / 免停机升级 | 拆成"data 容器 + web 容器"的多容器架构,web 侧至少 2 台 | 多容器的核心价值就是升级时不掉线:先在旁边 bootstrap 出新 web 进程,构建完成再切流量;单容器升级必然有几分钟不可用 |
> ⚠️ 三条实操提醒:① 一定要开 swap,官方安装向导会主动问你"是否为低内存机器创建 swap",别拒绝——fallocate -l 2G /swapfile 那一组命令在第六节 FAQ 里直接给。② 上传目录和数据库请放在本地 SSD 上,别挂到 NFS 或网络盘,PostgreSQL 对网络存储的延迟极其敏感。③ 如果你打算用两条 2 核 2G 的机器做"应用 + 数据库"分离,务必用防火墙或 iptables 保护 PostgreSQL 和 Redis 的端口,官方在多容器文档里专门用 WARNING 标了这一条。
3.2 服务商价格参考
下面是本文实际会用到的两档配置的公开官网参考区间。注意 AWS Lightsail 和 DigitalOcean 的价格是可直接对标到具体档位的固定月费(含流量额度),而阿里云国际版与腾讯云国际版的 ECS/CVM 属于可自由配置的机型,同配置在不同地域、不同代次、不同活动下的价差很大,所以给的是区间:
| 服务商 | 2 核 2G 档 月付 | 2 核 4G 档 月付 | 磁盘 | 适合本文哪一步 |
|---|---|---|---|---|
| DigitalOcean Droplet | $18(2 vCPU / 2 GiB / 60 GiB SSD / 3 TB 流量) | $24(2 vCPU / 4 GiB / 80 GiB SSD / 4 TB 流量) | 含 SSD | 官方文档与社区排错最贴合的生态,forum.example.com 一键跑通 |
| AWS Lightsail | $12(2 vCPU / 2 GB / 60 GB SSD / 3 TB 流量) | $24(2 vCPU / 4 GB / 80 GB SSD / 4 TB 流量) | 含 SSD | 固定月费最好预算,最低档 $5 起(0.5 GB,本文不推荐) |
| 阿里云国际版 ECS(通用型) | 约 $12~$20 | 约 $24~$38 | 另计或含盘 | 中文控制台、支持支付宝、与国内生态打通 |
| 腾讯云国际版 CVM / 轻量 | 约 $10~$18 | 约 $22~$32 | 另计或含盘 | 香港/新加坡/东京节点,到国内延迟低 |
| Vultr / Hetzner 等 | 约 $10~$18 | 约 $20~$28 | 含 SSD | 按小时计费,适合先跑一遍再定档 |
> 💡 一条容易被忽略的成本项:Discourse 的磁盘会持续增长,主要来自用户上传的图片附件和数据库膨胀(maximum_backups 默认保留 5 份备份也要占地方)。所以两档配置我建议直接买 60~80GB 的盘,而不是"先 25GB,不够再加"——云盘扩容虽然在线可做,但 Discourse 的 shared/standalone 目录用的是宿主机目录挂载,迁移前还得停机重建容器。磁盘一次到位,比事后扩容便宜。通过 5.chengzicloud.cloud 购买以上平台还可享额外折扣。
> 声明:本文价格数据采集于 2026 年 9 月,仅为公开官网参考区间。云服务器价格随区域、计费方式和活动浮动,请以各厂商官网实时价格为准。除特别标注外,金额单位均为美元(USD)。
3.3 那要不要干脆用官方托管?先把这笔账算清
Discourse 官方同时提供托管服务,所以"自建到底省不省"是可以直接比较的——这也是判断这套方案值不值得自己维护的最好标尺。官方定价页(2026 年 9 月口径)的档位是:Free 档(用于评估,功能受限)、Pro $100/月起、Business $500/月起、Enterprise 需询价,另外提供付费的迁移服务。
| 对比项 | 自建(本文方案) | 官方托管 Pro 档 |
|---|---|---|
| 月度成本(2 核 4G 档) | 约 $24/月(Lightsail/DO 官价) | $100/月起 |
| 你负责什么 | 服务器、Docker、操作系统安全更新、备份、升级点击、SMTP 额度 | 只负责社区运营 |
| 邮件投递 | 自己配第三方 SMTP(有免费档,见第五节) | 官方代发 |
| 升级与安全补丁 | 网页点一下 /admin/upgrade 或命令 ./launcher rebuild app(有数分钟停机) | 平台侧滚动升级,用户无感 |
| 数据主权 | 数据库、附件、备份全在你自己的盘上,随时可整站打包搬走 | 数据在对方基础设施上,导出能力受档位限制 |
| 最适合谁 | 已经在维护服务器、要数据主权、预算敏感、能接受偶尔自己排错 | 需要有人兜底、团队里没有运维角色、愿意用钱换时间 |
一句话结论:这不是"省 $76/月"的问题,而是"你要不要为数据主权和运维能力买单"的问题。 如果你已经按本站教程维护着几台服务器、看得懂 SSH 和 Nginx,那么自建 Discourse 的边际成本极低,而且顺手就把"邮件 + 备份 + 反代"这套通用能力练了一遍;如果你只是想要一个社区而不想碰服务器,官方 Pro 档其实是更诚实的选择。在自建路线上真正贵的东西从来不是软件,而是你花在排错上的时间——本文的结构正是为了把这个时间压到最短。
四、部署实操:从空机器到"域名能打开论坛"
下面的步骤以 Ubuntu 22.04 / Debian 12 为主线,RHEL / CentOS 7 的差异会单独标出。全程假设你的论坛域名是 forum.example.com——请把文中所有 forum.example.com 和 [email protected] 换成你自己的真实值。
第 0 步:三件必须先在"服务器之外"做完的事
| 前置条件 | 怎么确认 | 不做的后果 |
|---|---|---|
| 域名 A 记录已指向服务器 IP | dig +short forum.example.com 回显你的服务器公网 IP | 安装脚本的 DNS 校验会失败;即使跳过,Let's Encrypt 也签不出证书(官方文档明确写了可用 --skip-connection-test 临时绕过校验,但那只是绕过检查,不是绕过问题) |
| 云安全组 / 防火墙放行 80 与 443 入站 | ss -lntp \| grep -E ':80\|:443' 看本机有没有别的进程占用 | 容器起得来但外部打不开;这是"配置全对却访问不了"的头号原因。⚠️ 注意 ufw/firewalld 管不到云平台的安全组,两层都要放 |
| 准备 2GB swap | free -h 看 Swap 那一行是否非 0 | 内存不足时 ./launcher rebuild 会在编译资产阶段被 OOM Killer 杀掉,报错五花八门、极难定位 |
创建 swap 的完整命令(官方 FAQ 同款,2G 起步):
`bash
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile swap swap defaults 0 0' >> /etc/fstab
free -h
`
第 1 步:安装 Docker
Discourse 的部署脚本会自动帮你装 Docker(如果你机器上没有),但手工路线需要你先装好。用官方的一键脚本最省事:
`bash
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker version
`
RHEL / CentOS 7 走 Docker CE 官方仓库:
`bash
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable --now docker
`
> ⚠️ CentOS 7 的两点提醒:① glibc 2.17 不是问题——Discourse 是全容器化部署(Rails、PostgreSQL、Redis、Nginx 全在官方镜像里),宿主机的 glibc 版本根本不参与运行,这与本站此前多篇自建教程(Immich / Loki / Harbor / Cloudflare Tunnel)的结论一致;② 如果 SELinux 处于 enforcing,容器挂载 /var/discourse/shared 时可能报权限错误,可先 setenforce 0 验证,确认是 SELinux 后再决定永久调整为 permissive 还是给目录打上合适的标签。
第 2 步:路线 A —— 官方一键脚本(推荐新手)
官方现在提供了一条真正意义上的"一行命令",它会自动装 Docker 与 git、下载 Discourse 的 Docker 配置、并启动一个交互式安装向导:
`bash
wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/main/install-discourse | sudo bash
`
向导会依次问你要三样东西:
1. 管理员邮箱(可以填多个,用逗号分隔)——这个邮箱注册后自动获得管理员权限;
2. 域名选哪种:用自己的域名(填 forum.example.com),或者用官方的免费子域名 mysite.discourse.diy(在 id.discourse.com/my/subdomain 领取,安装时点 "Generate Code" 拿一个 6 位验证码,10 分钟内有效,然后脚本会自动帮你建好 DNS A 记录,完全不用手工配 DNS);
3. 要不要跳过邮件(SMTP)配置——可以跳过,但代价见第 6 步。
脚本还会自动完成这些事:探测公网 IP、按硬件自动设置 UNICORN_WORKERS 与 db_shared_buffers、校验 80/443 端口是否可用、校验域名是否解析到本机、并在低内存机器上主动提议创建 swap。确认后开始构建,大约 5~10 分钟,完成后 HTTPS 证书由 Let's Encrypt 自动签发,不需要你额外配置任何一项。
> 💡 只想先把环境铺好、之后再手动构建,可以加参数:... | sudo bash -s -- --skip-rebuild。
第 3 步:路线 B —— 手工拉取配置(已有站点 / 想完全可控)
当你的 80/443 已经被别的东西占用,或者你想精确控制 app.yml 里的每一个参数时,走这条路。它也是官方"进阶安装"文档描述的方式:
`bash
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
cp samples/standalone.yml containers/app.yml
nano containers/app.yml
`
containers/ 目录默认是空的,归你自己管;samples/ 里放的是官方模板。standalone.yml 的意思是"把所有东西塞进一个容器"——优点是一步到位,缺点是升级时需要整容器重建。
如果你想用官方那个交互式向导来填参数(它会逐项问你域名、邮箱、SMTP),在 80/443 空闲的前提下可以直接跑:
`bash
cd /var/discourse
./discourse-setup
`
但请记住第二节的红线三:只要 80 或 443 上有别的服务在跑,这条命令就没法用,只能手工编辑 app.yml。
第 4 步:把 app.yml 写对(关键参数逐个说明)
下面是一份可以直接抄的 standalone.yml 骨架(已把 SSL 两个模板解锁,让容器自己签证书):
`yaml
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
- "templates/web.ratelimited.template.yml"
- "templates/web.ssl.template.yml"
- "templates/web.letsencrypt.ssl.template.yml"
expose: - "80:80" - "443:443"
params: db_default_text_search_config: "pg_catalog.english"
env: LC_ALL: en_US.UTF-8 LANG: en_US.UTF-8 LANGUAGE: en_US.UTF-8 DISCOURSE_DEFAULT_LOCALE: zh_CN DISCOURSE_HOSTNAME: "forum.example.com" DISCOURSE_DEVELOPER_EMAILS: "[email protected]" DISCOURSE_SMTP_ADDRESS: smtp-relay.brevo.com DISCOURSE_SMTP_PORT: 587 DISCOURSE_SMTP_USER_NAME: "your-smtp-user" DISCOURSE_SMTP_PASSWORD: "your-smtp-password" DISCOURSE_NOTIFICATION_EMAIL: [email protected]
volumes: - volume: host: /var/discourse/shared/standalone guest: /shared - volume: host: /var/discourse/shared/standalone/log/var-log guest: /var/log
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone https://github.com/discourse/docker_manager.git
`
各段的含义与常见误填:
| 配置项 | 作用 | 必须注意 |
|---|---|---|
| templates: | 决定容器里装哪几块组件 | postgres / redis / web / web.ratelimited(限流)四块是底座;web.ssl + web.letsencrypt.ssl 两个模板默认是被注释掉的,要自己解锁,否则只有 HTTP;走外置 Nginx 反代时反而要把这两个注释掉 |
| expose: | 容器映射到宿主机的端口 | 默认 80:80 + 443:443。想只让本机访问可以写成 127.0.0.1:20080:80(官方支持的 host_ip:host_port:container_port 语法),这是"不想暴露端口"时最干净的做法 |
| params.db_shared_buffers | PostgreSQL 共享缓冲 | 官方口径:最多设为总内存的 25%,且会自动设置。2G 机器别手工填成 512MB 以上 |
| DISCOURSE_HOSTNAME | 论坛的对外域名 | 必填,且不接受裸 IP。Discourse 不工作在 http://1.2.3.4 这种地址上 |
| DISCOURSE_DEVELOPER_EMAILS | 谁是初始管理员 | 逗号分隔。注册时必须用这个邮箱,否则拿不到管理员权限 |
| DISCOURSE_SMTP_PASSWORD | SMTP 密码 | ⚠️ 密码里含 # 时必须用引号包起来("#pass#ord"),否则 YAML 会把 # 之后当成注释 |
| DISCOURSE_SMTP_FORCE_TLS | 在连接时就启用隐式 TLS | 用 465 端口的服务商需要设为 true;587 通常是 STARTTLS,一般不用设 |
| DISCOURSE_SMTP_DOMAIN | HELO/EHLO 域名 | 只在服务商明确要求时才设,乱设会触发发信失败 |
| DISCOURSE_MAXMIND_ACCOUNT_ID / DISCOURSE_MAXMIND_LICENSE_KEY | 按 IP 判断访客国家 | 到 MaxMind 免费注册 GeoLite2 拿一对凭据填进来,社区运营会用到"按地区统计"时再配 |
| volumes: | 数据落盘位置 | 容器是无状态的,所有数据都在 /shared。所以备份 = 备份 /var/discourse/shared/standalone,重建容器不会丢数据 |
| hooks.after_code | 装插件 | 在这段 cmd 列表里加 git clone 即可,每次重建自动拉取 |
> ⚠️ 一个官方专门用 WARNING 标出来的坑:Discourse 容器里的账号 UID 固定是 1000。如果你宿主机上 UID 1000 被别的账号占了(比如某些面板创建的第一个用户),重建容器后 /var/discourse/shared 里的文件属主会错乱,表现为站点起不来或附件 404。干净的系统上可以用 cat /etc/passwd 确认一下。
第 5 步:构建并启动容器
首次构建用 bootstrap(引导出一个新容器),之后修改了 app.yml 用 rebuild(销毁旧容器 + 引导 + 启动新容器):
`bash
cd /var/discourse
./launcher bootstrap app
./launcher start app
./launcher logs app
`
launcher 是这套方案唯一的运维入口,常用子命令如下:
| 命令 | 作用 |
|---|---|
| ./launcher bootstrap app | 按 app.yml 模板引导出一个新容器(首次用) |
| ./launcher start / stop / restart app | 启动 / 停止 / 重启容器 |
| ./launcher rebuild app | 销毁旧容器 → 引导 → 启动,修改 app.yml 后必须走这一步;有数分钟停机 |
| ./launcher enter app | 进入容器内部开一个 shell(排错最可靠的手段) |
| ./launcher logs app | 看容器日志 |
| ./launcher destroy app | 停止并删除容器(数据在 /shared,不会丢) |
| ./launcher cleanup | 清理停止超过 24 小时的容器,回收磁盘 |
| ./launcher start-cmd app | 只打印将要执行的 docker 命令,用于排查参数问题 |
另外两个实用的隐藏参数:--skip-prereqs(跳过前置检查)、--docker-args STRING(追加 docker 参数)。如果放在进程守护工具(systemd、supervisor)下面,可以设环境变量 SUPERVISED=true,容器就不会以分离模式启动,交给外部工具管重启。
第 6 步:注册管理员并配好邮件
构建完成后访问 https://forum.example.com,用你在 DISCOURSE_DEVELOPER_EMAILS 里填的那个邮箱注册,注册成功即自动成为管理员,随后会进入站内的设置向导。
如果你在第 2 步跳过了 SMTP,脚本会自动启用 Discourse ID 登录:用户通过 id.discourse.com 完成邮箱验证,也可以直接用 Google / Facebook / Apple / GitHub 登录,并且仍然能收到浏览器 Web Push 通知。这对"先跑起来、稍后配邮件"完全够用。但只要你要用邮件摘要、邮件回复、邮寄列表模式这几项,就必须配 SMTP——而且以后补配时仍要跑一次 ./launcher rebuild app。
官方邮件文档推荐的第三方 SMTP 服务商模板(参数直接照填):
| 服务商 | 官方标注的免费额度 | SMTP 地址 | 端口 | 用户名 / 密码 |
|---|---|---|---|---|
| Brevo(原 SendInBlue) | 300 封/天 | smtp-relay.brevo.com | 587 | SMTP & API 页面里的凭据 |
| Mailgun | 无试用版 10k 封/月 | smtp.mailgun.org | 587 | 域内 SMTP 凭据 |
| SendGrid | 30 天试用 40k 封 | smtp.sendgrid.net | 587 | 用户名固定填 apikey,密码填 API Key |
| Mailjet | 6k 封/月(单日上限 200) | 后台 "SMTP and SEND API Settings" 生成 | 587 | API Key / Secret Key |
| Elastic Email | —— | smtp.elasticemail.com | 2525 | 注册邮箱 / API Key |
> ⚠️ 两条必读:① 发信域必须是子域名。 官方原文强调:在任何邮件服务商那里都必须验证并使用子域名(如 discourse.example.com),只验证主域名会导致邮件配置不正确。② 必须处理退信。 用第三方服务时要开启 VERP 或在服务商后台配置 webhook,把退信事件回传给 Discourse;否则你会持续往失效邮箱发信,发信域声誉被拖垮,最终所有正常邮件一起进垃圾箱。
>
> 📌 另外官方特别说明:Discourse 的 Docker 镜像里不包含 postfix、exim 或任何 MTA,理由是"配好一个 MTA 太难了"。所以不要试图在本机自建邮件服务器来绕开这一节——那是另一个量级的工作,本站另有 邮件服务器搭建 专篇,但那套东西适合当"收件和业务邮箱",用来做 Discourse 的事务性发信并不划算。还有一个细节:Elastic Email 默认会在每封邮件底部加一条退订链接,需要找他们关掉,否则会和 Discourse 自身的订阅管理打架。
改完邮件配置后应用生效:
`bash
cd /var/discourse
./launcher rebuild app
`
五、进阶:和已有站点共存(外置 Nginx 反代)与附件外置到对象存储
5.1 什么情况下需要这一节
如果这台机器上只有 Discourse,前面的路线 A 已经结束了,容器自己签证书、自己占 80/443,你不需要这一节。但下面这两种情况非常普遍,都必须走外置反代:
- 服务器上已经有 Nginx / 宝塔 / Apache 跑着 WordPress 博客、Nextcloud 或别的站,80/443 被占着; - 你想让所有站点共用一个反向代理和一套证书管理,而不是每个服务各自签一次证书。
官方对此的态度是明确的"可行但要懂":文档开头就标注 NOTE: This is for advanced admins,并且强调前提是你已经有一个能正常工作的 Discourse——如果不满足这个前提,你很难判断配置到底哪一层出了问题。所以实操建议是:先在一台干净机器或干净域名上把 Discourse 跑通一次,再迁到共享机器的反代架构上。
5.2 改容器定义:从"占端口"变成"占一个 socket 文件"
第一步是把容器停掉,然后再动配置(顺序别反):
`bash
cd /var/discourse
./launcher stop app
`
接着编辑 containers/app.yml,做三件事:
1. 解锁 socket 模板:在 templates: 列表末尾加上 templates/web.socketed.template.yml;
2. 注释掉两个 SSL 模板:web.ssl 与 web.letsencrypt.ssl 都要失效,因为 HTTPS 由外层 Nginx 负责;
3. 整段取消 expose::不再向宿主机映射 80/443,容器改为把 Nginx 暴露在一个 unix socket 上。
改完后 templates: 段应该长这样(注意 expose: 整段已经不存在了):
`yaml
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
- "templates/web.ratelimited.template.yml"
- "templates/web.socketed.template.yml"
params: db_default_text_search_config: "pg_catalog.english"
env:
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
LANGUAGE: en_US.UTF-8
DISCOURSE_DEFAULT_LOCALE: zh_CN
DISCOURSE_HOSTNAME: "forum.example.com"
DISCOURSE_DEVELOPER_EMAILS: "[email protected]"
DISCOURSE_SMTP_ADDRESS: smtp-relay.brevo.com
DISCOURSE_SMTP_PORT: 587
DISCOURSE_SMTP_USER_NAME: "your-smtp-user"
DISCOURSE_SMTP_PASSWORD: "your-smtp-password"
`
然后重建容器,让 Discourse 的数据通过 socket 暴露出来:
`bash
/var/discourse/launcher rebuild app
`
重建完成后,宿主机上会出现这个文件,它就是外层 Nginx 要反代的目标:
`bash
/var/discourse/shared/standalone/nginx.http.sock
`
> 💡 如果你的反代用不了 unix socket(某些面板的图形化反代只支持填 IP:端口),那就别用 socket,改成让容器监听一个本机端口:把 expose: 段写成一行 - "127.0.0.1:8080:80",其他不变,然后反代到 http://127.0.0.1:8080。注意 127.0.0.1: 前缀很重要——只写 8080:80 会让 Docker 监听 0.0.0.0,而 Docker 自己写的 iptables 规则会绕过 ufw/firewalld,等于把未加密的论坛端口直接挂到公网上。
5.3 外层 Nginx 配置(含必须透传的四个头)
装好 Nginx 与 certbot 后,为论坛建一个 site,核心是 location / 里那段代理配置:
`nginx
server {
listen 80;
server_name forum.example.com;
location / {
proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}
}
`
四个头的作用,一个都不能少:
| 配置 | 少了它会怎样 |
|---|---|
| proxy_pass 指向 nginx.http.sock | 这是容器内 Nginx 的入口。注意 URL 末尾那个冒号(...nginx.http.sock:)不能漏,它是 unix socket 的语法要求 |
| proxy_set_header Host $http_host | Discourse 靠 Host 判断访问的是哪个站点(多站点场景靠它路由)。用 $host 而不是 $http_host 会丢掉端口信息 |
| proxy_http_version 1.1 | Discourse 前端大量使用长轮询。用 HTTP/1.0 会导致页面消息不刷新、通知不弹出 |
| X-Forwarded-Proto / X-Real-IP / X-Forwarded-For | Discourse 靠它们判断"用户是从 HTTPS 来的"以及真实客户端 IP。缺失会导致混合内容报警、头像不显示、限流失效、日志里全是反代 IP |
改完 reload 一下,然后签证书:
`bash
nginx -t
systemctl reload nginx
certbot --nginx -d forum.example.com
`
> ⚠️ 宝塔面板用户:宝塔的"网站 → 反向代理"里填 http://127.0.0.1:8080(配合 5.2 的端口方案),并把上面四个头在"配置文件"里手动补进去;图形界面默认只帮你填代理目标和 Host,另外三个头经常要自己加。
5.4 反代跑不通时的排错顺序
| 现象 | 先看哪里 | 常见原因 |
|---|---|---|
| 502 Bad Gateway | /var/log/nginx/error.log,再看 ./launcher logs app | socket 文件不存在(容器没起来 / 没 rebuild);socket 路径写错(单容器是 shared/standalone/,双容器是 shared/web-only/) |
| 页面能开但样式全乱、按钮无响应 | 浏览器控制台是否报混合内容 | web.ssl 模板没注释掉、或 X-Forwarded-Proto 没传。取决于架构,选一个方向把它调一致 |
| 能打开但一直"加载中" | 是否漏了 proxy_http_version 1.1 | 长轮询被降级成 HTTP/1.0 |
| 后台看所有用户 IP 都是 127.0.0.1 或反代 IP | 三个 X-Forwarded-* 头 | 这会让"按 IP 限流/封禁"完全失效,属于安全问题不只是显示问题 |
| 论坛报错且容器日志正常 | /var/discourse/shared/standalone/log/rails/production.log | Rails 侧的真实报错都在这里,比容器日志详细得多 |
5.5 顺带一提:多容器架构(升级不掉线)
standalone 的卖点是"简单",代价是每次升级都有数分钟停机。官方提供的另一条路是拆成两个容器:一个 data 容器跑 PostgreSQL 与 Redis(模板 samples/data.yml),一个 web_only 容器跑 Rails(模板 samples/web_only.yml)。多容器的好处官方列得很清楚:
- 升级时可以先在旁边把新 web 进程引导好,构建完成再切流量,用户几乎无感; - 可以把论坛扩到多台服务器、加冗余; - 数据库可以单独跑到更猛的机器上。
配置上要多填几个环境变量(DISCOURSE_DB_HOST / DISCOURSE_DB_PASSWORD / DISCOURSE_REDIS_HOST 指向 data 容器),web 侧的挂载点是 /var/discourse/shared/web-only,所以反代的 socket 路径也变成 .../shared/web-only/nginx.http.sock。launcher 会自动向容器注入一个 DISCOURSE_HOST_IP 环境变量供你引用。
> ⚠️ 官方用 WARNING 标出来的唯一一条:多容器模式下,一定要用防火墙或 iptables 保护 PostgreSQL 与 Redis 的端口。单容器时它们躲在容器内部不可见,拆开之后就是暴露在网段里的两个高危服务——这也是本站 网站安全加固 篇反复强调的老问题。
5.6 附件外置:把用户上传的图片丢进对象存储
前几节都假设附件存在宿主机磁盘上(/var/discourse/shared/standalone/uploads)。社区一旦活跃起来,图片附件就是磁盘的头号杀手,而云盘扩容在 Discourse 这套挂载结构下需要停机重建容器。所以从 4 核 8G 那一档开始,务实的做法是把附件和备份一起外置到 S3 兼容对象存储——官方支持的清单里包括 AWS S3、Cloudflare R2、DigitalOcean Spaces、Linode Object Storage、Scaleway、Google Cloud Storage 等,你也可以对接站内 MinIO 自建对象存储(它提供 S3 兼容接口,正好用得上)。
在 app.yml 的 env: 段里追加下面这些(以 S3 兼容服务为例):
`yaml
env:
DISCOURSE_USE_S3: true
DISCOURSE_S3_REGION: us-west-1
DISCOURSE_S3_ENDPOINT: https://s3.us-west-1.example-cloud.com
DISCOURSE_S3_ACCESS_KEY_ID: your-access-key
DISCOURSE_S3_SECRET_ACCESS_KEY: your-secret-key
DISCOURSE_S3_BUCKET: forum-files
DISCOURSE_S3_CDN_URL: https://files-cdn.example.com
DISCOURSE_S3_BACKUP_BUCKET: forum-files/backups
DISCOURSE_S3_INSTALL_CORS_RULE: false
`
参数含义与三条硬性规则:
| 参数 | 说明 |
|---|---|
| DISCOURSE_USE_S3 | 总开关。设为 true 后上传的图片不再落本地盘 |
| DISCOURSE_S3_ENDPOINT | S3 兼容服务必填,AWS S3 不用填(用官方端点)。用第三方时必须注意:填"整区端点"而不是"桶端点"——官方文档踩过的坑是,面板上给的桶端点形如 https://{bucket}.s3.{region}.xxx.cloud,直接填进去会连接失败,要去掉桶名子域,只填 https://s3.{region}.xxx.cloud |
| DISCOURSE_S3_BUCKET / DISCOURSE_S3_BACKUP_BUCKET | 附件桶与备份桶。备份桶支持带前缀(如 forum-files/backups),这样"备份与附件同桶"是被认可的用法;但同桶且同目录是不支持的,官方明确说"不会工作" |
| DISCOURSE_S3_CDN_URL | 指向对象存储桶的 CDN,用于 JS、图片和用户上传("可推送资产")。官方警告:不配 CDN、或直接把桶地址填进 CDN 字段,都是不受支持的做法,大概率出问题 |
| DISCOURSE_S3_INSTALL_CORS_RULE | 让 Discourse 自动往桶上装 CORS 规则;某些对象存储(Cloudflare R2 / GCS 等)不需要或不允许,官方示例里会关掉 |
| DISCOURSE_S3_HTTP_CONTINUE_TIMEOUT | 个别服务商(例如 Linode)需要显式设为 0 才不报错 |
另外,静态资产上传这件事发生在"资产编译之后",也就是发生在 rebuild 过程中。如果你希望一开始就用对象存储、而不是先落本地再迁移,就需要在 hooks 段里补一个钩子:
`yaml
hooks:
after_assets_precompile:
- exec:
cd: $home
cmd:
- sudo -E -u discourse bundle exec rake s3:upload_assets
- sudo -E -u discourse bundle exec rake s3:expire_missing_assets
`
> 📌 顺带解释一对容易搞混的参数:DISCOURSE_CDN_URL 指向你的论坛主机名、缓存 CSS 与主题资产("可拉取资产");DISCOURSE_S3_CDN_URL 指向你的对象存储桶、缓存 JS、图片与上传("可推送资产")。官方建议两者设为不同的值并且都配上。缓存加速这块可以复用本站 Cloudflare CDN 篇的配置思路。
六、日常运维:备份、恢复、升级与插件
6.1 自动备份:先确认它在跑,再把它搬走
Discourse 自带自动备份,但默认设置对活跃社区来说太松。进 管理后台 → 备份(Backup),需要关注这五个设置:
| 设置项 | 默认值 | 建议 |
|---|---|---|
| backup_frequency | 7(每周一次) | 改为 1(每天)。取值范围 0~30,0 表示关闭自动备份 |
| backup_time_of_day | 3:30(UTC) | 换算成你用户所在时区低峰期。国内用户为主的社区,UTC 3:30 ≈ 北京时间 11:30,正好是上班时间,建议后移 |
| backup_with_uploads | 开启 | 保持开启。关掉它只备份数据库,用户上传的图片全部丢失——这不是"省空间",而是"备份不完整" |
| maximum_backups | 5 | 保留份数上限,超出的旧备份自动删除。5 份日备份 = 只有 5 天回溯窗口,建议配合异地备份 |
| remove_older_backups | 空 | 按"天"删除更早的备份。留空表示不按时间删,只按份数删 |
自建实例的本地备份落在这个目录:
`bash
/var/discourse/shared/standalone/backups/default
`
但请立刻意识到一个问题:本地备份和论坛在同一块盘上,盘挂了两个一起没。 所以在后台把备份改成写进 S3 兼容对象存储(backup_location 设为 S3,同时配好 s3_backup_bucket / s3_access_key_id / s3_secret_access_key / s3_region)。开启后新备份不再落本地,本地只作为备份/恢复过程中的临时空间。这样你的备份和论坛就不在同一台机器上了,思路与本站 服务器备份与容灾 篇的"异地副本"原则一致。
> ⚠️ 两条红线:① 备份桶与附件桶不要用同一个"桶+目录"组合,官方明确说这样"不再被支持且不会工作";要同桶就必须给备份加前缀(my-bucket/backups),并确保该前缀下的文件是私有的。② 下载一份备份到本地存着。后台的备份列表里可以直接下载,给自己留一条"平台账号丢失也能恢复"的退路。
6.2 恢复时的现实预期
Discourse 的恢复不是在网页上点一下那么轻。备份文件(含 uploads 时)可能好几个 GB,恢复过程要重建数据库索引与附件,在 2 核机器上耗时以十分钟计,且恢复期间站点不可用。所以:
- 恢复演练要在低峰期做,并且先在测试机上验证备份文件可读——一份从没被恢复过的备份,等于没有备份; - 迁移服务器时,正确顺序是"新机器先装好一套同版本 Discourse → 上传备份 → 后台恢复 → 再切 DNS",而不是"新机器装最新版 → 恢复旧备份"(版本跨度大时容易出问题)。
6.3 升级:网页点一下,或命令 rebuild
Discourse 的升级是它最省心的地方,两条路都行:
网页升级(推荐,日常用):访问 https://forum.example.com/admin/upgrade,有新版时点 Upgrade。这是因为 standalone.yml 默认挂了 docker_manager 插件(hooks.after_code 里那行 git clone),它提供了这个网页升级能力。
命令行升级(网页挂了时用):
`bash
cd /var/discourse
./launcher rebuild app
`
记住两件事:
1. rebuild 会销毁旧容器再重建,有数分钟停机,并且会重新构建资产(这也是内存不足时最容易 OOM 的时刻)。请安排在低峰期;
2. 任何对 app.yml 的修改都必须靠 rebuild 生效——改完文件不 rebuild,等于没改。这也是很多"我明明配了 SMTP 却不发信"的根因。
不想让访客在重建期间看到连接错误,官方给出的方案是配一个离线页(Offline Page),让 Nginx 在 502 时返回一个"论坛正在升级"的静态页。
6.4 装插件:在 hooks 里加一行
官方推荐的插件安装方式就是在 app.yml 的 hooks.after_code 里追加 git clone,然后 rebuild:
`yaml
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone https://github.com/discourse/docker_manager.git
- git clone https://github.com/example/discourse-example-plugin.git
`
这么做的好处是插件列表跟着配置文件走:换机器、重建容器,插件自动装回来,不需要你记住当初装过什么。
6.5 收尾的安全加固两件事
官方在安装文档的"安装后"一节只强调了这么两条,但都是性价比极高的:
`bash
dpkg-reconfigure -plow unattended-upgrades
apt install fail2ban
`
前者让系统自动装安全补丁(Discourse 本身通过 /admin/upgrade 更新,但宿主机操作系统、Docker 引擎、Nginx 的安全更新要靠它);后者挡住针对 SSH 的暴力破解。再叠加本站在 服务器监控 篇里的做法,把论坛的可用性纳入监控(例如用 Uptime Kuma 盯着首页与 /admin/upgrade,一旦构建失败或站点 502 立刻告警),这套自建论坛的运维闭环就完整了。
七、常见问题 FAQ
Q1:我在服务器上装了宝塔面板,能不能在宝塔里一键部署 Discourse?
不能。官方文档的原文是:"唯一被官方支持的安装方式是基于 Docker 的。很遗憾,我们不支持任何其他安装方式,包括 cPanel、Plesk、Webmin 等面板。"宝塔属于同一类面板,没有官方支持的一键部署包。
但这不等于装不了:你完全可以让宝塔的 Nginx 占着 80/443,让 Discourse 容器躲在后面只监听 unix socket 或 127.0.0.1:8080,按本文第五节配好四个透传头即可。代价是宝塔的图形化反代界面只帮你填一部分,剩下三个头要手工补。
Q2:我只有一个公网 IP,没有域名,能装吗?
不能直接在裸 IP 上跑——DISCOURSE_HOSTNAME 字段只接受域名,官方 standalone.yml 模板里就写着"Discourse 不会在裸 IP 上工作"。但你不必为此买域名:官方提供免费子域名 yoursite.discourse.diy,在 id.discourse.com/my/subdomain 领取,安装时点 "Generate Code" 拿一个 6 位验证码(10 分钟内有效),脚本会自动帮你建好 DNS A 记录。适合测试和学习,正式运营仍建议用自己的域名并配好邮件子域。
Q3:1 核 1G 的便宜机器能跑吗?会不会很卡?
官方的最低要求确实是"1 核 1G + swap",但请注意那个 swap 是必需项而不是优化项。1G 内存在空闲时就已经接近占满:容器里同时跑着 Rails、Sidekiq、PostgreSQL、Redis 四个进程。1 核 1G 的机器"能打开、能用",但每次 rebuild 都有被 OOM Killer 杀掉的风险,后台任务(发摘要邮件、处理图片)也会明显拖慢前台。
结论:正式社区请直接上 2 核 2G 起,预算允许就 2 核 4G。 省下的那几美元,会以"重建失败后反复排错的两小时"的形式还回来。
Q4:用户说收不到激活邮件,我该从哪里查起?
按这个顺序查,绝大多数问题在前两步就能定位:
1. 发信域必须是子域名。 官方明确要求:在邮件服务商后台验证并使用的是子域名(如 discourse.example.com),只验证主域名会导致邮件配置不正确。这是最高频的根因。
2. 换一个 Gmail/Outlook 之外的邮箱试。 有些国内邮箱对来自新 IP、新域名的邮件直接静默丢弃,不报错也不退信。用 Gmail 验证能快速区分"发信链路坏了"还是"收件方拒收"。
3. 看是不是被当垃圾邮件。 检查发信域的 SPF、DKIM、DMARC 是否齐全。
4. 看容器日志:./launcher logs app 里搜 smtp;更详细的在容器内 /var/discourse/shared/standalone/log/rails/production.log。
5. 确认配置真的生效了:改完 app.yml 之后必须跑一次 ./launcher rebuild app。改文件不 rebuild,是"我明明配了却不发信"的头号原因。
Q5:./launcher rebuild app 跑到一半失败,或者慢到离谱,怎么办?
先看两个最常见的成因:
- 内存不足:构建资产阶段是内存峰值。先 free -h 确认 swap 开着;没开就按第四节第 0 步补一个 2GB swap,再重跑。
- 网络拉不动:构建过程要从 github.com 和 rubygems.org 拉代码与依赖。官方 README 里专门收录了一条"用淘宝镜像替换 rubygems.org 以解决国内网络错误"的文档,也有不少用户反馈"重试一次就好了"。如果服务器在代理后面,可以在 app.yml 的 env: 里加 HTTP_PROXY / http_proxy / HTTPS_PROXY / https_proxy 四个变量。
排错时最有用的三条命令:
`bash
cd /var/discourse
./launcher logs app
./launcher enter app
./launcher start-cmd app
`
想看 Rails 侧的真实报错,去这个文件(它比容器日志详细得多):
`bash
/var/discourse/shared/standalone/log/rails/production.log
`
Q6:能不能把 Discourse 装在 example.com/forum 这种子目录里?
不行,这也是它和 WordPress 最大的工程差异之一。Discourse 的官方安装流程、DISCOURSE_HOSTNAME 字段(它填的是一个主机名)、以及反代示例里的 location /,全部是按"根路径"设计的。正确做法是给它一个独立的(子)域名,比如 forum.example.com 或 community.example.com。
如果你非要在主站域名下提供社区入口,务实做法是反过来:在主站的 Nginx 里把 /forum 做一次 301 跳转到 https://forum.example.com。
Q7:邮件能不能不配?不配会怎样?
可以跳过,官方现在支持"安装时不配 SMTP":脚本会自动启用 Discourse ID 登录(用户通过 id.discourse.com 完成验证),也支持 Google / Facebook / Apple / GitHub 社交登录,并且用户依然能收到浏览器 Web Push 通知。
但要注意代价:邮件摘要、邮寄列表模式、以及"直接回邮件发帖"这三项都用不了,而它们恰恰是 Discourse 相比群聊的核心优势。官方的态度也很明确——"要让 Discourse 正常工作,邮件必须配好"。并且发信域一律用子域名验证,理由见 Q4。
Q8:我的 80/443 已经被别的站占了,还能装吗?
能。这正是第五节解决的场景。要点三条:① 停掉容器后再改 app.yml;② 加上 templates/web.socketed.template.yml 模板、注释掉两个 SSL 模板、整段取消 expose:;③ ./launcher rebuild app 之后,外层 Nginx 反代到 /var/discourse/shared/standalone/nginx.http.sock。
⚠️ 需要提醒的是:只要 80 或 443 上有别的服务在跑,官方的 ./discourse-setup 向导就用不了,你必须手工编辑 app.yml。官方文档对这个场景的标注是"为进阶管理员准备",所以建议先在一台干净机器上把 Discourse 跑通一次,再来做共存架构。
Q9:升级要停机多久?会不会影响用户?
./launcher rebuild app 是"销毁旧容器 → 引导新容器 → 启动",有数分钟停机,而且会重新编译前端资产(慢机器上加内存压力)。日常升级更推荐走网页:/admin/upgrade 点一下即可,这是 docker_manager 插件提供的能力。
若确实不能接受停机,官方的答案是拆成多容器架构:先用旁边的 web_only 容器把新版本引导好,构建完成再切流量。代价是配置复杂度上升,而且要自己加防火墙保护 PostgreSQL/Redis 端口。想更体面一点,还可以配一个离线页,让 Nginx 在重建期间返回"论坛正在升级"。
Q10:附件一定要放对象存储吗?本地盘能不能撑?
够用就不折腾,但请把"附件外置"当成一个计划项而不是应急项。判断标准很简单:如果你的社区开始有人上传截图和图片,磁盘增长会在几周内超过你的预期。而 Discourse 的数据目录是通过宿主机目录挂载进容器的,扩容云盘后还要停机重建容器,比一开始就规划 60~80GB 的盘麻烦得多。
真要外置时按 5.6 节配 DISCOURSE_USE_S3 那一组变量即可,官方支持 AWS S3、Cloudflare R2、DO Spaces、Linode、Scaleway、GCS 等,也可以对接自建的 MinIO。两条硬规则别忘:对象存储必须配 CDN(不配或直接把桶地址填进 CDN 字段,官方明确说"不受支持、大概率出问题");S3 兼容服务填端点时要去掉桶名子域,只填整区端点。
Q11:重建(rebuild)时从 github / rubygems 拉不下来怎么办?
官方 README 专门收录了一条"用淘宝镜像替换 rubygems.org,解决国内网络错误"的文档,说明这个问题是普遍存在的。三段式处理:① 重试一次,官方说临时网络中断重试就好;② 走代理的话在 app.yml 的 env: 里补 HTTP_PROXY / http_proxy / HTTPS_PROXY / https_proxy;③ 换镜像源。顺带一提,launcher 还支持 --skip-prereqs 跳过前置检查——但那只在你已经确认检查项没问题时用,别拿它当默认参数。
Q12:怎么把整个论坛搬到新服务器?
把 Discourse 的迁移拆成"配置 + 数据"两半看,就会很清楚:
- 配置在 /var/discourse/containers/app.yml,它是一个文本文件,直接复制走;
- 数据在 /var/discourse/shared/standalone(数据库、上传附件、日志都在这里,容器本身是无状态的,重建不丢数据)。
官方推荐的迁移方式不是搬目录,而是用备份文件:旧机器后台下载一份含 uploads 的备份 → 新机器先装好同版本的 Discourse → 后台上传备份并恢复 → 确认无误后再切 DNS。这样避免了"新装最新版、恢复旧数据"带来的版本跨度问题。恢复前务必先在测试机上验证这份备份能被正常恢复——一份从没演练过恢复的备份,等于没有备份。
Q13:MaxMind 的账号是必须配的吗?
不是必需品,但建议配。它提供基于 IP 的国家/地区识别(付费档还有城市级精度),官方给的是免费注册的 GeoLite2 凭据,填两个变量即可:
`yaml
env:
DISCOURSE_MAXMIND_ACCOUNT_ID: "your-account-id"
DISCOURSE_MAXMIND_LICENSE_KEY: "your-license-key"
`
配好之后,后台就能看到按地区维度的用户与流量统计,社区运营、内容本地化、以及判断"买哪个地域的服务器"都有依据。前提:MaxMind 的下载端点在部分网络环境下访问不稳定,导致容器构建时拉不到库——如果 rebuild 因为 MaxMind 失败,先把这两行去掉,让论坛先跑起来,之后再补。
八、写在最后:自建论坛真正的门槛在哪
把这篇教程压缩成一句话:Discourse 的安装本身不难(一条官方命令,5~10 分钟),难的是它周围那三件事——邮件、反代、备份。
- 邮件决定用户能不能注册、能不能被通知,而你只要记住"发信域用子域名"这一条就能避开 90% 的坑; - 反代决定你能不能在已有服务器上叠加论坛,关键就是那四个必须透传的头和 unix socket 路径; - 备份决定你的社区是不是"一次误删就归零",关键就是把它搬到另一台机器/另一个桶上,并且真的演练过一次恢复。
这三件事都是通用能力。做完一遍之后你会发现,同样的思路可以直接套到本站其他自建服务上——Nextcloud 的邮件配置、MinIO 的对象存储、服务器备份与容灾 的异地副本策略,本质上是同一套东西。这也是自建这条路的复利所在:你练的不是某一个软件,而是"把任何软件安全地跑起来"的手感。
延伸阅读:
- 海外云服务器搭建 WordPress 独立博客完整教程——论坛的内容发布层搭档,博客+论坛是独立站的经典组合 - Docker + Portainer 容器管理平台部署教程——理解本文容器命令的基础概念篇 - 海外云服务器搭建邮件服务器完整教程——想自建收件/业务邮箱时的对照方案 - MinIO 私有对象存储部署教程——5.6 节附件外置与 S3 备份的自建落地 - 服务器备份与容灾恢复实战——把 6.1 节的本地备份升级成异地副本 - 海外云服务器搭建 Nginx + Let's Encrypt SSL 完整教程——第五节外置反代与证书管理的细节参考 - Uptime Kuma 可用性监控与公网状态页部署教程——给论坛首页加一个 7×24 的哨兵
> 本文由 5.chengzicloud.cloud 提供,点击访问首页了解更多海外云服务器部署方案和专属优惠。