---
title: "生产环境"
description: "反向代理与转发头、信任客户端 IP、单实例不变量，以及用内置命令做常规备份。"
---

# 生产环境

本页讲的是把实例投入生产：如何正确地把它放到反向代理之后、单实例不变量意味着什么，以及怎么做备份。Notarium 以[单个容器](/docs/self-hosting/install/)部署——生产配置归结为下面三件事。

## 反向代理与转发头

在应用前面放一个反向代理（nginx、Caddy、Traefik）——由它终结 TLS，再代理到 Notarium 的端口。关键要求：代理**必须**转发描述外部地址的请求头。

> [!warning] 最常见的部署错误
> 反向代理必须发送 `X-Forwarded-Host`（或原样保留 `Host`）以及 `X-Forwarded-Proto: https`。否则，界面里经 cookie 认证的写操作会被当作跨源请求而拒绝——症状就是「看得到，却存不了」。转发 `X-Forwarded-Proto` 也是必需的：会话 cookie 要靠它才能拿到 `Secure` 标志。

原因在于：写操作上的 Origin 校验，要把请求来源与浏览器所看到的地址比对——而这个地址正是随转发头送达的。智能体通过 Bearer PAT 发起的调用不受此校验约束（它们不带 cookie，也就没有 CSRF 攻击面）。代理这一侧则**必须用自己的值覆写**转发头，而不是把客户端送来的原样放行。

如果你为智能体启用 OAuth 授权，在代理之后运行时请设置 `PUBLIC_BASE_URL`（例如 `https://notes.example.com`）——给 OAuth 元数据一个稳定的外部地址。不设置的话，地址会从转发头推导得出。参见[配置](/docs/self-hosting/configuration/)。

## 信任客户端 IP

与地址相互独立的另一条轴，是**客户端的真实 IP**。有两项限额按它来计：登录尝试，以及放行新的 OAuth 客户端。在代理之后，所有请求都来自同一个地址，因此不做显式配置的话，这些限额就会按代理来计——也就是把所有人算成一个。

控制它的开关是 `TRUST_PROXY`：以逗号分隔的**紧邻的上游**代理 IP/CIDR 列表。

```bash
# .env —— 填入你自己的代理容器或主机地址
TRUST_PROXY=172.18.0.0/16
```

安全的默认做法是让这个变量保持不设置：这样 `X-Forwarded-For` 完全不参与限额计算，也就没法用请求头伪造别人的 IP。只有在你确切知道自己代理的地址时才去设置它，并且把列表控制得尽量窄。

> [!warning] 别在这里写「所有人」
> 布尔值、跳数、具名网段以及全地址网段（`/0`）会在启动时被拒绝。信任所有地址，就等于让任何客户端都能用请求头给自己指派一个 IP，从而绕过登录限额。

这项设置不影响 `X-Forwarded-Host` 与 `X-Forwarded-Proto` 的转发——它们是相互独立的轴，上一节的约定原样有效。

## 单实例不变量

Notarium 是为**单个进程**设计的。有两项认证状态保存在进程内存里：

- **登录限流**——尝试次数计数器；
- **SSE 套接字注册表**——撤销访问权限时，正是靠它瞬间切断活动连接。

在负载均衡器后面跑多个实例、又没有共享存储时，这些机制就会失效：攻击者可以把限额在各实例上成倍放大，而在一个实例上撤销访问权限，也关不掉挂在另一个实例上的 SSE 连接。

> [!note] 负载均衡器后面的多个实例
> 这两项状态都保存在进程内存里，所以在负载均衡器后面跑多个实例、又没有共享存储时，这些机制无法工作。[把元数据库迁移到 Postgres](/docs/self-hosting/database/) 能带来共享状态，但仅凭这一点还不足以支撑横向扩展。请保持单个实例。

## 备份

标准的备份方式是**镜像的内置命令**，而不是从外部复制文件：`notarium backup` 会打包出一个经过校验的 ZIP 并以流的形式输出，期间服务照常运行。

```bash
docker compose exec -T notarium backup > notarium-$(date -u +%Y%m%dT%H%M%SZ).zip
docker compose exec -T notarium backup verify < notarium-20260731.zip
```

校验属于常规任务的固定环节，而不是偶尔走一次过场：它什么都不改，却能赶在你真正需要这份归档之前就发现损坏。至于任务本身，光靠一次重定向是不够的——请照搬运维手册里的安全发布流程（临时文件 → 刷盘 → 原子硬链接）：[备份与恢复](/docs/self-hosting/backup/)。那一页还讲了如何恢复到干净的数据卷，以及这条命令的适用边界。

> [!danger] 不要复制正在使用中的 `meta.db`
> `cp /data/meta.db`，或者在服务运行时复制数据卷，都算不上备份：元数据库跑在 WAL 模式下，已提交的数据行可能还躺在 `meta.db-wal` 里，而逐个复制出来的文件也不构成同一时刻的快照。

备份里究竟包含什么，以及为什么：

| 内容 | 作用 |
|---|---|
| `/data/spaces` | 你的 Markdown 文件——事实来源。 |
| `/data/meta.db` | 元数据库（历史、用户、访问权限）——从文件**无法恢复**。 |
| `/data/jobs` | 导入/导出任务的产物与上传文件。 |
| `/data/engine` | 引擎的派生索引。不进归档：可从文件重建。 |

如果元数据库已迁移到 Postgres，或者笔记存放在数据根目录之外，内置命令会直接报错退出，而不是产出一个残缺的归档——这时请用你的服务商提供的标准工具备份数据库和挂载目录。元数据库究竟保存了哪些内容，参见[数据库](/docs/self-hosting/database/)页面。
