编辑
2025-09-17
The Forge
00

目录

0. 前言
1. 安装Docker
2. 部署Overleaf CE
3. 进阶
3.1 配置SMTP服务
3.2 容灾
4. Appendix

0. 前言

由官方提供的Overleaf免费服务近期再次缩短编译时长,限制已降至10秒,这为用户日常使用带来了显著不便。虽然市面上还有不少Overleaf的替代方案,但自部署Overleaf镜像能在最大程度上提供更无缝的编辑体验。本文记录在 Ubuntu 24.04 服务器上通过 Docker 部署 Overleaf 的完整流程,旨在为需要自建服务的用户提供参考。

Overleaf Community Edition (Overleaf CE) 提供两种本地部署方式:

  • 使用 Overleaf Toolkit 进行一键部署:优点在于操作简单,只需几步就能完成部署;缺点封装比较完善、自由度低。
  • 使用 Docker镜像 部署:优点是使用docker原生部署,熟悉docker部署流程的同学能够拥有更大的自由度。

注意

本文只介绍使用Docker镜像部署的全流程,使用镜像部署的目的在于定制TexLive版本。Toolkit默认安装TexLive 2025,而Overleaf在线版使用的是TexLive 2024,为了保证编译的顺畅进行,希望CE使用的TexLive与Online保持一致。因此如果你不需要自定义TexLive版本,那么你完全可以从“正式开始构建Overleaf CE”开始

1. 安装Docker

Docker官方提供了不同的几种安装Docker的方式,以下从Apt源安装

bash
# Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update # Install the latest Version sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Docker在安装完成后默认启动,可以使用以下方式验证Docker服务是否运行 sudo systemctl status docker # 部分系统禁用了该功能,此时可使用以下命令启动Docker服务 sudo systemctl start docker # 最后,使用`hello-world`镜像验证docker安装 sudo docker run hello-world # 你应该看到 Hello from Docker! \ This message shows that your installation appears to be working correctly # 如果你不想每次使用docker命令都需要sudo权限,先检查是否存在docker用户组,通常会自动生成 grep docker /etc/group # 将当前用户添加到用户组 sudo usermod -aG docker $USER

2. 部署Overleaf CE

使用以下命令克隆Overleaf仓库

bash
git clone https://github.com/overleaf/overleaf.git

其中server-ce目录下包含构建镜像用到的DockerfileDockerfile-base,后者专门用于构建基础镜像,这个基础镜像包含了所有不常变动的基础设施,正如官方在README中说的

We split this out because it's a pretty heavy set of dependencies, and it's nice to not have to rebuild all of that every time.

首先编辑Dockerfile-base以修改安装的TexLive版本

# Install TexLive # --------------- # ... # 在这里直接修改镜像源地址,例如使用南京大学Docker源,注意历史版本位于tex-historic的tlnet-final下 ARG TEXLIVE_MIRROR=https://mirror.nju.edu.cn/tex-historic/systems/texlive/2024/tlnet-final # 仅仅修改镜像源地址是不够的,默认状态下,下面几行会执行安全验证 RUN mkdir /install-tl-unx \ # 请注释指定的语句 # 下载当前最新的GPG公钥texlive.asc && wget --quiet https://tug.org/texlive/files/texlive.asc \ <-注释 && gpg --import texlive.asc \ <-注释 && rm texlive.asc \ <-注释 && wget --quiet ${TEXLIVE_MIRROR}/install-tl-unx.tar.gz \ && wget --quiet ${TEXLIVE_MIRROR}/install-tl-unx.tar.gz.sha512 \ # 下载TexLive 2024的签名文件install-tl-unx.tar.gz.sha512.asc && wget --quiet ${TEXLIVE_MIRROR}/install-tl-unx.tar.gz.sha512.asc \ <-注释 # 使用当前最新的GPG公钥texlive.asc对进行验证,这显然是不可能通过的 && gpg --verify install-tl-unx.tar.gz.sha512.asc \ <-注释 && sha512sum -c install-tl-unx.tar.gz.sha512 \ && tar -xz -C /install-tl-unx --strip-components=1 -f install-tl-unx.tar.gz \ && rm install-tl-unx.tar.gz* \ && echo "tlpdbopt_autobackup 0" >> /install-tl-unx/texlive.profile \ && echo "tlpdbopt_install_docfiles 0" >> /install-tl-unx/texlive.profile \ && echo "tlpdbopt_install_srcfiles 0" >> /install-tl-unx/texlive.profile \ # 在构建base镜像时建议修改这行的scheme-basic为scheme-full,安装完整版TexLive,避免后续升级的麻烦 && echo "selected_scheme scheme-basic" >> /install-tl-unx/texlive.profile \

接下来正式开始构建Overleaf CE镜像

bash
# 首先安装make软件包 sudo apt update sudo apt install make # 验证安装 make --version # 先确保你位于server-ce/目录下 cd server-ce # 执行基础镜像的构建命令 make build-base # 在构建完成后,你应该就能使用以下命令查看到刚刚构建好的base镜像 docker images # 输出类似于 # 值得注意的是,overleaf镜像名称实际上是sharelatex,这是overleaf合并sharelatex的历史原因造成的 # 另外,同一个镜像被打上两个TAG,这是正常现象,符合Makefile中的定义 REPOSITORY TAG IMAGE ID ... sharelatex/sharelatex-base main 757806628bdc ... sharelatex/sharelatex-base main-16422f972bb4da0833be87675a3d34fe694d0941 757806628bdc ... # 接下来构建CE镜像 make build-community # 在构建完成后 docker images # 输出类似于 REPOSITORY TAG IMAGE ID CREATED SIZE sharelatex/sharelatex main f2ad213cc75d ... sharelatex/sharelatex main-644db1722e8cce6bc85ea794156910928375cc0c f2ad213cc75d ... sharelatex/sharelatex-base main a089e357f3a7 ... sharelatex/sharelatex-base main-644db1722e8cce6bc85ea794156910928375cc0c a089e357f3a7 ... # 返回overleaf根目录 cd .. # 目录正确的话你应该能看到docker-compose.yml ls docker-compose.yml # 建议新建一个统一的数据目录 mkdir data_folders # 并在docker-compose.yml分别修改sharelatex mongo 和 redis 容器的数据目录位置,例如 volumes: - ./data_folers/sharelatex_data:/var/lib/overleaf volumes: - ./data_folers/mongo_data:/data/db volumes: - ./data_folers/redis_data:/data # 启动服务 docker-compose up

警告

首次启动服务时不要使用-d参数,你必须密切关注控制台输出

理想情况下,由于已经构建好sharelatex镜像,不应该再从Docker源拉取,因此如果你看到以下内容,请使用Ctrl + C终止进程

bash
# 拉取 mongo 和 redis 是正常现象,无需在意 [+] Running 82/87 ⠏ mongo [⣿⣿⣿⣿⣿⣿⣿⣿] 265.9MB / 265.9MB Pulling 37.0s ✔ redis Pulled # sharelatex 已经在本地构建好,绝不应该再从镜像源拉取 17.5s ⠏ sharelatex [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷] 877.3MB / 902.9MB Pulling

这是由于docker-compose.yml

yml
services: sharelatex: restart: always # 未指定为我们定制化后的sharelatex镜像 image: sharelatex/sharelatex # 使用docker images查看你的镜像,并将上述内容修改为 # image: sharelatex/sharelatex:{你的TAG} # 示例 # image: sharelatex/sharelatex:main-644db1722e8cce6bc85ea794156910928375cc0c ...

重新启动服务

bash
# 同样,你不应该使用-d参数,在下面的日志中不应该出现sharelatex/sharelatex ... Pulling docker-compose up # 容器运行后新开一个bash,使用以下命令检查正在运行的容器其TAG是否正确 docker ps

现在,访问http://localhost/launchpad即可创建管理员账户

首次登录编译时会遇到编译失败的问题,请将docker-compose.yml中关于启用沙箱编译的配置全部注释,这一点在yaml文件内也有对应提示

yml
################ ## Server Pro ## ################ ## The Community Edition is intended for use in environments where all users are trusted and is not appropriate for ## scenarios where isolation of users is required. Sandboxed Compiles are not available in the Community Edition, ## so the following environment variables must be commented out to avoid compile issues. ## ## Sandboxed Compiles: https://docs.overleaf.com/on-premises/configuration/overleaf-toolkit/server-pro-only-configuration/sandboxed-compiles # SANDBOXED_COMPILES: 'true' ### Bind-mount source for /var/lib/overleaf/data/compiles inside the container. # SANDBOXED_COMPILES_HOST_DIR_COMPILES: '/home/user/sharelatex_data/data/compiles' ### Bind-mount source for /var/lib/overleaf/data/output inside the container. # SANDBOXED_COMPILES_HOST_DIR_OUTPUT: '/home/user/sharelatex_data/data/output' ### Backwards compatibility (before Server Pro 5.5) # DOCKER_RUNNER: 'true' # SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true'

3. 进阶

3.1 配置SMTP服务

Overleaf CE注册由管理员完成,允许管理员配置SMTP服务向用户发送注册邮件,所有配置项都位于docker-compose.yml文件

提示

首先注册网易云163邮箱,开启SMTP服务并记录SMTP密钥

# Overleaf CE可以使用AWS和SMTP服务完成邮件发送,这里我们使用SMTP服务 # 注意这一行一定要填,我怀疑它还起到了一个开启Email功能的作用 # OVERLEAF_EMAIL_FROM_ADDRESS: "hello@example.com" # OVERLEAF_EMAIL_AWS_SES_ACCESS_KEY_ID: # OVERLEAF_EMAIL_AWS_SES_SECRET_KEY: # OVERLEAF_EMAIL_SMTP_HOST: smtp.example.com # OVERLEAF_EMAIL_SMTP_PORT: 587 # OVERLEAF_EMAIL_SMTP_SECURE: false # OVERLEAF_EMAIL_SMTP_USER: # OVERLEAF_EMAIL_SMTP_PASS: # OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true # OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false # OVERLEAF_EMAIL_SMTP_NAME: '127.0.0.1' # OVERLEAF_EMAIL_SMTP_LOGGER: true # OVERLEAF_CUSTOM_EMAIL_FOOTER: "This system is run by department x" # 以网易云163免费邮的SMTP服务为例 OVERLEAF_EMAIL_SMTP_HOST: smtp.163.com OVERLEAF_EMAIL_SMTP_PORT: 465 OVERLEAF_EMAIL_SMTP_SECURE: true OVERLEAF_EMAIL_SMTP_USER: "XXXXX@163.com" OVERLEAF_EMAIL_SMTP_PASS: "这里是SMTP服务的密钥,而不是登录密码" OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false OVERLEAF_EMAIL_SMTP_NAME: 'Overleaf-XXX' OVERLEAF_EMAIL_SMTP_LOGGER: true OVERLEAF_CUSTOM_EMAIL_FOOTER: "可用HTML元素深度定制"

修改完成后一定要记得重启容器来让新的环境变量生效

bash
docker-compose down && docker-compose up -d

3.2 容灾

作为本地部署的Overleaf实例,灾难应对是避不开的话题,要是你的同学们在使用CE时出现意外情况导致数据丢失,后果可想而知。为了让咱们能睡个好觉,容器数据自动备份任务是必不可少的。

根据官方指南,我们可以确定必须参与备份的文件和文件夹如下

  • sharelatex 数据目录
  • redis 数据目录
  • mongo 数据目录
  • docker-compose.yml

提示

请先检查各数据目录的权限是否正确

  • mongo 和 redis 数据目录所有者uid应为999
  • sharelatex 数据目录所有者应为33
bash
ubuntu:~/overleaf/data_folers$ ll -n 总计 36 drwxr-xr-x 5 0 0 4096 Sep 10 22:38 ./ drwxrwxr-x 12 1001 1001 4096 Sep 10 20:14 ../ drwxr-xr-x 5 999 999 20480 Sep 17 15:27 mongo_data/ drwxr-xr-x 2 999 999 4096 Sep 17 04:00 redis_data/ drwxr-xr-x 4 33 33 4096 Sep 10 21:58 sharelatex_data/ # 如果权限不对的话 # sharelatex_data 的所有权,递归地交给 www-data 用户 (UID 33) sudo chown -R 33:33 ~/sharelatex_data # mongo_data 的所有权,递归地交给 mongodb 用户 (UID 999) sudo chown -R 999:999 ~/mongo_data # redis_data 的所有权,递归地交给 redis 用户 (UID 999) sudo chown -R 999:999 ~/redis_data

备份脚本最好挂载到root用户的crontab下,假设我们的备份脚本名为run_backup.sh

bash
# 切换root用户 sudo -i # 编辑自动任务 sudo crontab -e # 添加这行 # 第0分钟 凌晨3点 每天 每月 每周 执行run.backup.sh 输出重定向到backup_cron.log 0 3 * * * xxx/run_backup.sh >> xxxx/overleaf_backups/backup_cron.log 2>&1

相对应的,我们需要一个恢复脚本,用于将容器恢复到某次备份时的状态,这里有一个注意点就是最好在脚本最后添加权限修正的部分。

4. Appendix

sh
# ================================================================= # == Overleaf Community Edition - 自动化备份脚本 == # ================================================================= # 备份文件存储目录,最好是异地存储 BACKUP_DIR="xxx/Overleaf_Backups" # 数据目录根目录 SOURCE_DIR="xxx/overleaf/data_folers" # docker-compose.yml文件所在的目录 COMPOSE_DIR="xxx/overleaf" # 有效期,超过有效期的备份将自动删除 RETENTION_DAYS=14 # 生成带时间戳的文件名 TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") FILENAME="overleaf-backup-$TIMESTAMP.tar.gz" BACKUP_PATH="$BACKUP_DIR/$FILENAME" echo "=========================================" echo "Starting Overleaf Backup: $(date)" echo "=========================================" # 确保备份目录存在 mkdir -p $BACKUP_DIR # 停止服务以确保数据一致性 echo "[Step 1/5] Stopping Overleaf services..." cd $COMPOSE_DIR docker compose down if [ $? -ne 0 ]; then echo "Error: Failed to stop Overleaf services. Aborting backup." exit 1 fi echo "=> Services stopped successfully." # 创建备份压缩包 echo "[Step 2/5] Creating backup archive: $FILENAME" tar -czf $BACKUP_PATH \ -C $SOURCE_DIR sharelatex_data \ -C $SOURCE_DIR mongo_data \ -C $SOURCE_DIR redis_data \ -C $COMPOSE_DIR docker-compose.yml if [ $? -ne 0 ]; then echo "Error: Failed to create tar archive. Aborting backup." # 备份失败, 尝试重启服务 docker compose up -d exit 1 fi echo "=> Archive created successfully at $BACKUP_PATH" # 重启服务 echo "[Step 3/5] Restarting Overleaf services..." docker compose up -d if [ $? -ne 0 ]; then echo "Error: Failed to restart Overleaf services. Please check manually." exit 1 fi echo "=> Services restarted successfully." # 清理旧的备份 echo "[Step 4/5] Cleaning up old backups (older than $RETENTION_DAYS days)..." find $BACKUP_DIR -name "overleaf-backup-*.tar.gz" -mtime +$RETENTION_DAYS -delete echo "=> Cleanup complete." echo "=========================================" echo "Overleaf Backup Finished Successfully!" echo "=========================================" exit 0
sh
#!/bin/bash # ================================================================= # == Overleaf Community Edition - 灾难恢复脚本 == # ================================================================= # # !! 警告 !! # 此脚本将彻底删除现有的 Overleaf 数据,并从指定的备份文件中恢复。 # 这是一个具有破坏性的操作,请在执行前再三确认。 # # 使用方法: # sudo ./restore_overleaf.sh /path/to/your/overleaf-backup-file.tar.gz # # ================================================================= # --- 配置区 (请根据你的实际情况修改) --- # 1. Overleaf 数据应该存放的根目录 DATA_ROOT="xxx/overleaf/data_folers" # 2. docker-compose.yml 文件所在的目录 COMPOSE_DIR="xxx/overleaf" LOG_DIR="xxx/Overleaf_Backups/log" LOG_FILE="$LOG_DIR/restore_activity.log" # 检查是否以 root 身份运行 if [ "$(id -u)" -ne 0 ]; then echo "!! 错误: 此脚本必须以 root 用户或使用 sudo 运行。" exit 1 fi # 确保日志目录存在 mkdir -p "$LOG_DIR" echo -e "\n\n=======================================================" >> "$LOG_FILE" echo "=== Starting New Restore Session at $(date) ===" >> "$LOG_FILE" echo "=======================================================" >> "$LOG_FILE" exec > >(tee -a "$LOG_FILE") 2>&1 # 检查是否提供了备份文件路径作为参数 BACKUP_FILE=$1 if [ -z "$BACKUP_FILE" ]; then echo "!! 错误: 请提供备份文件的完整路径作为第一个参数。" echo "用法: sudo $0 /path/to/your/backup.tar.gz" exit 1 fi # 检查备份文件是否存在 if [ ! -f "$BACKUP_FILE" ]; then echo "!! 错误: 找不到指定的备份文件: $BACKUP_FILE" exit 1 fi # 最后的确认,给用户一个反悔的机会 echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo "!! 警告:危险操作 " echo "!! 此脚本将执行以下操作: " echo "!! 1. 停止并移除所有 Overleaf 容器。 " echo "!! 2. 永久删除以下现有数据目录: " echo "!! - $DATA_ROOT/sharelatex_data" echo "!! - $DATA_ROOT/mongo_data" echo "!! - $DATA_ROOT/redis_data" echo "!! 3. 从以下备份文件恢复数据: " echo "!! $BACKUP_FILE" echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" read -p "你确定要继续吗? (输入 'yes' 继续): " CONFIRMATION if [ "$CONFIRMATION" != "yes" ]; then echo "操作已取消。" exit 0 fi echo "=========================================" echo "Starting Overleaf Restore: $(date)" echo "=========================================" # 1. 停止并移除现有服务 echo "[Step 1/7] Stopping and removing existing Overleaf services..." cd "$COMPOSE_DIR" || { echo "!! 错误: 找不到部署目录 $COMPOSE_DIR"; exit 1; } docker compose down echo "=> Services stopped and removed." # 2. 创建一个临时的恢复工作目录 RESTORE_WORKDIR="/tmp/overleaf_restore_$$" # $$ 是当前进程ID,确保唯一性 mkdir -p "$RESTORE_WORKDIR" echo "[Step 2/7] Created temporary restore directory: $RESTORE_WORKDIR" # 3. 将备份压缩包复制并解压到临时目录 echo "[Step 3/7] Copying and extracting backup file..." cp "$BACKUP_FILE" "$RESTORE_WORKDIR/" tar -xzf "$RESTORE_WORKDIR/$(basename "$BACKUP_FILE")" -C "$RESTORE_WORKDIR" echo "=> Backup extracted." # 4. 永久删除现有的数据目录 echo "[Step 4/7] Deleting existing data directories..." rm -rf "$DATA_ROOT/sharelatex_data" rm -rf "$DATA_ROOT/mongo_data" rm -rf "$DATA_ROOT/redis_data" echo "=> Existing data directories removed." # 5. 移动恢复出来的数据到目标位置 echo "[Step 5/7] Moving restored data to final destination..." mv "$RESTORE_WORKDIR/sharelatex_data" "$DATA_ROOT/" mv "$RESTORE_WORKDIR/mongo_data" "$DATA_ROOT/" mv "$RESTORE_WORKDIR/redis_data" "$DATA_ROOT/" # 询问是否恢复配置文件,大部分时候我们不需要修改docker-compose.yml文件 while true; do read -p "是否恢复备份中的 docker-compose.yml 配置文件? (yes/no): " yn case $yn in [Yy][Ee][Ss]) RESTORE_CONFIG_FILE=true echo "=> 将会恢复 docker-compose.yml。" mv "$RESTORE_WORKDIR/docker-compose.yml" "$COMPOSE_DIR/" break # 跳出循环 ;; [Nn][Oo]) RESTORE_CONFIG_FILE=false echo "=> 将会跳过恢复 docker-compose.yml,请确保你当前的配置文件是正确的。" break # 跳出循环 ;; *) echo "!! 无效输入: 请输入 'yes' 或 'no'。" ;; esac done echo "=> Data moved." # 6. 为恢复出来的数据设置正确的权限 echo "[Step 6/7] Setting correct permissions for restored data..." chown -R 33:33 "$DATA_ROOT/sharelatex_data" chown -R 999:999 "$DATA_ROOT/mongo_data" chown -R 999:999 "$DATA_ROOT/redis_data" echo "=> Permissions set." # 7. 启动服务并清理临时文件 echo "[Step 7/7] Starting Overleaf services and cleaning up..." docker compose up -d rm -rf "$RESTORE_WORKDIR" echo "=> Services started and temporary files removed." echo "=========================================" echo "Overleaf Restore Finished Successfully!" echo "=========================================" exit 0

本文作者:MoooYuuu

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!