- 包管理器
- 操作系统
【免费下载链接】nixpkgs
Nix Packages collection & NixOS
本文以 NixOS 官方模块文档 nixos/modules/services/web-apps/lemmy.md 为骨架,结合 模块实现源码 与 集成测试,系统讲解如何在 NixOS 上通过声明式配置部署 Lemmy——一个用 Rust 编写的 Reddit 联邦替代方案。读完本文,你将掌握services.lemmy的全部选项、最小可用配置、数据库与密钥文件的接入方式,以及 Caddy / nginx 反代路由与 systemd 服务治理的底层原理。
模块概览:一个配置同时拉起三大服务
services.lemmy模块由社区维护(meta.maintainers 标注了happysalada、lucasew,所属团队为ngi),背后由两个独立的软件包支撑,二者均在 pkgs/top-level/all-packages.nix 中定义:
- lemmy-server:后端 API 服务,默认监听端口8536;
- lemmy-ui:基于 Node.js 的前端界面,默认监听端口1234。
启用模块后,除了这两个进程,模块还会自动为你编排好配套的周边设施(详见下文各节):本地 PostgreSQL 数据库、图片托管服务 pict-rs、以及可选的反向代理(Caddy 或 nginx)。也就是说,一次services.lemmy.enable = true即可获得一个完整可用的联邦论坛实例。
快速上手(Quickstart)
根据 模块文档 Quickstart 章节,启动一个最小可用的 Lemmy 实例,只需要如下配置:
{ services.lemmy = { enable = true; settings.hostname = "lemmy.union.rocks"; database.createLocally = true; caddy.enable = true; }; }启用这段配置后的实际效果:
- 后端在8536端口启动,前端在1234端口启动;
- 通过 Caddy 反向代理,将你指定的域名(
settings.hostname)暴露到公网; - PostgreSQL 会在同一台机器上自动初始化并创建数据库,无需任何手工建库步骤。
从 模块源码 可以看到,这个"最小配置"背后其实串联了一连串的自动行为:
- 自动启用
services.pict-rs.enable = true(第 207 行),为 Lemmy 提供图片上传托管能力,settings.pictrs.url会被默认指向 pict-rs 的监听地址; - 当
database.createLocally = true时自动启用services.postgresql,并通过ensureDatabases/ensureUsers创建名为lemmy的数据库与同名用户(第 196-205 行); - 当
caddy.enable = true时自动启用 Caddy 并生成完整的虚拟主机路由(第 209-245 行)。
首次访问时,系统会引导你定义管理员账号(详见"使用说明"一节)。
使用说明(Usage)
依据 模块文档 Usage 章节:第一次连接实例时,界面会要求你定义一个管理员用户。这一交互在 集成测试 中也有体现——测试通过settings.setup预设了admin_username、admin_email、site_name,并通过adminPasswordFile注入管理员密码,从而让首次启动即可完成管理员初始化,无需人工交互。
配置选项全解
services.lemmy的全部选项定义在 lemmy.nix 的 options 段(第 28-121 行),下面按分组逐一说明。
基础选项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
services.lemmy.enable | bool | false | 是否启用 Lemmy 服务 |
services.lemmy.server.package | package | pkgs.lemmy-server | 后端服务器包,可替换为自定义构建版本 |
services.lemmy.ui.package | package | pkgs.lemmy-ui | 前端界面包 |
services.lemmy.ui.port | port | 1234 | lemmy-ui 监听端口 |
反向代理选项
services.lemmy.caddy.enable:是否启用 Caddy 反向代理暴露 Lemmy;services.lemmy.nginx.enable:是否启用 nginx 反向代理暴露 Lemmy。
两者互斥使用其一即可,路由规则详见"反向代理"一节。
数据库选项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
services.lemmy.database.createLocally | bool | false | 是否在本机创建 PostgreSQL 数据库 |
services.lemmy.database.uri | string / null | null | 数据库连接 URI,优先级高于配置文件中的 database 段。示例:postgres:///lemmy?host=/run/postgresql&user=lemmy |
services.lemmy.database.uriFile | path / null | null | 存放数据库连接 URI 的文件路径(配合密钥注入使用,见"密钥文件注入"一节) |
需要特别注意的是,模块对database.uri与database.uriFile的使用有一组内置断言(lemmy.nix 第 303-305 行):指定了uriFile时不允许同时指定uri,也不允许createLocally为 true,否则构建/部署会直接报错。
settings:自由形式的 Lemmy 配置
services.lemmy.settings是一个freeformType = settingsFormat.type(JSON 格式)的 submodule,也就是说它直接透传给 Lemmy 原生的config.hjson配置文件,可以容纳任意官方支持的配置键。模块为最常用的几个键提供了类型化定义:
| 配置键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
settings.hostname | string | 必填 | 实例的公网域名,例如lemmy.ml |
settings.port | port | 8536 | Lemmy 后端监听端口 |
settings.captcha.enabled | bool | true | 是否启用验证码 |
settings.captcha.difficulty | enum | "medium" | 验证码难度,可选easy/medium/hard |
模块自动合并的默认配置
即便你不写,模块也会在启用后自动为settings注入一批与安全、联邦、限流强相关的默认值(lemmy.nix 第 157-193 行):
bind = "127.0.0.1":后端只监听本机回环地址,强制要求通过反向代理对外暴露,避免端口直出公网;tls_enabled = true:默认开启 TLS 语义;pictrs.url:自动指向本机 pict-rs 服务地址;actor_name_max_length = 20:用户名长度上限;- 一组限流参数:
rate_limit.message = 180、rate_limit.message_per_second = 60;rate_limit.post = 6、rate_limit.post_per_second = 600;rate_limit.register = 3、rate_limit.register_per_second = 3600;rate_limit.image = 6、rate_limit.image_per_second = 3600;
database段默认值:user = "lemmy"、host = "/run/postgresql"、port = 5432、database = "lemmy"、pool_size = 5(即默认走 Unix socket 连接本地 PostgreSQL,与文档中"本地数据库 + Unix socket 已测试通过"的描述一致)。
这些默认值均以lib.mkDefault注入,你可以在settings中显式覆盖。
数据库接入方式
模块支持两种数据库接入形态:
1. 本地自动建库(推荐起步方式)
services.lemmy = { enable = true; settings.hostname = "example.com"; database.createLocally = true; };此时模块自动启用services.postgresql,创建lemmy数据库并授权给lemmy用户(ensureDBOwnership = true),后端通过postgres:///lemmy?host=/run/postgresql&user=lemmy连接(见 systemd 服务的LEMMY_DATABASE_URL环境变量逻辑,lemmy.nix 第 318-322 行)。
2. 使用外部数据库 URI
services.lemmy = { enable = true; settings.hostname = "example.com"; database.uri = "postgres://user:pass@db.example.com:5432/lemmy"; # 或者用文件方式(可配合 systemd 密钥注入): # database.uriFile = "/run/secrets/lemmy-db-uri"; };断言约束(lemmy.nix 第 289-306 行)同时还会检查:当createLocally = true时,settings.database.host必须是localhost或/run/postgresql,否则部署时报错"if you want to create the database locally, you need to use a local database"。
密钥文件注入:把敏感配置留在文件里
Lemmy 原生配置中有一类敏感值(数据库 URI、验证码图片服务 API key、SMTP 密码、管理员初始密码)。模块提供了四个文件型选项,避免把明文写进 Nix 配置:
pictrsApiKeyFile→ 对应settings.pictrs.api_keysmtpPasswordFile→ 对应settings.email.smtp_passwordadminPasswordFile→ 对应settings.setup.admin_passworddatabase.uriFile→ 对应settings.database.uri
其实现机制(lemmy.nix 第 124-193 行)值得单独说明:
- 模块将四个选项统一归入
secretOptions,通过lib.filterAttrs只保留已设置的项; - 已设置的项会以
{ _secret = optionName; }形式递归合并进settings,作为 systemd 的占位标记; - systemd 服务通过
LoadCredential = [ "pictrsApiKeyFile:/path/to/file" ... ]把文件挂载进$CREDENTIALS_DIRECTORY; - 服务
preStart中调用utils.genJqSecretsReplacementSnippet,将占位标记替换为真实密钥,并写入/run/lemmy/config.hjson,同时通过umask u=rw,g=,o=保证合并后的配置只有属主可读写。
集成测试 对这套机制有专门的验证子测试:断言/run/lemmy/config.hjson的权限为-rw-------,并确认目录权限位中没有 group/other 的可写位,防止已合并的配置被替换。
反向代理:Caddy 与 nginx 双实现
Lemmy 前端与后端运行在不同端口,且 ActivityPub 联邦协议要求按请求头区分流量,因此反向代理的路由规则是部署成败的关键。模块为两种代理分别生成了完整配置。
Caddy(caddy.enable = true)
lemmy.nix 第 209-245 行 生成的路由逻辑为:
handle_path /static/*与handle_path /static/<ui-version>/*:由 lemmy-ui 包的dist目录直接提供静态资源;@for_backend匹配path /api/* /pictrs/* /feeds/* /nodeinfo/*:全部转发到后端127.0.0.1:<settings.port>;@post匹配所有 POST 请求:转发到后端;@jsonld匹配Accept: application/activity+json或Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"的请求:转发到后端(这是 ActivityPub 联邦与 Mastodon 等实例互操作的关键);- 其余请求默认转发到前端
127.0.0.1:<ui.port>。
nginx(nginx.enable = true)
lemmy.nix 第 247-287 行 生成的路由逻辑为:
- 正则位置
~ ^/(api|pictrs|feeds|nodeinfo|.well-known):转发到后端,启用proxyWebsockets与recommendedProxySettings; - 根位置
/:通过变量$proxpass动态分流——请求头Accept为 ActivityPub JSON 类型或请求方法为 POST 时转发到后端,否则转发到前端; - 对 URL 做去尾部斜杠的重写(
rewrite ^(.+)/+$ $1 permanent); - 显式设置
Host头:源码注释明确指出,转发Host头是校验入站 ActivityPub HTTP 签名所必需的,其余X-Real-IP、X-Forwarded-For头用于改善日志数据。
底层运行模型:两个 systemd 服务
模块最终生成两个 systemd 服务(lemmy.nix 第 308-386 行):
lemmy.service(后端)
ExecStart = "${cfg.server.package}/bin/lemmy_server";- 环境变量
LEMMY_CONFIG_LOCATION指向生成的配置(无密钥时为普通生成文件,有密钥时为/run/lemmy/config.hjson);LEMMY_DATABASE_URL优先取database.uri,否则取本地 Unix socket URI; - 安全加固:
DynamicUser = true、PrivateTmp = true、MemoryDenyWriteExecute = true、NoNewPrivileges = true; - 依赖顺序:
after/requires覆盖pict-rs.service,使用本地数据库时还依赖postgresql.target。
lemmy-ui.service(前端)
- 由
pkgs.nodejs-slim运行dist/js/server.js,工作目录为 ui 包目录; - 环境变量:
LEMMY_UI_HOST = 127.0.0.1:<ui.port>LEMMY_UI_LEMMY_INTERNAL_HOST = 127.0.0.1:<settings.port>LEMMY_UI_LEMMY_EXTERNAL_HOST = <settings.hostname>LEMMY_UI_HTTPS = "false"、NODE_ENV = "production"
- 依赖后端:
requires = [ "lemmy.service" ]。
两个服务的documentation均指向 Lemmy 官方管理文档(join-lemmy.org/docs/en/admins/from_scratch.html)。
端到端验证:集成测试教我们怎么验收
nixos/tests/lemmy.nix 是一份可复现的验收脚本,覆盖了从启动到联邦路由的完整链路,可作为你部署后的自检清单:
- 配置安全性:等待
lemmy.service启动后,检查/run/lemmy/config.hjson权限为-rw-------,目录无可写权限泄露; - 后端可用:等待 5678 端口开放,
curl --fail localhost:5678/api/v3/site成功(预留 50 秒等待数据库迁移完成); - 前端可用:等待
lemmy-ui.service与 1234 端口,curl --fail localhost:1234成功; - Caddy 全链路:经 Caddy 访问域名,页面响应体包含字符串
Lemmy; - 外部可达:从独立 client 节点
curl -v --fail <hostname>成功; - 路由正确性(停止 lemmy-ui 后验证):
- 后端无法处理的路径返回 502,说明请求确实被路由到了后端;
/static/js/client.js返回 200(静态资源);/api/v3/site、/feeds/all.xml、/nodeinfo/2.0.json返回 200(后端 API 与联邦端点);/pictrs/返回 404(命中 pict-rs 后端路由,尚未上传图片);- 带
-X POST或 ActivityPubAccept头的任意路径返回 404,证明 POST 与 JSON-LD 请求确实按规则被分流到后端。
已知限制与兼容性注意事项
依据 模块文档 Missing 章节 与源码中的断言/迁移逻辑:
- 该模块目前仅在"本地数据库 + Unix socket 连接"形态下经过充分测试;改用其他数据库连接方式(远程 PostgreSQL、TCP 连接等)很可能需要额外修改,请谨慎验证后再上线;
services.lemmy.jwtSecretPath选项已移除:自 Lemmy v0.13.0 起 JWT 密钥由服务自动生成,无需(也无法)再手动指定(见 lemmy.nix 第 20-26 行的 mkRemovedOptionModule);settings.federation配置已失效:自 0.17.0 起该键被移除,若仍在配置中声明会触发部署断言报错(lemmy.nix 第 296-301 行);- 配置了任一密钥文件选项后,合并后的配置文件位于运行时目录
/run/lemmy/config.hjson,由 systemdLoadCredential机制注入,请勿在 Nix 配置中明文放置数据库密码、SMTP 密码或管理员密码。
延伸阅读
- 模块文档原文:本文的骨架来源;
- 模块实现源码:全部选项、默认值与 systemd 编排细节;
- 集成测试:完整的端到端验收脚本;
- pict-rs 模块:被自动启用的图片托管服务,可单独调整其存储路径等选项;
- lemmy-server / lemmy-ui 包定义:通过
server.package、ui.package可替换为自定义版本。
- 包管理器
- 操作系统
【免费下载链接】nixpkgs
Nix Packages collection & NixOS
相关推荐
NixOS 上部署 Anki Sync Server:内置同步服务模块配置与源码级原理详解
NixOS 上部署 Anki Sync Server:内置同步服务模块配置与源码级原理详解 导读 本文围绕 NixOS 仓库中的 services.anki s
包管理器操作系统在 NixOS 上部署 GoToSocial:ActivityPub 联邦社交服务器完整配置指南
在 NixOS 上部署 GoToSocial:ActivityPub 联邦社交服务器完整配置指南 GoToSocial 是一个用 Golang 编写的 Acti
包管理器操作系统NixOS Livebook 模块实战:用户服务部署、environmentFile 安全配置与源码级原理
NixOS Livebook 模块实战:用户服务部署、environmentFile 安全配置与源码级原理 本文基于 NixOS 官方 Livebook 模块文
包管理器操作系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考