# 部署指南

> 生产环境下 ServerBee 服务端和 Agent 的部署策略。

URL: https://docs.serverbee.app/zh/docs/deployment

本文介绍在生产环境部署 ServerBee 的最佳实践：Railway、Docker、systemd、反向代理配置、TLS 和备份策略。

## Railway（一键部署） [#railway一键部署]

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/serverbee-server)

最快的部署方式。点击上方按钮，然后配置以下环境变量：

```bash
SERVERBEE_LOG__LEVEL="info"                    # 日志级别（trace/debug/info/warn/error）

SERVERBEE_RETENTION__RECORDS_DAYS="7"          # 原始指标保留天数
SERVERBEE_RETENTION__RECORDS_HOURLY_DAYS="90"  # 小时聚合保留天数
SERVERBEE_RETENTION__AUDIT_LOGS_DAYS="180"     # 审计日志保留天数
SERVERBEE_SCHEDULER__TIMEZONE="UTC"            # 时区，影响流量按天聚合（如 Asia/Shanghai）

SERVERBEE_OAUTH__BASE_URL=""                   # OAuth 回调公网地址（如 https://xxx.up.railway.app）
SERVERBEE_OAUTH__GITHUB__CLIENT_ID=""          # GitHub OAuth Client ID
SERVERBEE_OAUTH__GITHUB__CLIENT_SECRET=""      # GitHub OAuth Client Secret
SERVERBEE_OAUTH__ALLOW_REGISTRATION="false"    # 首次 OAuth 登录自动创建账号（true=开放注册，false=仅已绑定用户可登录）
```

部署后：

1. 添加 **Volume** 挂载到 `/data`，以在多次部署间持久化数据
2. 将 Agent 配置为使用 Railway 提供的 URL 进行连接
3. 首次启动时，服务端会自动创建管理员账号并随机生成密码。打开 Railway 部署日志，查找醒目的凭据横幅即可获取该密码。首次登录时你必须修改此密码，并可选择一个新的用户名。

<Callout type="info">
  Railway 会自动分配端口并提供 HTTPS，无需配置 `SERVERBEE_SERVER__LISTEN` 或 TLS 证书。
</Callout>

### 选择 Railway 版本 [#选择-railway-版本]

源码模板会固定为仓库当前包版本，目前是 `1.0.0-beta.4`。ServerBee 尚无稳定 `1.x` 版本时，这可避免静默部署 `:latest` 所指向的旧镜像。

要锁定到指定版本（稳定版或预发版），在 Railway 服务的 **Variables** 里新增一条变量：

```bash
SERVERBEE_IMAGE_TAG=1.0.0-beta.4
```

保存后触发 Redeploy。修改版本前先查看 [Release 页面](https://github.com/ZingerLittleBee/ServerBee/releases)。后续预发布版也会更新浮动 `beta` tag，`latest` 只由无后缀稳定版更新；需要可重复部署时仍应固定精确版本。

<Callout type="info">
  Dockerfile 通过 `ARG SERVERBEE_IMAGE_TAG=1.0.0-beta.4` 暴露这个开关。Railway 会自动把 Service Variables 同时作为构建参数和运行时环境变量注入；该变量不在 `SERVERBEE_*` 配置体系中，因此对运行时无副作用。
</Callout>

## 引导安装 [#引导安装]

在 Linux 主机上，最快的二进制 + systemd 部署方式是使用引导安装脚本：

```bash
# 安装服务端
curl -fsSL https://raw.githubusercontent.com/ZingerLittleBee/ServerBee/main/deploy/install.sh | sudo sh -s -- server

# 安装 Agent
curl -fsSL https://raw.githubusercontent.com/ZingerLittleBee/ServerBee/main/deploy/install.sh | sudo sh -s -- agent \
  --server-url http://your-server-ip:9527 \
  --enrollment-code YOUR_ONE_TIME_CODE
```

安装完成后，使用 `serverbee` CLI 管理你的部署（安装时会自动部署到 `/usr/local/bin/serverbee`）：

```bash
sudo serverbee status
sudo serverbee upgrade -y
sudo serverbee upgrade --channel beta -y   # 显式保持预发布轨道
sudo serverbee restart
sudo serverbee config
sudo serverbee env
sudo serverbee uninstall agent -y
```

<Callout type="info">
  `install server` / `install agent` 是引导命令。如果 `/usr/local/bin` 中已经存在对应二进制，安装脚本会直接沿用现有文件而不会覆盖。向已部署主机下发新 release 二进制时，请改用 `upgrade`（或手动替换二进制）。
</Callout>

默认 `auto` 通道会在无后缀稳定版存在时选择最新稳定版，否则回退到最新预发布版。安装器会按组件保存该策略，并在升级时复用。传 `--channel stable` 或 `--channel beta` 可显式固定轨道，也可用 `--version vX.Y.Z-beta.N` 固定精确版本。CLI 会拒绝用较旧的 SemVer 覆盖当前安装。

## Docker Compose（推荐） [#docker-compose推荐]

Docker Compose 是在生产环境部署 ServerBee 最简单的方式。先创建配置文件（不能让不存在的 bind source 被 Docker 自动建成目录）：

```bash
mkdir -p config
cat > config/server.toml <<'EOF'
[server]
data_dir = "/data"
EOF
```

然后在 `config` 同级创建 `docker-compose.yml`：

```yaml
services:
  serverbee-server:
    image: ghcr.io/zingerlittlebee/serverbee-server:1.0.0-beta.4
    container_name: serverbee-server
    restart: unless-stopped
    ports:
      - "127.0.0.1:9527:9527"
    volumes:
      - serverbee-data:/data
      - ./config/server.toml:/etc/serverbee/server.toml:ro
    environment:
      - SERVERBEE_AUTH__SECURE_COOKIE=true
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:9527/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

volumes:
  serverbee-data:
```

<Callout type="info">
  将端口绑定到 `127.0.0.1:9527` 而非 `0.0.0.0:9527`，可确保服务端只能通过反向代理访问，而无法从公网直接访问。
</Callout>

启动服务：

```bash
docker compose up -d
```

查看日志：

```bash
docker compose logs -f serverbee-server
```

## 源码编译 [#源码编译]

开发者或有定制需求的用户可以从源码编译 ServerBee。源码编译需要 Rust 1.89+ 和 Bun 1.x（或 Node.js 22+）。

```bash
# 克隆仓库
git clone https://github.com/ZingerLittleBee/ServerBee.git
cd ServerBee

# 构建前端
cd apps/web
bun install
bun run build
cd ../..

# 构建服务端（通过 rust-embed 内嵌前端静态资源）
cargo build --release -p serverbee-server

# 构建 Agent
cargo build --release -p serverbee-agent
```

编译产物位于 `target/release/` 目录下：

* `serverbee-server` — 服务端，内嵌前端静态资源
* `serverbee-agent` — 指标采集 Agent

启动服务端：

```bash
./target/release/serverbee-server
```

将编译好的二进制部署到目标主机后，可以参考下文的 systemd 服务配置进行进程管理。

## systemd 服务 [#systemd-服务]

对于不使用 Docker 的部署，可以用 systemd 管理服务端和 Agent 进程。

<Callout type="info">
  如果你不需要手写 unit 文件，更推荐上面的引导安装方式。它会为你自动生成配置文件和 systemd unit，后续通过 `serverbee` CLI 统一完成升级、重启和配置修改。

  注意：下面的 unit 示例采用自定义路径（`/usr/local/bin`、`/var/lib/serverbee`、`/etc/serverbee`）演示手动部署。引导脚本安装的实际布局不同——二进制在 `/opt/serverbee/bin/`、配置在 `/opt/serverbee/etc/`、数据在 `/opt/serverbee/data/`，且由 `serverbee` CLI 托管，无需手写 unit。
</Callout>

### Server 服务 [#server-服务]

创建 `/etc/systemd/system/serverbee-server.service`：

```ini
[Unit]
Description=ServerBee Server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/serverbee-server
Restart=always
RestartSec=5
User=serverbee
Group=serverbee
WorkingDirectory=/var/lib/serverbee

# 安全加固
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/serverbee /var/log/serverbee
PrivateTmp=true

# 资源限制
MemoryMax=512M

[Install]
WantedBy=multi-user.target
```

创建服务用户和目录：

```bash
# 创建系统用户
sudo useradd -r -s /sbin/nologin -d /var/lib/serverbee serverbee

# 创建目录
sudo mkdir -p /var/lib/serverbee /var/log/serverbee /etc/serverbee
sudo chown serverbee:serverbee /var/lib/serverbee /var/log/serverbee

# 放置配置文件
sudo cp server.toml /etc/serverbee/server.toml

# 放置二进制
sudo cp serverbee-server /usr/local/bin/
sudo chmod +x /usr/local/bin/serverbee-server
```

systemd 部署使用的生产 `server.toml`：

```toml
[server]
listen = "127.0.0.1:9527"
data_dir = "/var/lib/serverbee"

[log]
level = "info"
file = "/var/log/serverbee/server.log"
```

启用并启动：

```bash
sudo systemctl daemon-reload
sudo systemctl enable serverbee-server
sudo systemctl start serverbee-server
sudo systemctl status serverbee-server
```

### Agent 服务 [#agent-服务]

完整的 Agent systemd 服务 unit 文件请参见 [Agent 配置](/zh/docs/agent) 指南。

## 反向代理 [#反向代理]

强烈建议在生产环境中将 ServerBee 部署在反向代理之后。反向代理可以提供 TLS 终止、HTTP/2 以及额外的安全响应头。

### 访问地址和 Cookie 配置 [#访问地址和-cookie-配置]

先确定一个对外访问地址，并让浏览器地址、Agent 的 `server_url` 和 Cookie 配置保持一致：

| 场景              | 对外地址                          | `auth.secure_cookie` | Docker 环境变量                                  | Agent 地址                      |
| --------------- | ----------------------------- | -------------------- | -------------------------------------------- | ----------------------------- |
| IP 直连，普通 HTTP   | `http://203.0.113.10:9527`    | `false`              | `SERVERBEE_AUTH__SECURE_COOKIE=false`        | `http://203.0.113.10:9527`    |
| 域名 + HTTPS 反向代理 | `https://monitor.example.com` | `true`               | `SERVERBEE_AUTH__SECURE_COOKIE=true` 或不设置该变量 | `https://monitor.example.com` |

使用域名访问时，需要先添加 DNS `A` 或 `AAAA` 记录指向服务器，再用 Nginx、Caddy 或 Traefik 终止 HTTPS，并把请求反向代理到 ServerBee 的 `127.0.0.1:9527`。

如果你是通过快速开始脚本安装的，脚本会为了 HTTP 直连写入 `auth.secure_cookie = false`。在把同一套安装切换到 HTTPS 前，请修改 `/opt/serverbee/etc/server.toml`：

```toml
[auth]
secure_cookie = true
```

然后重启服务端：

```bash
sudo systemctl restart serverbee-server
```

如果 ServerBee 已经通过安装脚本部署，也可以让 CLI 自动完成 Caddy HTTPS 配置：

```bash
sudo serverbee domain setup --domain monitor.example.com --email admin@example.com -y
```

该命令会校验域名是否解析到当前服务器，安装并配置 Caddy，把 ServerBee 改为只监听 `127.0.0.1:9527`，并把 `auth.secure_cookie` 设置为 `true`。如果 DNS 还没生效，命令会停止并打印你需要添加的 `A`/`AAAA` 记录。

### Nginx [#nginx]

```nginx
upstream serverbee {
    server 127.0.0.1:9527;
}

server {
    listen 80;
    server_name monitor.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name monitor.example.com;

    ssl_certificate     /etc/letsencrypt/live/monitor.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/monitor.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    # 安全响应头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    location / {
        proxy_pass http://serverbee;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket 支持（Agent、浏览器实时更新和终端均需要）
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # 为长连接 WebSocket 设置较长的超时
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;

        # 关闭缓冲以支持实时数据
        proxy_buffering off;
    }
}
```

### Caddy [#caddy]

Caddy 会自动使用 Let's Encrypt 处理 TLS：

```
monitor.example.com {
    reverse_proxy 127.0.0.1:9527 {
        # Caddy 默认支持 WebSocket
    }
}
```

这就是所需的全部 Caddy 配置。Caddy 默认处理 HTTPS 证书、HTTP/2、WebSocket 升级和安全响应头。

### Traefik [#traefik]

Traefik 通过 Docker labels 集成，无需单独的配置文件。Traefik 会自动检测 WebSocket 连接，无需额外配置。

```yaml
services:
  serverbee-server:
    image: ghcr.io/zingerlittlebee/serverbee-server:1.0.0-beta.4
    volumes:
      - serverbee-data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.serverbee.rule=Host(`monitor.example.com`)"
      - "traefik.http.routers.serverbee.entrypoints=websecure"
      - "traefik.http.routers.serverbee.tls.certresolver=letsencrypt"
      - "traefik.http.services.serverbee.loadbalancer.server.port=9527"
    restart: unless-stopped
    networks:
      - traefik

networks:
  traefik:
    external: true

volumes:
  serverbee-data:
```

## TLS / HTTPS [#tls--https]

生产环境部署请始终使用 HTTPS。推荐的方式如下：

### Let's Encrypt + Certbot（Nginx） [#lets-encrypt--certbotnginx]

```bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d monitor.example.com
```

Certbot 会自动配置 nginx 并设置证书续期。

### Let's Encrypt + Caddy [#lets-encrypt--caddy]

Caddy 会自动申请和续期证书，无需额外设置。

### 手动证书 [#手动证书]

如果你有自己的证书，请把它们放在一个安全目录中，并在反向代理配置里引用。确保证书文件可被代理进程读取，并且你已经准备好续期机制。

## Agent HTTPS 连接配置 [#agent-https-连接配置]

当服务端部署在 HTTPS 之后时，需要相应地更新 Agent 的 `server_url`：

```toml
server_url = "https://monitor.example.com"
```

Agent 会自动根据提供的 URL 处理 WebSocket 连接。

<Callout type="info">
  ServerBee 自身不处理 TLS 终止。所有 HTTPS/WSS 加密都由前置的反向代理（Nginx/Caddy/Traefik）处理。这种架构简化了服务端的实现，也便于统一管理证书。
</Callout>

<Callout type="warn">
  使用 HTTPS 时，请在服务端配置中保持 `auth.secure_cookie = true`。设为 `false` 可能仍然可以登录，但会去掉浏览器的 Secure Cookie 保护，不适合生产环境。
</Callout>

## 备份与恢复 [#备份与恢复]

### 备份哪些内容 [#persistent-data]

SQLite 保存监控记录、用户、设置和上传的组件包。部分资产存放在独立文件中，仅下载数据库不能恢复完整安装：

| 项目              | 位置                                                                    | 数据库备份是否包含 | 说明                                         |
| --------------- | --------------------------------------------------------------------- | --------- | ------------------------------------------ |
| 数据库             | `{data_dir}/serverbee.db`                                             | 是         | 默认 `database.path`；包含数据库中的设置和组件包           |
| 品牌资产            | `{data_dir}/brand/`                                                   | 否         | 上传的 Logo 和 favicon；数据库只保存访问 URL，不保存图片内容    |
| 下载的 GeoIP 国家数据库 | `{data_dir}/dbip-country-lite.mmdb`                                   | 否         | 已下载的 DB-IP Lite Country 数据库                |
| 下载的 ASN 数据库     | `{data_dir}/dbip-asn-lite.mmdb`                                       | 否         | 已下载的 DB-IP Lite ASN 数据库                    |
| 自定义 MMDB 文件     | `geoip.mmdb_path`、`asn.mmdb_path`                                     | 否         | 配置路径位于 `data_dir` 之外时，需单独保存                |
| 服务端配置           | `/opt/serverbee/etc/`                                                 | 否         | 安装器配置和版本通道策略；还需保存外部环境文件和密钥                 |
| Agent 配置与状态     | `/opt/serverbee/etc/agent.toml`、配置的 `state_dir` 和 `security.data_dir` | 否         | 在每台 Agent 主机上单独备份，包括运行令牌；Server 备份不会复制远程文件 |

`serverbee.db-wal` 可能包含尚未写入主数据库的变更。运行中的服务端应使用 SQLite `.backup` 或备份 API，不要分别复制数据库和 sidecar 文件。停机后的完整目录快照会一并保留剩余的 `serverbee.db-wal` 和 `serverbee.db-shm`。

### 备份策略 [#备份策略]

下方示例使用安装器默认路径：`/opt/serverbee/data/serverbee.db` 和 `/opt/serverbee/etc/`。请按实际部署替换数据目录、数据库文件名、配置目录及外部 MMDB 路径。Docker 示例采用上文的 Compose 布局（`/data`、`config/server.toml` 和 `docker-compose.yml`）。

主机文件操作以 root 身份执行，需安装 `sqlite3` CLI，并限制备份目录的访问权限。先创建一个带时间戳的目录，保存数据库及配套资产：

```bash
set -e
umask 077
SERVERBEE_BACKUP_DIR="/backups/serverbee-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$SERVERBEE_BACKUP_DIR"
```

**方式零：内置管理员备份 API，仅数据库**

管理员专用 `/api/settings/backup` 端点使用 SQLite `VACUUM INTO`，仅导出数据库，不包含品牌图片、MMDB 文件、配置或 Agent 状态。把管理员 API key 放在环境变量中：

```bash
curl --fail --request POST \
  --header "X-API-Key: ${SERVERBEE_ADMIN_API_KEY}" \
  --output "$SERVERBEE_BACKUP_DIR/serverbee.db" \
  https://monitor.example.com/api/settings/backup
test "$(sqlite3 "$SERVERBEE_BACKUP_DIR/serverbee.db" 'PRAGMA integrity_check;')" = ok
```

若在另一台主机上下载数据库，请在 Server 主机上生成资产和配置归档，再复制到同一份备份目录中。

**方式一：SQLite 备份命令，仅数据库**

```bash
sqlite3 /opt/serverbee/data/serverbee.db ".backup '$SERVERBEE_BACKUP_DIR/serverbee.db'"
test "$(sqlite3 "$SERVERBEE_BACKUP_DIR/serverbee.db" 'PRAGMA integrity_check;')" = ok
```

以上两种数据库备份还需配套以下归档。`assets.tar.gz` 保存数据目录中除当前数据库及其 sidecar 之外的文件，包含已存在的 `brand/` 和两种下载的 MMDB 文件；`config.tar.gz` 保存安装器配置目录：

```bash
tar czf "$SERVERBEE_BACKUP_DIR/assets.tar.gz" \
  --exclude='./serverbee.db*' -C /opt/serverbee/data .
tar czf "$SERVERBEE_BACKUP_DIR/config.tar.gz" -C /opt/serverbee etc
```

收集这一组文件时，请保持品牌资产、MMDB 下载和配置不变。分别在线复制不构成原子快照；这些文件正在变化时，请使用下方的停机快照。还需把外部 `geoip.mmdb_path`、`asn.mmdb_path`、环境文件及密钥加入备份，并记录原始路径。验证后将完整备份复制到 ServerBee 主机之外。

**方式二：完整主机快照，需要停止服务端**

```bash
set -e
sudo systemctl stop serverbee-server
tar czf "$SERVERBEE_BACKUP_DIR/data.tar.gz" -C /opt/serverbee/data .
tar czf "$SERVERBEE_BACKUP_DIR/config.tar.gz" -C /opt/serverbee etc
sudo systemctl start serverbee-server
```

数据归档包含数据库、剩余 WAL/SHM 文件、品牌资产和下载的 MMDB。外部文件仍需按上文单独保存。归档命令失败时，先解决问题，再重启并确认备份完整。

**方式三：完整 Docker volume 与配置快照**

```bash
set -e
umask 077
SERVERBEE_BACKUP_DIR="$PWD/backups/serverbee-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$SERVERBEE_BACKUP_DIR"
docker compose stop serverbee-server

SERVERBEE_DATA_VOLUME="$(
  docker inspect serverbee-server \
    --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{println .Name}}{{end}}{{end}}'
)"
test -n "$SERVERBEE_DATA_VOLUME"

docker run --rm \
  --mount "type=volume,src=${SERVERBEE_DATA_VOLUME},dst=/data,readonly" \
  --mount "type=bind,src=${SERVERBEE_BACKUP_DIR},dst=/backups" \
  alpine tar czf /backups/data.tar.gz -C /data .
tar czf "$SERVERBEE_BACKUP_DIR/config.tar.gz" config docker-compose.yml

docker compose start serverbee-server
```

这会保存实际 `/data` volume 中的全部文件，包括品牌及下载的 MMDB 资产，并另行归档挂载的配置。Compose `.env` 文件、外部密钥及 MMDB bind mount 也需保存。不要假设 volume 的实际名称一定是 `serverbee-data`：Compose 通常会添加项目名前缀，引用错误名称可能创建一个空 volume。

### 恢复 [#恢复]

启动 Server 前，先恢复数据库及对应的资产和配置。下方示例适用于配齐 `assets.tar.gz` 和 `config.tar.gz` 的数据库备份，使用安装器布局，并要求事先通过 `PRAGMA integrity_check`：

```bash
set -e
umask 077
SERVERBEE_RESTORE_DIR="/backups/serverbee-20260314T020000Z"
test "$(sqlite3 "$SERVERBEE_RESTORE_DIR/serverbee.db" 'PRAGMA integrity_check;')" = ok
test -f "$SERVERBEE_RESTORE_DIR/assets.tar.gz"
test -f "$SERVERBEE_RESTORE_DIR/config.tar.gz"
sudo systemctl stop serverbee-server

# Preserve the current state before replacing it.
SERVERBEE_BEFORE_RESTORE="/backups/before-restore-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$SERVERBEE_BEFORE_RESTORE"
mv /opt/serverbee/data /opt/serverbee/etc "$SERVERBEE_BEFORE_RESTORE/"

# Restore into an empty directory, without the previous database WAL/SHM.
mkdir -p /opt/serverbee/data
cp "$SERVERBEE_RESTORE_DIR/serverbee.db" /opt/serverbee/data/serverbee.db
tar xzf "$SERVERBEE_RESTORE_DIR/assets.tar.gz" -C /opt/serverbee/data
tar xzf "$SERVERBEE_RESTORE_DIR/config.tar.gz" -C /opt/serverbee

# Restore external MMDB/environment files and service ownership before starting.
# For a dedicated service user: chown -R serverbee:serverbee /opt/serverbee/data
sudo systemctl start serverbee-server
```

停机主机快照则将 `data.tar.gz` 解压到空的数据目录，替代复制 `serverbee.db` 和解压 `assets.tar.gz` 两步。两种方式都需恢复对应的 `config.tar.gz` 和外部文件。启动前检查恢复的配置，旧环境变量或密钥服务中的值可能覆盖恢复的 TOML。

管理员专用 `/api/settings/restore` 端点接受原始 SQLite 文件，会把原数据库保留为 `.pre-restore`，并要求重启。它仅恢复数据库，不修改独立资产和配置，不能代替完整安装恢复。需要恢复这些文件时，请使用上方停机流程或 [Docker 恢复流程](#从-docker-备份恢复到新-volume)。

### 自动备份 [#自动备份]

下方 cron 任务仅定时备份数据库。先安装 `sqlite3`，并执行 `sudo install -d -m 0700 /backups` 创建目录：

```bash
# /etc/cron.d/serverbee-backup
0 2 * * * root umask 077; sqlite3 /opt/serverbee/data/serverbee.db ".backup '/backups/serverbee-$(date +\%Y\%m\%d).db'" && find /backups -name 'serverbee-*.db' -mtime +30 -delete
```

它会在每天凌晨 2:00 运行，删除超过 30 天的数据库备份。资产和配置也需安排配套归档，尤其在品牌、MMDB 或配置发生变更后；验证后将完整备份复制到主机之外。

## 健康检查 [#健康检查]

ServerBee 提供一个健康检查端点：

```
GET /healthz
```

用它来确认服务端是否正在运行且响应正常。可以在你的监控系统、Docker 健康检查或负载均衡器中配置它。

### Docker 健康检查 [#docker-健康检查]

上面的 Docker Compose 示例已包含健康检查。连续 3 次失败只会把容器标记为 `unhealthy`，Docker **不会**仅因 unhealthy 自动重启；restart policy 只在进程退出时生效。请从外部监控健康状态，再有意识地排查或重启。

### 外部监控 [#外部监控]

如果你使用外部的可用性监控服务，请把它指向：

```
https://monitor.example.com/healthz
```

这样就形成了「监控监控系统」的配置，当 ServerBee 自身宕机时你也会收到告警。

## 资源需求 [#资源需求]

ServerBee 面向轻量级 VPS 实例设计：

| 组件        | 最低配置                | 推荐配置           |
| --------- | ------------------- | -------------- |
| 服务端 CPU   | 1 vCPU              | 2 vCPU         |
| 服务端内存     | 128 MB              | 256 MB         |
| 服务端磁盘     | 100 MB + 数据         | 1 GB+（取决于保留策略） |
| Agent CPU | 可忽略                 | \< 单核的 1%      |
| Agent 内存  | 实测稳态 cgroup 约 27 MB | 30–40 MB 余量    |

数据库大小取决于服务器数量、启用的数据写入功能、采样间隔和保留设置。请使用[存储与容量规划](/zh/docs/storage-sizing)中的实测 30 天基线、功能乘数和 WAL 预留建议，避免混用不同保留窗口的粗略估算。

官方 Linux release 二进制和 Docker 镜像使用 musl，`MALLOC_ARENA_MAX` 对这些构建无效。请测量实际部署版本和负载，不要按历史 glibc 记录决定容量。详见[资源开销](/zh/docs/resource-usage)。

## 安全检查清单 [#安全检查清单]

将 ServerBee 暴露到公网前，请逐项确认：

* [ ] 修改默认管理员密码
* [ ] 使用 HTTPS 和有效的 TLS 证书
* [ ] 设置 `auth.secure_cookie = true`（默认值）
* [ ] 将服务端绑定到 localhost，并通过反向代理对外暴露
* [ ] 为登录尝试设置严格的速率限制
* [ ] 为管理员账号启用 TOTP 两步验证
* [ ] 仅在需要时通过「添加 Server」或显式重新接入发出绑定且短时有效的 offer
* [ ] 保持 GeoIP 数据库更新（如启用）
* [ ] 配置自动备份
* [ ] 用外部健康检查监控服务端自身

## 升级指南 [#升级指南]

ServerBee 在启动时会自动运行数据库迁移，升级后无需手动执行任何迁移步骤。升级前请先备份完整的数据目录、配置和外部资产。

**Docker：**

```bash
docker compose pull
docker compose up -d            # 重启并自动运行迁移
docker compose ps
```

**二进制：**

```bash
wget https://github.com/ZingerLittleBee/ServerBee/releases/download/v1.0.0-beta.4/serverbee-server-linux-amd64
sudo systemctl stop serverbee-server
sudo mv serverbee-server-linux-amd64 /usr/local/bin/serverbee-server
sudo chmod +x /usr/local/bin/serverbee-server
sudo systemctl start serverbee-server
```

如果你使用安装脚本部署，`sudo serverbee upgrade -y` 会自动完成二进制下载、替换和重启。

## 卸载与清除 [#卸载与清除]

`sudo serverbee uninstall <server|agent>` 会停止并删除受管理的服务/容器与可执行文件，但有意保留配置和数据，便于重装。只有完成并验证备份后才使用 `--purge -y`；它还会删除安装器管理的配置、Server 数据或 Docker volume/image，并在没有剩余组件时删除管理 CLI。

以下残留需要单独确认：

* Agent 运行状态默认位于 `/var/lib/serverbee/capability_grants.json` 和 `/var/lib/serverbee/security`。自定义 `state_dir` 或 `security.data_dir` 可能指向别处；确认没有其他 Agent 使用后再删除。
* 域名配置可能安装 Caddy，并在 `/etc/caddy/Caddyfile` 加入域名块。卸载器不会删除软件包或共享代理配置。先备份文件，只删除 ServerBee 域名块，执行 `sudo caddy validate --config /etc/caddy/Caddyfile`，再运行 `sudo systemctl reload caddy`。仅当没有其他站点使用 Caddy 时才卸载它。
* 非 purge 的 Docker 卸载会保留 Compose 文件、配置与 named volume。删除前先通过 `docker inspect serverbee-server` 核对准确 volume。

清理后确认没有 `serverbee-*` 服务/容器仍在运行，并确认 `9527`（若同时删除代理，还包括 `80`/`443`）的监听者符合预期。

## 数据库迁移失败与恢复 [#数据库迁移失败与恢复]

ServerBee 在 Rust 服务端启动过程中执行数据库迁移。迁移发生在 HTTP 监听端口启动之前；如果迁移失败，进程会退出，Docker 的 `restart: unless-stopped` 或 systemd 的重启策略可能使服务进入重启循环。

<Callout type="warn">
  当前版本不会在迁移前自动创建数据库快照，也没有自动回滚迁移。Docker volume 只能持久化文件，不是可回退的版本。所有迁移都是向前执行，迁移的 `down()` 不会恢复旧 schema。
</Callout>

### 可能失败的场景 [#可能失败的场景]

| 类别        | 典型原因                                   | 推荐处理                                    |
| --------- | -------------------------------------- | --------------------------------------- |
| 存储不可写     | `/data` 权限错误、只读挂载、磁盘或 inode 耗尽         | 停止重启循环，修复存储问题，然后重新执行迁移                  |
| 数据库锁      | 另一个 ServerBee 实例、备份程序或 SQLite 客户端长期持有锁 | 确认只有一个服务端写入数据库，停止冲突进程后重试                |
| 数据库损坏     | SQLite 主文件、WAL 或 SHM 损坏，主机异常断电         | 保留故障现场，执行完整性检查，优先恢复已验证备份                |
| schema 漂移 | 手工修改过表、索引或迁移历史，实际结构与迁移记录不一致            | 不要伪造迁移记录；恢复兼容备份或发布向前修复迁移                |
| 旧数据违反新约束  | 重复值、空值、孤立外键或无法解析的历史数据                  | 在数据库副本中定位并修复数据，再重新执行迁移                  |
| 迁移实现缺陷    | SQL、数据回填或目标 schema 本身有错误               | 保留原数据库，部署包含修正版迁移的新镜像                    |
| 执行中断      | 迁移时容器被强制终止、主机重启或断电                     | 先制作故障快照；确认 schema 和迁移记录一致后再重试           |
| 多实例竞争     | 多个容器同时对同一个 SQLite 文件启动迁移               | 将服务缩容到一个实例；SQLite 部署不应由多个 Server 实例共享写入 |
| 版本回退不兼容   | 新版迁移已成功，但随后只把镜像切回旧版                    | 同时恢复升级前数据库备份，或使用兼容新 schema 的应用版本        |

### 第一步：停止重启循环 [#第一步停止重启循环]

Docker Compose：

```bash
docker compose stop serverbee-server
```

如果容器仍然被外部工具反复拉起，可以临时关闭容器级重启策略：

```bash
docker update --restart=no serverbee-server
docker stop serverbee-server
```

systemd：

```bash
sudo systemctl stop serverbee-server
sudo systemctl reset-failed serverbee-server
```

停止服务不会删除 Docker volume 或数据库文件。不要使用 `docker compose down -v`，其中的 `-v` 会删除 Compose 管理的 volume。

### 第二步：读取迁移错误 [#第二步读取迁移错误]

Docker：

```bash
docker logs --tail 200 serverbee-server
```

systemd：

```bash
sudo journalctl -u serverbee-server -b -n 200 --no-pager
```

迁移成功时日志中会出现 `Database migrations complete`。如果没有这条日志，请从第一条数据库或 migration 错误开始排查，不要只看后续重启信息。

### 第三步：保存故障现场 [#第三步保存故障现场]

即使数据库可能已经处于部分迁移或损坏状态，也要先保存完整副本。它既是人工修复的输入，也是避免后续尝试覆盖证据的保险。

Docker：

```bash
SERVERBEE_DATA_VOLUME="$(
  docker inspect serverbee-server \
    --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{println .Name}}{{end}}{{end}}'
)"
test -n "$SERVERBEE_DATA_VOLUME"

mkdir -p backups
docker run --rm \
  --mount "type=volume,src=${SERVERBEE_DATA_VOLUME},dst=/data,readonly" \
  --mount "type=bind,src=$(pwd)/backups,dst=/backups" \
  alpine \
  tar czf /backups/serverbee-failed-migration-$(date -u +%Y%m%dT%H%M%SZ).tar.gz -C /data .
```

systemd 或二进制部署，在服务停止后复制整个数据目录，而不是只复制主数据库文件：

```bash
sudo mkdir -p /backups
sudo cp -a /opt/serverbee/data \
  "/backups/serverbee-failed-migration-$(date -u +%Y%m%dT%H%M%SZ)"
```

故障数据快照还需配套保存配置、外部 MMDB 文件、环境文件和密钥。这些文件可能解释故障原因，也是完整恢复所需的内容。

### 第四步：检查数据库状态 [#第四步检查数据库状态]

在故障快照的副本上运行检查，不要直接修改唯一的生产 volume：

```bash
mkdir -p recovery-data
SERVERBEE_FAILED_BACKUP="$(ls -1t backups/serverbee-failed-migration-*.tar.gz | head -n 1)"
test -f "$SERVERBEE_FAILED_BACKUP"
tar xzf "$SERVERBEE_FAILED_BACKUP" -C recovery-data

sqlite3 recovery-data/serverbee.db "PRAGMA integrity_check;"
sqlite3 recovery-data/serverbee.db "PRAGMA foreign_key_check;"
sqlite3 recovery-data/serverbee.db \
  "SELECT * FROM seaql_migrations ORDER BY applied_at;"
```

* `PRAGMA integrity_check` 应返回 `ok`。
* `PRAGMA foreign_key_check` 正常情况下不返回任何记录。
* `seaql_migrations` 展示已经记录为成功的迁移；全新数据库在迁移表创建前失败时，这张表可能不存在。

不要删除 `seaql_migrations` 中的记录，也不要手工把失败迁移标记为成功。迁移记录与真实 schema 不一致时，后续版本可能在更难恢复的位置失败。

### 第五步：选择恢复方式 [#第五步选择恢复方式]

**环境问题**

如果问题只是磁盘空间、权限、只读挂载或临时数据库锁，修复根因后重新启动。SeaORM 会再次执行尚未记录为成功的迁移：

```bash
docker update --restart=unless-stopped serverbee-server
docker compose up -d serverbee-server
docker logs -f serverbee-server
```

**旧数据或 schema 不兼容**

在数据库副本上识别冲突数据。优先发布能够处理旧数据的向前修复 migration；如果必须人工修改数据，先在副本上验证完整迁移和应用启动，再对生产数据库执行同一组已审查操作。

**迁移代码缺陷**

不要反复重启同一个失败镜像。保留故障现场，部署包含修正版 migration 的新镜像。仅切回旧镜像不等于数据库回滚；只有在确认失败迁移没有改变 schema 时才可能安全，否则应恢复升级前备份。

**数据库损坏**

优先恢复最近一次通过完整性检查的备份。SQLite `.recover` 只应作为没有可用备份时的数据抢救手段，恢复结果可能不完整，需要在隔离副本上验证。

### 从 Docker 备份恢复到新 volume [#从-docker-备份恢复到新-volume]

恢复时建议创建新 volume，不要覆盖故障 volume。这样可以随时切回故障现场继续分析：

```bash
SERVERBEE_RECOVERY_VOLUME="serverbee-data-recovery-$(date -u +%Y%m%dT%H%M%SZ)"
docker volume create "$SERVERBEE_RECOVERY_VOLUME"

docker run --rm \
  --mount "type=volume,src=${SERVERBEE_RECOVERY_VOLUME},dst=/data" \
  --mount "type=bind,src=$(pwd)/backups/serverbee-20260314T020000Z,dst=/backups,readonly" \
  alpine \
  tar xzf /backups/data.tar.gz -C /data

echo "$SERVERBEE_RECOVERY_VOLUME"

# Inspect the matching Compose/configuration archive before restoring it.
mkdir -p recovered-config
tar xzf backups/serverbee-20260314T020000Z/config.tar.gz -C recovered-config
```

检查 `recovered-config/config/server.toml` 和 `recovered-config/docker-compose.yml`，恢复到部署项目，并重新配置对应的外部 MMDB 挂载、环境文件和密钥。数据归档会恢复 `brand/` 及下载的 MMDB 文件，无法恢复 `/data` 之外的文件。

然后让 Compose 中现有的 `serverbee-data` 逻辑名称指向刚创建的 volume：

```yaml
volumes:
  serverbee-data:
    external: true
    name: serverbee-data-recovery-20260722T103000Z
```

确认 Compose 文件中的 `name` 与上一条命令输出完全一致，再启动服务。恢复较旧的数据库时，目标镜像可能再次执行迁移，因此应先确定要运行的是原版本还是包含修复的新版本。

### 恢复后验证 [#恢复后验证]

```bash
docker compose up -d serverbee-server
docker compose ps
docker logs --tail 200 serverbee-server
curl --fail http://127.0.0.1:9527/healthz
```

只有同时满足以下条件，才能认为恢复完成：

* 日志包含 `Database migrations complete`。
* 容器保持运行且健康检查通过。
* `PRAGMA integrity_check` 返回 `ok`。
* 能登录并读取已有 Server、告警和历史记录。
* 原先配置的 Logo、favicon 和 GeoIP/ASN 数据库均可用。
* 没有持续出现数据库锁、外键或缺失表/列错误。

### 预防措施 [#预防措施]

* 每次升级前创建并验证完整的数据与配置备份；当前升级命令不会自动备份。
* 将备份复制到 Docker 主机之外，避免主机磁盘故障同时损坏生产 volume 和本地备份。
* SQLite 部署保持单个 Server 实例，不要让多个容器共享写入同一个数据库文件。
* 升级前检查可用磁盘空间，并使用明确的镜像版本 tag，避免无法确认迁移来自哪个版本。
* 保留升级前镜像及配套的数据库、资产和配置备份。数据库回退依赖备份，不能只依赖旧镜像。
* 升级后等待迁移成功日志和健康检查，不要仅以 `docker compose up -d` 返回成功作为完成依据。

<Cards>
  <Card title="Server 配置" href="/zh/docs/server" />

  <Card title="Agent 配置" href="/zh/docs/agent" />

  <Card title="架构设计" href="/zh/docs/architecture" />
</Cards>
