Skip to content

Operations zh

SkimMail docs edited this page Sep 15, 2026 · 3 revisions

English · Tiếng Việt · 中文

运维

安装之后如何运行 SkimMail:保护数据、看清它在做什么、更新它,以及理解它在磁盘 上主动做了哪些事。

这一页提到的每个界面都是已发布版本里真实存在的。凡是还没有界面的地方,这 一页会直说,并给出对应的命令。

所有东西放在哪里

只有一个目录,DATA_DIR —— apt 包是 /var/lib/skimmail

路径 是什么
skimmail.db 数据库:账户、邮件头、规则、设置
blobs/ 缓存的邮件正文(见邮件正文缓存
master.key 加密所有已存账户密码的密钥
plugins/ 按需下载的运行时插件

丢失 master.key 无法挽回。 数据库里每一个账户密码都用它加密,从 1.11.0 起缓存的邮件正文也是。没有密钥的数据库不是「功能降级」,而是根本读不出来。 任何值得保留的备份都必须同时包含两者;只备份 skimmail.db 几乎没有意义。

反过来也成立,而这正是人们清理时最容易做错的地方:删掉 master.key 却留着数 据库,是三种可能状态里最糟的一种。要么整个 DATA_DIR 都删,要么一个都别 删。

备份与恢复

Settings ▸ Backup,仅所有者。完整指南——归档里有什么、加密与密码规则、按 计划自动备份、如何恢复,以及在真正需要之前如何验证一次恢复——单独成页: 备份与恢复

这里值得重复一句,因为它解释了上面那张表:归档把 master.key 作为自己内部的 一个独立条目带走。这正是丢失这个文件不是「从备份恢复」类问题、而更像丢失一行 数据库记录的原因——缺密钥的数据库和缺密钥条目的归档,会以同一种方式失效。

日志

Settings ▸ Logs。 日志级别(debug / info / warn / error)是一个 实时开关:改完立即生效,无需重启,并且会覆盖 LOG_LEVEL(见配置) 设定的启动默认值。

实际查看日志有三种方式:

  • 应用内实时查看器,就在同一个界面上——通过 WebSocket 的实时流,打开时 回填最近约 400 条缓冲记录,可按级别过滤、按文字自由搜索,并有暂停/继续,让 繁忙的实例不会在你读到之前就把某一行滚走。不需要主机的 shell 访问权限。

  • 轮转文件,当设置了 LOG_FILE 时(apt 包将其设为 /var/log/skimmail/skimmail.log)。路径本身是仅限环境变量的——UI 或 API 都无法改变它,因此运行在 SkimMail 内部的任何东西都不可能被诱导写入任意文 件。UI 在运行时改变的是:是否写入文件、轮转的大小 (LOG_MAX_SIZE_MB,1–1024 MB,默认 10),以及保留多少个轮转副本 (LOG_MAX_BACKUPS,0–20,默认 3)。一条用量条显示当前文件有多满; Download 把文件流式传给浏览器,Truncate 在确认后清空它。

  • journal,无论是否有文件 sink 都始终开启:

    sudo journalctl -u skimmail -f
    

    记得加 sudo。没有它你会看到空的或不完整的 journal,而不是错误提示—— 读起来像「什么都没发生」,实际上是你没有权限看。

格式(textjsonLOG_FORMAT)是仅限环境变量、UI 中只读的——级别和轮 转是仅有的两个带实时开关的旋钮。

权限说明。 这个标签页本身在 operator 及以上级别就能打开,查看实时查 看器是 operator 级别的操作。这里的其余一切——读取或修改级别与轮转设置、下载 文件、清空文件——在 API 里都仅限所有者GET/PUT /api/settings/logsGET /api/logs/downloadPOST /api/logs/truncate)。因为这个界面要先加载完设置才会渲染其他任何内 容,今天一个 operator 打开 Settings ▸ Logs 看到的会是一个永远加载不完 的界面,而不是一个隐藏了设置卡片的查看器——在围绕这个标签页分配角色之前值得 知道这一点。见用户与角色

更新

Settings ▸ Updates,仅所有者。SkimMail 能检查自己的发布源(默认是 GitHub Releases;可用 UPDATE_FEED_URL 覆盖为自托管或隔离网络的镜像——见 配置),并且在某些部署方式下能自行安装更新。

检查是可选启用的,默认关闭——在你打开它或点击 Check now 之前,不会有 任何请求发往 GitHub。打开自动检查后,它会按一个周期轮询(默认 24 小时,可 调整),从启动后两分钟开始,这样一次重启永远不会引发意外的「打电话回家」。 比当前运行版本新的发布会显示发行说明和版本徽章;Skip 会记录下那个版本, 让它不再被提示。

这个按钮能不能真正安装,取决于你的部署方式:

部署方式 SkimMail 报告的结果 Update 按钮会做什么
预构建的二进制 tarball,解压到服务用户自己拥有的目录 self 下载、验证、原地替换并自行重启
容器(Docker / containerd / Kubernetes) managed 拒绝;改为拉取并运行新镜像
apt 包 managed 同样拒绝——附带的 systemd unit 里的 ProtectSystem=strict/usr/binskimmail 服务用户只读,即使不在容器里,二进制也无法改写自己

检测是自动的(SkimMail 会寻找 /.dockerenv 和容器 cgroup,再尝试在自己可执 行文件旁创建一个临时文件)——没有设置可以覆盖这个判断。既然 apt 路径也是 managed,就照常方式更新:

sudo apt update && sudo apt install --only-upgrade skimmail

发布后马上出现 "Unable to locate package",几乎总是本地索引过期,而不是发布 失败。先跑 sudo apt update

当按钮真的能运行时——部署方式是 self、有针对你平台发布的发布资产、并 且这个构建带有发布验证密钥(每一个官方 apt/Docker/tarball 构建都有)—— Update now 会下载发布资产以及 SHA256SUMS 和它的 .minisig 签名;用官方 构建里内置的固定密钥验证签名;用已验证的校验和列表核对资产的 SHA256;解压 二进制;把当前的二进制改名为 <binary>.old;把新的改名替换进去;然后 re-exec 进入新进程——进程会替换自己,所以没有单独的「重启」步骤。如果替换 成功但 re-exec 本身失败了(少见,取决于平台),新的二进制其实已经装好了, 只需手动重启一次服务即可完成。

不带验证密钥构建的二进制只会停留在通知级别。 它会告诉你有更新,然后拒绝 安装,而不是安装一个自己无法校验的东西——同一把密钥也支撑着下面「插件」一节 里的 manifest 签名。

回滚是手动的,而且只有 self 部署才可能。 apply 这一步在成功时从不会 删除 <binary>.old,所以如果新版本出了问题,你可以停止服务、把 .old 改回 当前的二进制名字,再重启。这里没有按钮,也没有针对失败更新的自动检测——决定 和恢复都是你自己的事。

自动安装更新(嵌套在检查开关下面,且只有那个开关也打开时才有作用)会在一 次检查发现可自更新的发布时立即应用它。在 managed 部署上,这个开关没有任何 可以触发的对象,因为它想触发的更新会以按钮同样的方式被拒绝;UI 会在开关旁边 说明这一点。

插件

Settings ▸ Plugins,仅所有者。出口和远程访问相关功能以运行时插件的形式 提供,而不是编译进基础二进制,这样一个只读邮件的安装能保持精简。今天存在三 个插件:

插件 类型 服务于
cloudflared external(Cloudflare 官方二进制,已固定) 远程访问 —— Cloudflare Tunnel
skimmail-tunnel-ts SkimMail 自行构建 远程访问 —— Tailscale
skimmail-egress-wg SkimMail 自行构建 Connections 上的内嵌 WireGuard 引擎

它们位于 DATA_DIR/plugins/(见上面的「所有东西放在哪里」),以服务器自身 的权限作为子进程运行,并且每次被 spawn 时都会重新验证——不只是安装时。

Install 为你的 OS/架构预取已固定的构件,做 SHA256 校验,并显示下载进度 条;此时还没有任何东西在运行,所以不需要确认。Update 把已安装的插件对 账到 manifest 当前固定的版本——如果它正支撑着某个正在运行的东西(一条正在 运行的隧道、一个启用中的内嵌 WireGuard 出口),确认框会点名短暂中断的是什 么,随后它先停止再重新拉起。Uninstall 直接删除文件,同样先点名影响。内 嵌 WireGuard 引擎还有一个在插件存在于磁盘之前使用的进程内「builtin」构建; 它在这个界面里既不可安装也不可移除,因为根本没有文件可以操作。

在 API 里读取清单是 operator 级别的路由,但整个 Settings ▸ Plugins 标 签页在 UI 里要求所有者,所以实际上低于所有者的角色完全无法通过应用触达这个 界面。

哪些插件可以安装、以及各是什么版本,来自 plugins.json,发布在与 apt 仓库 相同的 GitHub Pages 源上,和自更新用的是同一个信任根。在带有发布验证密钥 的构建上(自 1.10.0 起,且官方构建都是如此),manifest 自身的 .minisig 签名会在其中任何内容被信任之前先被校验,缺少签名会被直接拒绝——一个固定的 哈希值,其可信度不会超过承载它的那份文档,删掉一个文件不能成为关掉这项校验 的办法。没有验证密钥的构建(自定义构建且未设置 UPDATE_PUBKEY)仍然对每个 构件强制执行 SHA256 固定,但会记录一条一次性警告,说明这个固定值本身未经 验证。

同步健康

Settings ▸ Sync。 每个账户的状态、最近一次错误,以及连续失败次数。反复失败 的账户会被自动停止,而不是无限重试;这块面板就是你看到并重新启用它的地方。

这个停止是有意为之,其他功能都尊重它。例如后台正文预取会完全跳过被自动停止的账 户,并且绝不把自己的失败计入那个计数器——可选的后台工作不应该有能力停掉一个 本来健康的邮箱。

完整模型——面板的三种状态、自动停止的阈值及其取值范围,以及哪些同步旋钮需要 重启才能生效——单独成页:同步与同步健康

缓存自己会做的事

缓存的邮件正文现在有了生命周期。完整说明见邮件正文缓存; 从运维角度看,有三件事会自动发生:

1.11.0 首次启动时的一次对账。 它为早期版本缓存的正文建立索引,并在文件系统 存储上删除它们遗留的孤儿对象。你只会看到一次:

reconciled body cache store=fs indexed=1843 bytes=284127744 extra=12 deleted=12

只有一次。它会记录自己已经跑过,所以重启不会重复。

在 S3 上它只统计然后停下。 日志行以 needs_purge=true 结尾,什么都不会被 删除,因为那个存储桶可能同时存放着你的备份归档,而 SkimMail 不会去猜。删除是由 你来决定的:

Settings ▸ Security ▸ 邮件正文缓存 ▸ 清理无归属对象,或者同一件事的 API 形式:

curl -sS -b cookies -X POST http://localhost:8080/api/settings/cache/purge

两条路都仅限所有者。按钮是 1.11.1 才有的;在 1.11.0 上 API 是唯一的路,因为 那时面板被放在一个隐藏的设置标签页里。

持续清理。 超过 CACHE_MAX_SIZE_MB 时最久未读的正文先被清除;超过 BODY_TTL_DAYS 则按年龄过期。环境变量提供初始值(见配置); 从 1.11.1 起,这两个限制可以在 Settings ▸ Security 里修改,在那里保存的 值从此胜过环境变量。

卸载 SkimMail

apt remove 会停止并注销服务,并原封不动地保留 DATA_DIR——包括主密钥。 apt purge 也一样:没有 postremove 脚本,因此不存在任何自动删除你邮件的东西。

彻底清除是手动的,且不可撤销:

sudo rm -rf /var/lib/skimmail
sudo userdel skimmail          # 可选:服务账户

不删除就重新安装,会从你离开的地方原样继续——不需要重新下载任何东西。


参考首页 · 配置 · 安全 · 故障排查 · 常见问题


SkimMail · skimmail@base101.app · 2026-09-15 · commit dffbb18

Clone this wiki locally