Overleaf CE部署

0. 前言

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

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

  • 使用 Overleaf Toolkit 进行一键部署:优点在于操作简单,只需几步就能完成部署;缺点封装比较完善、自由度低。

  • 使用 Docker镜像 部署:优点是使用docker原生部署,熟悉docker部署流程的同学能够拥有更大的自由度。

:::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
# 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仓库

1
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版本

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
# 首先安装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

:::danger{title=“警告”}
首次启动服务时不要使用-d参数,你必须密切关注控制台输出

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

1
2
3
4
5
6
7
8
# 拉取 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

1
2
3
4
5
6
7
8
9
10
11
services:
sharelatex:
restart: always
# 未指定为我们定制化后的sharelatex镜像
image: sharelatex/sharelatex

# 使用docker images查看你的镜像,并将上述内容修改为
# image: sharelatex/sharelatex:{你的TAG}
# 示例
# image: sharelatex/sharelatex:main-644db1722e8cce6bc85ea794156910928375cc0c
...

重新启动服务

1
2
3
4
5
# 同样,你不应该使用-d参数,在下面的日志中不应该出现sharelatex/sharelatex ... Pulling
docker-compose up

# 容器运行后新开一个bash,使用以下命令检查正在运行的容器其TAG是否正确
docker ps

:::

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
################
## 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文件

:::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=“提示”}
请先检查各数据目录的权限是否正确

  • mongo 和 redis 数据目录所有者uid应为999

  • sharelatex 数据目录所有者应为33

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/

# 如果权限不对的话
# 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

1
2
3
4
5
6
7
# 切换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

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
# =================================================================
# == 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
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

# =================================================================
# == 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