0. 前言
由官方提供的Overleaf免费服务近期再次缩短编译时长,限制已降至10秒,这为用户日常使用带来了显著不便。虽然市面上还有不少Overleaf的替代方案,但自部署Overleaf镜像能在最大程度上提供更无缝的编辑体验。本文记录在 Ubuntu 24.04 服务器上通过 Docker 部署 Overleaf 的完整流程,旨在为需要自建服务的用户提供参考。
Overleaf Community Edition (Overleaf CE) 提供两种本地部署方式:
:::warning{title=“注意”}
本文只介绍使用Docker镜像部署的全流程,使用镜像部署的目的在于定制TexLive版本。Toolkit默认安装TexLive 2025,而Overleaf在线版使用的是TexLive 2024,为了保证编译的顺畅进行,希望CE使用的TexLive与Online保持一致。因此如果你不需要自定义TexLive版本,那么你完全可以从“正式开始构建Overleaf CE”开始
:::
1. 安装Docker
Docker官方提供了不同的几种安装Docker的方式,以下从Apt源安装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 sudo apt-get updatesudo apt-get install ca-certificates curlsudo install -m 0755 -d /etc/apt/keyringssudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.ascsudo chmod a+r /etc/apt/keyrings/docker.ascecho \ "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 updatesudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsudo systemctl status dockersudo systemctl start dockersudo docker run hello-worldHello from Docker! \ This message shows that your installation appears to be working correctly grep docker /etc/group sudo usermod -aG docker $USER
2. 部署Overleaf CE
使用以下命令克隆Overleaf仓库
1 git clone https://github.com/overleaf/overleaf.git
其中server-ce目录下包含构建镜像用到的Dockerfile和Dockerfile-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版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # 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镜像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 sudo apt updatesudo apt install makemake --version cd server-cemake build-base docker images REPOSITORY TAG IMAGE ID ... sharelatex/sharelatex-base main 757806628bdc ... sharelatex/sharelatex-base main-16422f972bb4da0833be87675a3d34fe694d0941 757806628bdc ... 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 ... cd ..ls docker-compose.ymlmkdir data_foldersvolumes: - ./data_folers/sharelatex_data:/var/lib/overleaf volumes: - ./data_folers/mongo_data:/data/db volumes: - ./data_folers/redis_data:/data docker-compose up
:::danger{title=“警告”}
首次启动服务时不要使用-d参数,你必须密切关注控制台输出 。
理想情况下,由于已经构建好sharelatex镜像,不应该再从Docker源拉取,因此如果你看到以下内容,请使用Ctrl + C终止进程
1 2 3 4 5 6 7 8 [+] Running 82/87 ⠏ mongo [⣿⣿⣿⣿⣿⣿⣿⣿] 265.9MB / 265.9MB Pulling 37.0s ✔ redis Pulled 17.5s ⠏ sharelatex [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷] 877.3MB / 902.9MB Pulling
这是由于docker-compose.yml中
1 2 3 4 5 6 7 8 9 10 11 services: sharelatex: restart: always image: sharelatex/sharelatex ...
重新启动服务
1 2 3 4 5 docker-compose up docker ps
:::
现在,访问http://localhost/launchpad即可创建管理员账户
首次登录编译时会遇到编译失败的问题,请将docker-compose.yml中关于启用沙箱编译的配置全部注释,这一点在yaml文件内也有对应提示
3. 进阶
3.1 配置SMTP服务
Overleaf CE注册由管理员完成,允许管理员配置SMTP服务向用户发送注册邮件,所有配置项都位于docker-compose.yml文件
:::tip{title=“提示”}
首先注册网易云163邮箱,开启SMTP服务并记录SMTP密钥
:::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # 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元素深度定制"
修改完成后一定要记得重启容器来让新的环境变量生效
1 docker-compose down && docker-compose up -d
3.2 容灾
作为本地部署的Overleaf实例,灾难应对是避不开的话题,要是你的同学们在使用CE时出现意外情况导致数据丢失,后果可想而知。为了让咱们能睡个好觉,容器数据自动备份任务是必不可少的。
根据官方指南 ,我们可以确定必须参与备份的文件和文件夹如下
sharelatex 数据目录
redis 数据目录
mongo 数据目录
docker-compose.yml
:::tip{title=“提示”}
请先检查各数据目录的权限是否正确
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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/ sudo chown -R 33:33 ~/sharelatex_datasudo chown -R 999:999 ~/mongo_datasudo chown -R 999:999 ~/redis_data
:::
备份脚本最好挂载到root用户的crontab下,假设我们的备份脚本名为run_backup.sh
1 2 3 4 5 6 7 sudo -isudo crontab -e0 3 * * * xxx/run_backup.sh >> xxxx/overleaf_backups/backup_cron.log 2>&1
相对应的,我们需要一个恢复脚本,用于将容器恢复到某次备份时的状态,这里有一个注意点就是最好在脚本最后添加权限修正的部分。
4. Appendix
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 BACKUP_DIR="xxx/Overleaf_Backups" SOURCE_DIR="xxx/overleaf/data_folers" 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 #!/bin/bash DATA_ROOT="xxx/overleaf/data_folers" COMPOSE_DIR="xxx/overleaf" LOG_DIR="xxx/Overleaf_Backups/log" LOG_FILE="$LOG_DIR /restore_activity.log" 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>&1BACKUP_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' 继续): " CONFIRMATIONif [ "$CONFIRMATION " != "yes" ]; then echo "操作已取消。" exit 0 fi echo "=========================================" echo "Starting Overleaf Restore: $(date) " echo "=========================================" 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." RESTORE_WORKDIR="/tmp/overleaf_restore_$$" mkdir -p "$RESTORE_WORKDIR " echo "[Step 2/7] Created temporary restore directory: $RESTORE_WORKDIR " 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." 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." 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 /" 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." 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." 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