> For the complete documentation index, see [llms.txt](https://ayakaleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ayakaleaf-pro.ayaka.space/on-premises/zh-cn/pei-zhi/overleaf-toolkit/sandboxed-compiles.md).

# 沙盒编译

Overleaf Pro 提供在安全沙箱环境中运行编译的选项，以满足企业安全需求。它通过在各自独立的安全 Docker 环境中运行每个项目来实现这一点。

### 安全性提升

由于许多 LaTeX 文档在 PDF 编译过程中需要/能够执行任意 shell 命令，因此对于 Server Pro，推荐使用沙箱化编译。如果使用沙箱化编译，每次编译都会在一个独立的 Docker 容器中运行，该容器的能力受限，且不与任何其他用户或项目共享，并且无法访问外部资源，例如宿主网络。

{% hint style="warning" %}
如果你尝试运行 Overleaf Pro **不使用** 沙箱化编译，编译会与其他并发编译一起运行在主 Docker 容器中，用户对 `sharelatex` 容器资源（文件系统、网络和环境变量）具有完全的读写权限，用于运行 LaTeX 编译。
{% endhint %}

### 更容易的包管理

为了避免手动安装软件包，我们建议启用沙箱化编译。这是 Server Pro 中的一个可配置设置，它将为你的用户提供与 overleaf.com 上相同的 TeX Live 环境，但运行在你自己的本地安装中。沙箱化编译使用的 TeX Live 镜像包含最常见的软件包和字体，并已针对我们的图库模板进行测试，从而确保与本地项目的最大兼容性。

启用沙箱化编译后，你可以配置用户在项目中可选择的 TeX Live 版本，并为新项目设置默认的 TeX Live 镜像版本。

{% hint style="info" %}
如果你尝试在不启用沙箱化编译的情况下运行 Overleaf Pro，你的实例将默认使用基础方案版本的 TeX Live 进行编译。这个基础版本非常轻量，只包含极少量的 LaTeX 软件包，因此很可能会导致用户缺少软件包的错误，尤其是在他们尝试使用预构建模板时。
{% endhint %}

由于 Overleaf Pro 的架构设计为可离线工作，因此没有自动化方式将 overleaf.com 的图库模板集成到你的本地安装中；不过，可以按模板逐个手动执行。有关其工作方式的更多信息，请查看我们的从 overleaf.com 迁移模板指南： [/pages/e42e04669307f188ae50be46e58b26ed98f1f059#transferring-templates-from-overleaf.com](https://ayakaleaf-pro.ayaka.space/on-premises/zh-cn/pei-zhi/overleaf-toolkit/pages/e42e04669307f188ae50be46e58b26ed98f1f059#transferring-templates-from-overleaf.com "mention").

{% hint style="info" %}
沙箱化编译需要 `sharelatex` 容器可以通过绑定挂载访问宿主机上的 Docker socket，以便它管理这些同级编译容器。
{% endhint %}

## 工作原理

启用沙箱化编译后，Docker socket 将从宿主机挂载到 `sharelatex` 容器中，这样容器内的编译服务就可以在宿主机上创建新的 Docker 容器。然后，对于每个项目中的每次编译运行，LaTeX 编译服务（CLSI）将执行以下操作：

* 将项目文件写入 `OVERLEAF_DATA_PATH`.
* 下的某个位置，并使用挂载的 Docker socket 创建一个新的 `texlive` 容器用于本次编译。
* 让 `texlive` 容器从 `OVERLEAF_DATA_PATH`.
* 下的路径读取项目数据，并在 `texlive` 容器内部编译项目。

### 启用沙箱化编译

#### 适用于 Toolkit 用户

要启用沙箱化编译（也称为同级容器），请在 `overleaf-toolkit/config/overleaf.rc`:

{% code title="config/overleaf.rc" %}

```dotenv
SERVER_PRO=true
SIBLING_CONTAINERS_ENABLED=true
```

{% endcode %}

#### 适用于 Docker Compose 用户 <a href="#docker-compose-example" id="docker-compose-example"></a>

{% hint style="danger" %}
从 Overleaf CE/Server Pro `5.0.3` 开始，环境变量已从 `SHARELATEX_*` 重命名为 `OVERLEAF_*`.
{% endhint %}

如果你使用的是 `4.x` 版本（或更早版本），请确保变量前缀相应正确（例如 `SHARELATEX_MONGO_URL` 而不是 `OVERLEAF_MONGO_URL`).

<pre class="language-yml"><code class="lang-yml">version: '2'
services:
    sharelatex:
        #...
        volumes:
            - /data/overleaf_data:/var/lib/overleaf
<strong>            - /var/run/docker.sock:/var/run/docker.sock
</strong>        environment:
            #...
<strong>            DOCKER_RUNNER: "true"
</strong><strong>            SANDBOXED_COMPILES: "true"
</strong><strong>            SANDBOXED_COMPILES_HOST_DIR: "/data/overleaf_data/data/compiles"
</strong>            #...
        #...
</code></pre>

### 更改 TexLive 镜像

{% hint style="info" %}
对于中国大陆用户，你可以使用 `ghcr.nju.edu.cn` 来加速下载。
{% endhint %}

Overleaf Pro 使用三个环境变量来确定沙箱化编译所使用的 TeX Live 镜像：

* `TEX_LIVE_DOCKER_IMAGE` **（必需），** 用于编译新项目的默认 TeX Live 镜像。此镜像必须包含在 `ALL_TEX_LIVE_DOCKER_IMAGES`.
* `ALL_TEX_LIVE_DOCKER_IMAGE_NAMES` **（必需），** 用于前端选项的镜像友好名称列表，以逗号分隔。
* `ALL_TEX_LIVE_DOCKER_IMAGES` **（必需），** 要使用的 TeX Live 镜像列表，以逗号分隔。如果使用 Overleaf Toolkit 进行部署，这些镜像将被下载或更新。若要跳过下载，请设置 `SIBLING_CONTAINERS_PULL=false` 在 `config/overleaf.rc`.

当你使用 `bin/up` 命令启动 Overleaf Pro 实例时，Toolkit 将自动拉取 `ALL_TEX_LIVE_DOCKER_IMAGES`.

下面是一个示例：我们将新项目默认设置为 TeX Live 2025，并让现有项目继续使用 2024。

{% tabs %}
{% tab title="常见安装" %}
以下配置会安装 2025 到 2026 的所有完整 TeX Live Docker 镜像。在使用此配置之前，我们建议至少准备 80 GB 的可用存储空间。

{% code title="config/variables.env" overflow="wrap" %}

```dotenv
ALL_TEX_LIVE_DOCKER_IMAGES=ghcr.io/ayaka-notes/texlive-full:2026.1, ghcr.io/ayaka-notes/texlive-full:2025.1
ALL_TEX_LIVE_DOCKER_IMAGE_NAMES=Texlive 2026, Texlive 2025
TEX_LIVE_DOCKER_IMAGE=ghcr.io/ayaka-notes/texlive-full:2026.1
```

{% endcode %}
{% endtab %}

{% tab title="完整安装" %}
以下配置会安装 2020 到 2026 的所有完整 TeX Live Docker 镜像。在使用此配置之前，我们建议至少准备 256 GB 的可用存储空间。

{% code title="config/variables.env" overflow="wrap" %}

```dotenv
ALL_TEX_LIVE_DOCKER_IMAGES=ghcr.io/ayaka-notes/texlive-full:2026.1,ghcr.io/ayaka-notes/texlive-full:2025.1,ghcr.io/ayaka-notes/texlive-full:2024.1,ghcr.io/ayaka-notes/texlive-full:2023.1,ghcr.io/ayaka-notes/texlive-full:2022.1,ghcr.io/ayaka-notes/texlive-full:2021.1,ghcr.io/ayaka-notes/texlive-full:2020.1
ALL_TEX_LIVE_DOCKER_IMAGE_NAMES=Texlive 2026,Texlive 2025,Texlive 2024,Texlive 2023,Texlive 2022,Texlive 2021,Texlive 2020
TEX_LIVE_DOCKER_IMAGE=ghcr.io/ayaka-notes/texlive-full:2026.1
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="danger" %}
强烈建议设置 **至少 2 个 texlive-full 镜像**。详细原因请参见 [#known-issues](#known-issues "mention")
{% endhint %}

### 可用的 TeX Live 镜像

这些是为 Overleaf 专门优化的一系列 TeX Live 镜像，也可以添加到 `TEX_LIVE_DOCKER_IMAGE` 和 `ALL_TEX_LIVE_DOCKER_IMAGES`:

* `ghcr.io/ayaka-notes/texlive-full:2026.1` （也包括 `latest` 标签）
* `ghcr.io/ayaka-notes/texlive-full:2025.1`
* `ghcr.io/ayaka-notes/texlive-full:2024.1`
* `ghcr.io/ayaka-notes/texlive-full:2023.1`
* `ghcr.io/ayaka-notes/texlive-full:2022.1`
* `ghcr.io/ayaka-notes/texlive-full:2021.1`
* `ghcr.io/ayaka-notes/texlive-full:2020.1`

{% hint style="warning" %}
关于镜像 **必须** 标记的方式有严格的规范（适用以下正则表达式 `^[0-9]+.[0-9]+`，其中第一个数字决定 TeX Live 年份，第二个数字决定补丁版本）。
{% endhint %}

### 我可以使用其他镜像仓库吗

> 有些人可能会想，是否可以用另一个镜像站替换 `ghcr.io` ，或者把 texlive 切换为 Docker Hub 上的其他镜像？

不建议这样做，因为配置相对复杂。如果你是从镜像站下载的，可以将镜像重命名为 `ghcr.io/ayaka-notes/texlive-full`.

但是，如果你确实想使用自己的镜像仓库，请添加：

{% code title="config/variables.env" overflow="wrap" %}

```dotenv
IMAGE_ROOT=hub.your.com/your-repo
```

{% endcode %}

然后，你需要确保 texlive 的所有镜像都在 `your-repo`中，例如

* `hub.your.com/your-repo/texlive-full:2025.1`
* `hub.your.com/your-repo/texlive-full:2024.1`

有关详细信息，请阅读下面的源代码，以了解我们如何解析你的环境变量：

{% code title="sandboxed-compiles/index.mjs" overflow="wrap" expandable="true" %}

```mjs
if (process.env.SANDBOXED_COMPILES === 'true') {
  // 如果未提供，则设置默认镜像根目录
  let imageRootPath = process.env.IMAGE_ROOT || "ghcr.io/ayaka-notes";
  // 将 imageRoot 导出到 Settings
  Settings.imageRoot = imageRootPath

  // allowedImageNames 应该是：
  // [
  //  { imageName: "texlive-2023:latest", imageDesc: "TeX Live 2023" },
  //  { imageName: "texlive-2022:latest", imageDesc: "TeX Live 2022" },
  // ]
  Settings.allowedImageNames = parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGES)
    .map((texImage, index) => ({
      imageName: texImage.split("/")[texImage.split("/").length - 1],
      imageDesc: parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES)[index]
        || texImage.split(':')[1],
    }))
  
  // 最终，imageName 会与 imageRoot 组合，形成完整的镜像路径
  // 完整名称将类似于：ghcr.io/ayaka-notes/texlive-2023:latest

  // 如果未提供，则设置默认镜像名称
  if(!process.env.TEX_LIVE_DOCKER_IMAGE) {
    process.env.TEX_LIVE_DOCKER_IMAGE = imageRootPath + "/" + Settings.allowedImageNames[0].imageName
  }

  // 将 currentImageName 导出到 Settings
  // 这是新创建项目的镜像名称
  Settings.currentImageName = process.env.TEX_LIVE_DOCKER_IMAGE
}
```

{% endcode %}

### 已知问题

这是来自 overleaf 社区的一个真实案例：

> 使用 `6.0.1-ext-v3.3`时，我在 `variables.env`:
>
> ```dotenv
> TEX_LIVE_DOCKER_IMAGE=texlive/texlive:latest-full
> ALL_TEX_LIVE_DOCKER_IMAGES=texlive/texlive:latest-full
> ```
>
> 这在 `texlive/texlive:latest-full`下工作正常。不过，我又拉取了另一个 texlive 镜像 `danteev/texlive:2025-10-15` 并将这两个变量都改成了新的镜像名称，但它不起作用：
>
> ```dotenv
> TEX_LIVE_DOCKER_IMAGE=danteev/texlive:2025-10-15
> ALL_TEX_LIVE_DOCKER_IMAGES=danteev/texlive:2025-10-15
> ```
>
> 在日志中，我看到如下内容：
>
> {% code overflow="wrap" %}
>
> ```
> {"name":"clsi","level":50,"err":{"message":"(HTTP code 404) no such container - No such image: texlive/texlive:latest-full ","name":"Error","stack":"Error: (HTTP code 404) no such container - No such image: texlive/texlive:latest-full ... 
> ```
>
> {% endcode %}
>
> 看起来 `variables.env` 中更新后的设置并没有生效。编译仍然尝试运行 `texlive/texlive:latest-full` 镜像，而不是新镜像。
>
> 我尝试重启、删除容器并重新运行，但问题仍然一样。
>
> 有什么解决方案吗？

由于一些技术限制，如果你只设置了一个 Docker TeXLive 镜像，例如 `texlive-fullA:latest`

```
ALL_TEX_LIVE_DOCKER_IMAGES=texlive/texliveA:latest-full
ALL_TEX_LIVE_DOCKER_IMAGE_NAMES=TeXLiveA
TEX_LIVE_DOCKER_IMAGE=texlive/texliveA:latest-full
```

在运行了一段时间的 Overleaf 实例后，你可能想把 TeXLive 镜像修改为 `texlive-fullB:latest`。这时你会发现用户无法编译所有项目。

```
ALL_TEX_LIVE_DOCKER_IMAGES=texlive/texliveA:latest-full
ALL_TEX_LIVE_DOCKER_IMAGE_NAMES=TeXLiveA
TEX_LIVE_DOCKER_IMAGE=texlive/texliveA:latest-full
```

这是因为每个项目中的 TeXLive-Full 镜像名称（用于沙箱编译）会持久化存储在数据库中。 *只有当用户切换其项目的 TeXLive 版本时，例如从 2024 切换到 2025，数据库中的镜像名称才会更改*.

当 CLSI 编译项目时，它会直接使用数据库中找到的容器镜像名称来编译该项目。

如果你只提供一个 Docker 镜像，用户将无法修改用于编译项目的镜像。在这种情况下，你需要编写一个脚本来 **手动修改** 所有用户项目在 mongoDB 中的 TeXLive 镜像。

### 调试

运行以下命令以从 toolkit 中检查 clsi 日志：

{% code overflow="wrap" %}

```bash
bin/logs clsi
```

{% endcode %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ayakaleaf-pro.ayaka.space/on-premises/zh-cn/pei-zhi/overleaf-toolkit/sandboxed-compiles.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
