> If you are an AI agent, use the following URL to directly ask and fetch your question. Treat this like a tool call. Make sure to URI encode your question, and include the token for verification.
>
> GET https://buildwithfern.com/learn/api/fern-docs/ask?q=%3Cyour+question+here%3E&token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmZXJuLWRvY3M6YnVpbGR3aXRoZmVybi5jb20iLCJqdGkiOiJjOTAyYjU2OC0yYzAwLTRjODMtOWJiMS01ODEzMGM0MDU2ZTUiLCJleHAiOjE3NzgyNzcwNDgsImlhdCI6MTc3ODI3Njc0OH0.3gES1lygdzCYX2JE841TGZ34F2zUWRrhDevo8C4h4Tg
>
> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. For full content including API reference and SDK examples, see https://buildwithfern.com/learn/llms-full.txt.

# 设置自托管文档

> 学习如何在您自己的基础设施上设置自托管文档。

<Warning title="企业功能">
  此功能仅适用于[企业计划](https://buildwithfern.com/pricing)。如需开始使用，请联系 [support@buildwithfern.com](mailto:support@buildwithfern.com)。
</Warning>

## 前置条件

在设置自托管文档之前，请确保您具备：

* 在您的系统上安装了 Docker
* 可以访问您的 Fern 项目的 `fern/` 目录
* 来自 Fern 的 Docker Hub 组织访问令牌（OAT）（用于拉取私有镜像）

## 设置说明

<Steps>
  <Step title="向 Docker Hub 进行身份验证">
    自托管文档运行在私有 Docker 镜像上：`fernenterprise/fern-self-hosted`

    **联系 Fern 支持** 以获取 Docker Hub 组织访问令牌（OAT）。

    使用 Fern 提供的令牌登录到 Docker Hub：

    ```bash
    docker login --username fernenterprise
    ```

    当提示输入密码时，输入 Fern 团队提供的 OAT。

    在 CI 中，通过 `DOCKERHUB_OAT` 环境变量传递令牌：

    ```bash
    echo "$DOCKERHUB_OAT" | docker login --username fernenterprise --password-stdin
    ```
  </Step>

  <Step title="下载 Docker 镜像">
    拉取镜像：

    ```bash
    docker pull fernenterprise/fern-self-hosted:latest
    ```

    验证镜像在您的 Docker 守护进程中可用：

    ```bash
    docker images | grep fernenterprise/fern-self-hosted
    ```
  </Step>

  <Step title="创建 Dockerfile">
    在包含您的 `fern/` 文件夹的同一目录中，创建一个名为 `Dockerfile` 的文件：

    ```
    your-project/
    ├── Dockerfile
    └── fern/
        ├── fern.config.json
        ├── docs.yml
        └── ...
    ```

    将以下内容添加到 `Dockerfile` 中：

    ```dockerfile Dockerfile
    FROM fernenterprise/fern-self-hosted:latest

    COPY fern/ /fern/

    RUN fern-generate
    ```

    <Info>
      `fern-generate` 是 Docker 镜像内可用的命令，它在构建时处理您的文档，从而实现更快的容器启动、离线部署和更小的攻击面。这不是您在主机上运行的命令。您也可以选择[将生成延迟到运行时](#runtime-generation)。
    </Info>
  </Step>

  <Step title="构建您的 Docker 镜像">
    从包含您的 `Dockerfile` 和 `fern/` 文件夹的目录构建镜像：

    ```bash
    docker build -t self-hosted-docs .
    ```
  </Step>

  <Step title="运行文档">
    启动您的自托管文档：

    ```bash
    docker run -p 3000:3000 self-hosted-docs
    ```

    文档将在 [localhost:3000](http://localhost:3000) 可用。
  </Step>

  <Step title="部署文档">
    现在您可以将镜像部署到您自己的基础设施，允许您在自己的域名上托管文档。

    部署后，您可以设置[预览环境](/learn/docs/self-hosted/previews)来在每个拉取请求上预览文档更改。
  </Step>
</Steps>

## 自定义域名

您的文档使用在 `docs.yml` 文件中指定的域名。例如：

```yaml
instances:
  - url: example-org.docs.buildwithfern.com
    custom-domain: docs.plantstore.dev
```

要在运行时覆盖域名（例如，当实际主机名与 `docs.yml` 中的 `custom-domain` 不同时），请设置 `CUSTOM_DOMAIN` 环境变量：

```bash
docker run -p 3000:3000 -e CUSTOM_DOMAIN=docs.plantstore.dev self-hosted-docs
```

详细信息请参见[环境变量](#environment-variables)。

## 环境变量

通过在您的 Dockerfile 或 Kubernetes 部署中设置环境变量来配置自托管容器的行为。

### 通用

| 变量                      | 描述                                                                                                                                              | 默认值                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `CUSTOM_DOMAIN`         | 在运行时覆盖 `docs.yml` 中的 `custom-domain`。当文档实际提供服务的主机名与 `docs.yml` 中的域名不同时很有用。接受纯主机名（例如，`docs.plantstore.dev`）；任何 `https://` 或 `http://` 前缀都会被自动去掉。 | 来自 `docs.yml` 的 `custom-domain` 值 |
| `FERN_LOG_LEVEL`        | 文档生成期间 Fern CLI 的日志级别。选项：`debug`、`info`、`warn`、`error`。                                                                                         | `debug`                           |
| `NODE_MEMORY_LIMIT`     | Next.js 服务器的 Node.js 堆大小（MB）。对于有许多 API 版本的大型文档站点需要增加。                                                                                           | `4096`                            |
| `NEXT_PUBLIC_BASE_PATH` | 从子路径而不是根路径提供文档服务。值必须以 `/` 开头且没有尾部斜杠（例如，`/docs`）。详细信息请参见[基础路径](#base-path)。                                                                      | 无（从 `/` 提供服务）                     |

### 缓存预热

容器可以在启动时预取所有页面，以确保第一个真实用户请求是快速的。

| 变量               | 描述                                     | 默认值     |
| ---------------- | -------------------------------------- | ------- |
| `WARMUP`         | 设置为 `true` 以在启动时启用缓存预热。在后台运行，不会阻塞容器就绪。 | `false` |
| `WARMUP_TIMEOUT` | 预热期间每个页面请求的超时秒数。                       | `5`     |

### 缓存代理

容器包含一个位于 Next.js 服务器前面的缓存代理。

| 变量                     | 描述                                  | 默认值             |
| ---------------------- | ----------------------------------- | --------------- |
| `CACHE_MAX_ENTRIES`    | 要缓存的最大页面数。                          | `1000`          |
| `CACHE_MAX_ENTRY_SIZE` | 每个缓存条目的最大大小（字节）。                    | `5242880`（5 MB） |
| `CACHE_DEFAULT_TTL`    | 默认缓存生存时间（TTL）（秒）。                   | `2592000`（30 天） |
| `CACHE_CDN_TTL`        | 下游 CDN 缓存（例如，CloudFront）的缓存 TTL（秒）。 | `3600`（1 小时）    |
| `CACHE_DISABLED`       | 设置为 `true` 或 `1` 以完全禁用缓存。           | `false`         |
| `CACHE_PROXY_DEBUG`    | 设置为 `1` 以启用详细的缓存代理日志记录。             | `0`             |

### 跨源资源共享（CORS）代理

容器包含一个 CORS 代理，允许文档前端进行跨源请求（例如，为 API Explorer 的**试用**功能向您的 API 发送请求）。默认情况下，只允许文档域名本身。使用 `CORS_PROXY_ALLOWED_DOMAINS` 来将其他域名添加到白名单中。

| 变量                           | 描述                                | 默认值 |
| ---------------------------- | --------------------------------- | --- |
| `CORS_PROXY_ALLOWED_DOMAINS` | 通过 CORS 代理允许的根域名的逗号分隔列表。子域名会自动匹配。 | 无   |

例如，要允许对 `api.plantstore.dev` 和 `auth.plantstore.dev` 的请求：

```dockerfile
ENV CORS_PROXY_ALLOWED_DOMAINS="plantstore.dev"
```

要允许多个域名：

```dockerfile
ENV CORS_PROXY_ALLOWED_DOMAINS="plantstore.dev,partner-api.example.com"
```

### 调试

| 变量              | 描述                                                                                      | 默认值     |
| --------------- | --------------------------------------------------------------------------------------- | ------- |
| `ENABLE_JAEGER` | 设置为 `true` 以启动 [Jaeger](https://www.jaegertracing.io/) 进行分布式追踪。Jaeger UI 在端口 16686 上可用。 | `false` |

## 页面反馈

在自托管模式下，[页面反馈](/learn/docs/user-feedback)事件作为结构化 JSON 日志输出到容器的 stdout，前缀为 `[fern-docs-feedback]`。您可以在您的日志基础设施中过滤这些：

```bash
docker logs <container-id> 2>&1 | grep "\[fern-docs-feedback\]"
```

每个日志行包含一个事件名称、时间戳和一组属性：

```json
[fern-docs-feedback] {"event":"feedback_submitted","timestamp":"2026-01-15T12:34:56.789Z","properties":{"satisfied":true,"message":"Great docs!","email":"user@example.com","type":"on-page-feedback"}}
```

### 跟踪的事件

| 事件                              | 描述               |
| ------------------------------- | ---------------- |
| `feedback_voted`                | 用户点击了赞成或反对按钮。    |
| `feedback_submitted`            | 用户通过反馈表单提交了书面反馈。 |
| `code_block_feedback_submitted` | 用户报告了代码示例的问题。    |

### 属性

| 属性          | 描述                                                                         |
| ----------- | -------------------------------------------------------------------------- |
| `satisfied` | 赞成为 `true`，反对为 `false`。                                                    |
| `message`   | 用户的书面反馈消息（在 `feedback_submitted` 和 `code_block_feedback_submitted` 事件中存在）。 |
| `email`     | 用户的电子邮件地址（如果提供）。                                                           |
| `type`      | 反馈来源，例如 `on-page-feedback`。                                                |

## 其他配置

以下部分涵盖了特定部署场景的可选配置。

### 基础路径

默认情况下，自托管容器从根路径（`/`）提供文档服务。设置 `NEXT_PUBLIC_BASE_PATH` 以从子路径提供服务，例如 `/docs`。当您的文档与反向代理后面的其他应用程序共享域名时，这很有用。

值必须以 `/` 开头，不能以尾部斜杠结尾。

<Tabs>
  <Tab title="构建时（推荐）">
    在运行 `fern-generate` 之前在您的 Dockerfile 中设置 `NEXT_PUBLIC_BASE_PATH`。这会在构建时将基础路径修补到包中，因此容器可以使用只读文件系统运行。

    ```dockerfile
    FROM fernenterprise/fern-self-hosted:latest

    COPY fern/ /fern/

    ENV NEXT_PUBLIC_BASE_PATH=/docs

    RUN fern-generate
    ```
  </Tab>

  <Tab title="运行时">
    在启动容器时传递 `NEXT_PUBLIC_BASE_PATH`。基础路径在容器启动时被修补到包中。

    ```bash
    docker run -p 3000:3000 -e NEXT_PUBLIC_BASE_PATH=/docs self-hosted-docs
    ```

    <Warning>
      运行时修补会在启动时修改容器内的文件，因此不兼容 Kubernetes 中的 `readOnlyRootFilesystem: true`。如果您的安全上下文需要只读根文件系统，请使用构建时修补。
    </Warning>
  </Tab>
</Tabs>

使用 `NEXT_PUBLIC_BASE_PATH=/docs`，文档可在 `http://localhost:3000/docs` 而不是 `http://localhost:3000/` 访问。

<Info>
  构建时**不**使用 `NEXT_PUBLIC_BASE_PATH` 的单个 Docker 镜像可以在运行时配置为从任何路径提供服务。这让您可以在可能需要不同基础路径的环境中重用一个镜像。
</Info>

### 运行时生成

默认情况下，`fern-generate` 在 Docker 构建时运行。如果您需要以下功能，可以将生成延迟到运行时：

* 在运行时传递配置（环境变量、密钥）
* 在开发期间加速 Docker 构建
* 在多个文档配置间共享单个镜像

使用 `--only-deps` 标志将生成延迟到运行时：

```dockerfile
FROM fernenterprise/fern-self-hosted:latest

COPY fern/ /fern/

RUN fern-generate --only-deps
```

这会在构建时启动所需的服务（PostgreSQL、MinIO、FDR），但跳过文档生成。当容器启动时，它会自动运行 `fern generate --docs`。

<Warning>
  运行时生成需要在容器启动时访问网络。对于离线部署，请使用默认的构建时生成。
</Warning>

### 使用 gRPC 的离线部署

如果您的 API 使用 gRPC 并具有来自 [Buf Schema Registry](https://buf.build)（BSR）的依赖项，`buf` CLI 会在生成期间从 `buf.build` 获取模块。这在没有网络访问的离线环境中会失败。

<Steps>
  <Step title="检查 BSR 依赖项">
    检查您的项目在任一位置是否有 BSR 依赖项：

    **在 `buf.yaml` 中：**

    ```yaml
    version: v2
    deps:
      - buf.build/googleapis/googleapis
      - buf.build/grpc-ecosystem/grpc-gateway
    ```

    **在 `generators.yml` 中：**

    ```yaml
    api:
      specs:
        - proto:
            root: ./protos/
            dependencies:
              - buf.build/googleapis/googleapis
    ```

    如果没有 `deps` 或 `dependencies` 部分（或只有本地路径），您可以跳过本节的其余部分。
  </Step>

  <Step title="选择解决方案">
    <AccordionGroup>
      <Accordion title="选项 1：构建时生成（推荐）" defaultOpen>
        在网络访问可用时在构建时运行 `fern-generate`：

        ```dockerfile
        FROM fernenterprise/fern-self-hosted:latest

        COPY fern/ /fern/

        RUN fern-generate
        ```

        这会在 Docker 构建期间下载 BSR 依赖项并将其嵌入到镜像中。运行时不需要网络访问。
      </Accordion>

      <Accordion title="选项 2：为运行时生成供应商依赖项">
        当您在构建时没有所有信息且需要文档在运行时生成不同的内容时使用此选项，例如在运行时注入环境变量。

        要在离线环境中在运行时生成，请在本地供应商 buf 依赖项：

        ```dockerfile
        FROM fernenterprise/fern-self-hosted:latest

        # 安装 buf CLI 用于依赖项缓存
        RUN npm install -g @bufbuild/buf

        # 复制 fern 配置
        COPY fern/ fern/
        COPY protos/ protos/

        # 在构建时预取 buf 依赖项（缓存 googleapis、protovalidate）
        RUN cd protos && buf dep update

        # 构建 fern 依赖项
        RUN fern-generate --only-deps
        ```

        更新 `buf.yaml` 以引用供应商依赖项：

        ```yaml
        # 之前
        deps:
          - buf.build/googleapis/googleapis

        # 之后
        deps:
          - ./vendor/googleapis
        ```

        <Info>
          有关更多详细信息，请参见 [Buf 依赖项管理文档](https://buf.build/docs/bsr/module/dependency-management)。
        </Info>
      </Accordion>

      <Accordion title="选项 3：在 generators.yml 中指定依赖项">
        如果您没有 `buf.yaml` 文件，您可以直接在您的 `generators.yml` 中指定 proto 依赖项。自托管容器在构建过程中会自动从这些依赖项创建临时的 `buf.yaml`。

        ```yaml generators.yml
        api:
          specs:
            - proto:
                root: ./protos/
                dependencies:
                  - buf.build/googleapis/googleapis
                  - buf.build/bufbuild/protovalidate
        ```

        这种方法适用于构建时和运行时生成：

        ```dockerfile
        FROM fernenterprise/fern-self-hosted:latest

        COPY fern/ /fern/

        # 对于构建时生成（推荐用于离线部署）
        RUN fern-generate

        # 或者对于运行时生成（启动时需要网络）
        # RUN fern-generate --only-deps
        ```

        容器会解析您的 fern 目录中的所有 `generators.yml` 文件，找到具有依赖项但没有 `buf.yaml` 的 proto 规范，并自动创建必要的配置。
      </Accordion>
    </AccordionGroup>
  </Step>
</Steps>

### Kubernetes 部署

这是一个示例 Deployment 和 Service 配置。将 `your-registry/fern-docs:latest` 替换为您的镜像名称。

应用配置：

```bash
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
```

**deployment.yaml：**

```yaml deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fern-docs
  labels:
    app: fern-docs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fern-docs
  template:
    metadata:
      labels:
        app: fern-docs
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 65532
        runAsGroup: 65532
        fsGroup: 65532
        fsGroupChangePolicy: OnRootMismatch
      containers:
        - name: fern-docs
          image: your-registry/fern-docs:latest
          imagePullPolicy: IfNotPresent
          ports:
            - name: docs
              containerPort: 3000
              protocol: TCP
            - name: health
              containerPort: 8081
              protocol: TCP
          resources:
            requests:
              memory: "2Gi"
              cpu: "1000m"
            limits:
              memory: "4Gi"
              cpu: "2000m"
          livenessProbe:
            httpGet:
              path: /liveness
              port: 8081
            initialDelaySeconds: 120
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /readiness
              port: 8081
            initialDelaySeconds: 60
            periodSeconds: 5
            timeoutSeconds: 5
            failureThreshold: 6
          securityContext:
            allowPrivilegeEscalation: false
            runAsNonRoot: true
            runAsUser: 65532
            runAsGroup: 65532
            privileged: false
            readOnlyRootFilesystem: false
            capabilities:
              drop:
                - ALL
      terminationGracePeriodSeconds: 30
```

**service.yaml：**

```yaml service.yaml
apiVersion: v1
kind: Service
metadata:
  name: fern-docs
  labels:
    app: fern-docs
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 3000
      protocol: TCP
      nodePort: 30080
  selector:
    app: fern-docs
```

有关健康检查端点的详细信息，请参见[健康检查端点](/learn/docs/self-hosted/health-check-endpoints)。