rogu
2025 年7 月 29 日 13:28
1
您好,我正在尝试使用标准的./discourse-setup流程安装Discourse,但在引导过程中遇到了一个错误:
FAILED
--------------------
Errno::ENOENT: No such file or directory @ rb_sysopen - /etc/runit/1.d/letsencrypt
Location of failure: /usr/local/lib/ruby/gems/3.3.0/gems/pups-1.3.0/lib/pups/replace_command.rb:11:in `read'
replace failed with the params {"filename"=>"/etc/runit/1.d/letsencrypt", "from"=>"/--keylength/", "to"=>"-d forum.mysite.org --keylength"}
bootstrap failed with exit code 1
** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one.
看起来错误是由一个插件触发的,该插件试图在引导过程中在容器内的文件/etc/runit/1.d/letsencrypt上运行一个replace命令,而该文件不存在。插件中的相关行如下所示:
- replace:
filename: "/etc/runit/1.d/letsencrypt"
from: "/--keylength/"
to: "-d forum.mysite.org --keylength"
关于如何修复此问题或正确重新生成缺失文件的任何建议?
提前感谢。
pfaffman
(Jay Pfaffman)
2025 年7 月 29 日 20:48
2
插件?那是你 app.yml 中的 cups 代码,对吧?你想添加另一个证书吗?就像在 Set up Let’s Encrypt with multiple domains / redirects 中那样?你能包含实际代码和两个证书吗?
正如你指出的,那个 runit 不再存在了,现在那个魔法在 /usr/local/bin/letsencrypt(在容器内)
ssl_certificate \/shared\/ssl\/${DISCOURSE_HOSTNAME}_ecc.cer;/" \
/etc/nginx/conf.d/outlets/server/20-https.conf
sed -Ei "s/ssl_certificate_key .+/ssl_certificate_key \/shared\/ssl\/${DISCOURSE_HOSTNAME}.key; \
ssl_certificate_key \/shared\/ssl\/${DISCOURSE_HOSTNAME}_ecc.key;/" \
/etc/nginx/conf.d/outlets/server/20-https.conf
exec /usr/local/bin/letsencrypt
- file:
path: /usr/local/bin/letsencrypt
chmod: "+x"
contents: |
#!/bin/bash
LETSENCRYPT_DIR="/shared/letsencrypt"
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf
extra_domains() {
if [ -n "$DISCOURSE_HOSTNAME_ALIASES" ]; then
domains=$(echo $DISCOURSE_HOSTNAME_ALIASES | sed "s/,/ -d /g")
echo "-d $domains"
fi
我认为 ,如果你的站点是 www.mysite.org,并且你也想为 forum.mysite.org 获取证书,你可能想要类似这样的东西:
- replace:
filename: "/usr/local/bin/letsencrypt"
from: "/-d www.mysite.org/"
to: "-d www.mysite.org -d forum.mysite.org "
global: true
我的做法(可能对你没有帮助)是进入容器,运行 apt update;apt install -y vim,然后编辑 /usr/local/bin/letsencrypt,使其能够请求你想要的证书。
我在上面链接的 Let’s Encrypt 主题中添加了代码,应该能让你输入你的域名并获取可以复制/粘贴到你的 app.yml 中的代码。
1 个赞
在尝试更新时,我遇到了看起来与此相同的错误消息。
我没有尝试更改任何与证书有关的内容。
我的 app.yml 文件目前显示如下:
after_ssl:
- replace:
filename: "/etc/runit/1.d/letsencrypt"
from: /--keylength/
to: "-d www.nzarchitecture.net.nz --keylength"
我遵循了您的建议
apt update;apt install -y vim
但结果是“vim 已经是最新版本(2:9.1.0016-1ubuntu7.8)”。
至于第二步建议,我不知道我想要什么证书,因为我无意更改任何内容。
好的,经过几个小时的摸索,我终于恢复了正常运行。
我找到一个旧的 app.yml 文件并替换了它,只是删除了后来被 Discourse 合并的插件的旧引用。
这个较旧的 app.yml 文件不包含下面我发现的较新文件中的代码。
after_ssl:
- replace:
filename: "/etc/runit/1.d/letsencrypt"
from: /--keylength/
to: "-d www.nzarchitecture.net.nz --keylength"
我不记得我自己添加了那段代码,尽管我设置了我的网站使用 letsencrypt 来获取免费安全证书,但 Set up HTTPS support with Let's Encrypt 的说明似乎根本不需要那些行,所以我不知道它们是做什么用的。
是否还有其他东西可能将那些行写入 app.yml?例如,它们是否可能在 beta 更新期间添加?
至少现在,那些行被删除了,我的网站又能正常运行并保持最新了。
当我的当前 ssl 证书过期时,我猜我可能会发现那些额外的行是做什么用的。
pfaffman
(Jay Pfaffman)
2025 年7 月 31 日 20:07
5
嗯,是的,你正在这样做,只是你不记得了。
如果你在 yml 文件中有 after_ssl 节,那么你曾经设置过,如果有人在你的域名前面加上 www,它就会转到 www 地址并重定向到非 www 地址,而不会出现证书错误。
Paul_King:
我遵循了你的建议
我的建议是在容器内安装 vim 并尝试在那里修改文件,但我认为我添加到 app.yml 的代码应该有效。
对了。现在如果你访问 https://www.nzarchitecture.net.nz/ 你会看到一个证书错误。你可以使用 https://forcewww.com/
你可以访问上面的链接并获取我认为应该有效的新代码,但我还没有测试过,因为我还没有找到需要它的正在升级的网站。
如果我想让 www.nzarchitecture.net.nz 和 nzarchitecture.net.nz 由同一个 letsencrypt 证书覆盖,我的理解是你的代码应该类似下面这样?
after_ssl:
- replace:
filename: /usr/local/bin/letsencrypt
from: /-d nzarchitecture.net.nz /
to: "-d nzarchitecture.net.nz -d www.nzarchitecture.net.nz "
global: true
我尝试将此节(术语用得很好,谢谢!)添加到 app.yml 的末尾,替换了原始的 ‘after_ssl:’ 节,现在可以无错误地重建 Discourse——但这似乎对我没有帮助;如果我在主/已认证域 nzarchitecture.net.nz 前面加上 ‘www’ 前缀,我的浏览器仍然会显示“net::ERR_CERT_COMMON_NAME_INVALID”响应。
如果这对你有帮助,我附上我的完整 app.yml(密码和电子邮件地址已隐藏)
## 这是全合一的独立 Discourse Docker 容器模板
##
## 修改此文件后,您必须重建
## /var/discourse/launcher rebuild app
##
## 编辑时请务必*极其小心*!
## YAML 文件对空格或对齐错误非常非常敏感!
## 如有需要,请访问 http://www.yamllint.com/ 来验证此文件
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
- "templates/web.ratelimited.template.yml"
## 如果您想添加 Lets Encrypt (https),请取消注释这两行
- "templates/web.ssl.template.yml"
- "templates/web.letsencrypt.ssl.template.yml"
- "templates/import/mysql-dep.template.yml"
## 此容器应暴露哪些 TCP/IP 端口?
## 如果您想让 Discourse 与 Apache 或 nginx 等其他 Web 服务器共享端口,
## 请参阅 https://meta.discourse.org/t/17247 获取详细信息
expose:
- "80:80" # http
- "443:443" # https
params:
db_default_text_search_config: "pg_catalog.english"
## 将 db_shared_buffers 设置为总内存的最多 25%。
## 将由 bootstrap 根据检测到的 RAM 自动设置,或者您可以覆盖它
db_shared_buffers: "128MB"
## 可以提高排序性能,但会增加每个连接的内存使用量
#db_work_mem: "40MB"
## 此容器应使用哪个 Git 版本? (默认:tests-passed)
#version: tests-passed
env:
LANG: en_US.UTF-8
# DISCOURSE_DEFAULT_LOCALE: en
## 此容器支持多少并发 Web 请求?取决于内存和 CPU 核心。
## 将由 bootstrap 根据检测到的 CPU 自动设置,或者您可以覆盖它
UNICORN_WORKERS: 2
## TODO: 此 Discourse 实例将响应的域名
## 必需。Discourse 不能使用纯 IP 地址。
DISCOURSE_HOSTNAME: nzarchitecture.net.nz
## 如果您希望容器以与上面指定的相同主机名 (-h 选项) 启动,请取消注释
## (默认值为“$hostname-$config”)
#DOCKER_USE_HOSTNAME: true
## TODO: 将被设为管理员和开发者的逗号分隔的电子邮件列表
## 首次注册时,例如“user1@example.com,user2@example.com”
DISCOURSE_DEVELOPER_EMAILS: '****************'
## TODO: 用于验证新帐户和发送通知的 SMTP 邮件服务器
# 需要 SMTP 地址、用户名和密码
# 注意:SMTP 密码中的字符 '#' 可能会导致问题!
DISCOURSE_SMTP_ADDRESS: smtp.mailgun.org
DISCOURSE_SMTP_PORT: 587
DISCOURSE_SMTP_USER_NAME: ****************
DISCOURSE_SMTP_PASSWORD: "****************"
#DISCOURSE_SMTP_ENABLE_START_TLS: true # (可选,默认值为 true)
## 如果您添加了 Lets Encrypt 模板,请取消注释下方以获取免费 SSL 证书
LETSENCRYPT_ACCOUNT_EMAIL: ******************
## 此 Discourse 实例的 http 或 https CDN 地址(配置为拉取)
## 请参阅 https://meta.discourse.org/t/14857 获取详细信息
#DISCOURSE_CDN_URL: https://discourse-cdn.example.com
## Docker 容器是无状态的;所有数据都存储在 /shared 中
volumes:
- volume:
host: /var/discourse/shared/standalone
guest: /shared
- volume:
host: /var/discourse/shared/standalone/log/var-log
guest: /var/log
## 插件在此处
## 请参阅 https://meta.discourse.org/t/19157 获取详细信息
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone https://github.com/discourse/docker_manager.git
- git clone https://github.com/discourse/discourse-bbcode.git
## 任何自定义命令,在构建后运行
run:
- exec: echo "开始执行自定义命令"
## 如果您想设置首次注册的“发件人”电子邮件地址,请取消注释并更改:
## 获取第一个注册电子邮件后,重新注释该行。它只需要运行一次。
#- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
- exec: echo "自定义命令结束"
after_ssl:
- replace:
filename: /usr/local/bin/letsencrypt
from: /-d nzarchitecture.net.nz /
to: "-d nzarchitecture.net.nz -d www.nzarchitecture.net.nz "
global: true
/usr/local/bin/ 中没有文件是问题的一部分吗?
如果需要,我应该在哪里找到正确的 letsencrypt 文件并将其放在那里?
另外,如果“letsencrypt”是该路径末尾的文件名,那么文件名末尾没有文件扩展名是否正常?
pfaffman
(Jay Pfaffman)
2025 年8 月 1 日 10:46
7
我的想法是删除 after_ssl 行并将其他行取消缩进。
如果您添加我的 ssh 密钥,使用
ssh-import-id-gh pfaffman
并给我发邮件,我会看看我能做什么。
pfaffman
(Jay Pfaffman)
2025 年8 月 1 日 16:21
9
这是一个更新。
将此添加到 app.yml 底部的 run 部分将解决为 DISCOURSE_HOSTNAME 和 www.DISCOURSE_HOSTNAME 请求证书的问题,方法是获取 /usr/local/bin/letsencrypt。
- exec: sed -i "s|-d \\${DISCOURSE_HOSTNAME}|-d \\${DISCOURSE_HOSTNAME} -d www.\\${DISCOURSE_HOSTNAME}|g" /usr/local/bin/letsencrypt
这(不知何故?)曾经足够了,但现在当请求来自 http://www.HOSTNAME/.well-known … 时,它会被重定向到非 www 站点,而不是发送它应该发送的挑战。我尝试过这样做:
server {
listen 80;
listen [::]:80;
server_name nzarchitecture.net.nz www.nzarchitecture.net.nz;
# Serve ACME challenge (Let's Encrypt)
location ^~ /.well-known/acme-challenge/ {
root /var/www/discourse/public; # Make sure this matches your Let's Encrypt webroot
allow all;
}
# Redirect everything else to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
但没有完全弄清楚。
如果团队中的任何人听到,最好有一个 letsencrypt 钩子,以便可以在 after_letsencrypt 中调用它。在 after_ssl 中进行这些更改之前是有效的,但现在似乎如果我们这样做,它会在 ssl 之后运行,但在 letsencrypt 之前运行。
3 个赞
我的 DISCOURSE_HOSTNAME 是 www.textkit.com。我正在做与 nzarchitecture.net.nz 相反的事情,在我的证书中添加一个没有 www 的主机名。这对我来说是有效的:
- exec: sed -i "s|-d \\${DISCOURSE_HOSTNAME}|-d www.textkit.com -d textkit.com|g" /usr/local/bin/letsencrypt
我无法解释为什么 @pfaffman 和 nzarchitecture.net.nz 会在他的版本中遇到问题,尽管我的主机名顺序可能与之有关。
我也遇到了这个问题。
我删除了(通过注释掉):
after_ssl:
# - replace:
# filename: "/etc/runit/1.d/letsencrypt"
# from: /--keylength/
# to: "-d example.com --keylength"
# - replace:
# filename: "/etc/nginx/conf.d/discourse.conf"
# from: /return 301 https.+/
# to: |
# return 301 https://$host$request_uri;
并按照 @pfaffman 的建议在底部的 run 部分添加了:
- exec: sed -i "s|-d \\${DISCOURSE_HOSTNAME}|-d \\${DISCOURSE_HOSTNAME} -d www.\\${DISCOURSE_HOSTNAME}|g" /usr/local/bin/letsencrypt
这对我来说似乎足够了:
网站重建,并且似乎拥有有效的证书
从根域名到 www 的重定向正在工作
感谢 @pfaffman !
4 个赞
pfaffman
(Jay Pfaffman)
2025 年8 月 7 日 18:35
13
merefield:
从根域名到 www 的重定向正在生效
哦!太棒了。也许他们所做的允许证书续订正常工作的更改也解决了我的问题。
如果我在 PR 被接受之前遇到任何其他需要此操作的网站,我会记住这一点。
1 个赞
这里有几个活动部件。它在我重建时对我有用,如果情况发生变化,我会在这里汇报!
1 个赞
允许证书续订的 PR 尚未合并——那部分仍在进行中。
合并后,应该可以大大简化 app.yml。
2 个赞
nathank
(Nathan Kershaw)
2025 年8 月 8 日 00:10
16
奇怪的是,那段代码在一个网站上却能正常运行,而在我的另一个网站上,只有旧代码(也只有旧代码)才能正常运行。
嗯,希望很快这一切都会变得无关紧要了!
1 个赞
pfaffman
(Jay Pfaffman)
2025 年8 月 8 日 00:39
17
这很奇怪。discourse_docker 是否被固定到了一个旧版本?
nathank
(Nathan Kershaw)
2025 年8 月 8 日 00:54
18
不,不是。实例之间的差异不大(主题/插件/配置相似),只是其中一个实例比另一个旧得多。
嗯,希望这只是学术上的。
1 个赞
Paul_King
(Paul King)
2025 年11 月 20 日 01:11
19
这已经实现了吗?
我的 Let’s Encrypt 证书昨天过期了,没有自动续订。不确定这是否与我根据上面这个帖子对 app.yml 所做的更改有关,或者与 Discourse 后续的更新有关。
在人工智能的帮助下(是的,我知道!),在按照人工智能的指引深入研究了涉及使用 ngix 和 certbot(公平地说,最终奏效了)的许多弯路之后,我设法让它续订了,然后我撤销了那些更改,并返回到由 Discourse 管理的默认方法。在此过程中,我不得不重建了两次,所以不确定这是否是触发续订的原因。
抱歉给您带来不便——我很好奇,您上一次重建是在什么时候?
我们刚刚检查并再次测试了续订流程,但无法重现问题——我们的测试续订正常,所以要么是我们的这边没有发生什么,要么是 Let’s Encrypt 的暂存环境(staging)与生产环境(prod)的续订工作方式有所不同。
我也可以确认,如果其他方法都失败了,重建您的站点确实会强制进行续订过程。
我们在底层使用 acme.sh (在容器内的 /opt/acme.sh 中)——如果您愿意,可以进入正在运行的 Docker 容器,并通过那里运行脚本进行检查/续订。
1 个赞