Cleaned, updated and tested Overleaf (sharelatex)

This commit is contained in:
Goyban
2026-08-24 14:59:02 +02:00
parent 3c4b63d295
commit ee1afa4231
7 changed files with 315 additions and 61 deletions
+6
View File
@@ -10,6 +10,12 @@
!*/.env.example
!*/README.md
# Support files a stack needs at runtime: init scripts, config fragments.
# Keep them in a per-service `init/` directory so they are never mistaken for
# application state.
!*/init/
!*/init/**
# Docs are not service data.
!docs/**
+1 -1
View File
@@ -40,7 +40,7 @@ handled separately — see [Exposing services](#exposing-services).
| [cloudflare-tunnel](cloudflare-tunnel/) | — | Outbound connector. Exposes other services without opening a port |
| [jellyfin](jellyfin/) | 8096 | Media streaming. Also 7359/udp for auto-discovery |
| [openwebui](openwebui/) | 3033 | LLM chat UI. One `OWUI_TAG` switches between CPU, CUDA and bundled-Ollama |
| [overleaf](overleaf/) | 8020 | LaTeX editor. Needs Mongo + Redis |
| [overleaf](overleaf/) | 8020 | LaTeX editor. Four containers: app, Mongo (replica set), Redis, init |
<!-- One row per service. Add as you go — the table is the index people scan. -->
+32
View File
@@ -0,0 +1,32 @@
# Copy to .env and adjust. .env is git-ignored; this file is not.
# ─── Required ───────────────────────────────────────────────────────
# Signs project invite tokens. Generate your own -- do not reuse anyone's:
#
# openssl rand -base64 32
#
OVERLEAF_INVITE_TOKEN_SECRET=
# ─── Public address ─────────────────────────────────────────────────
# Every generated link is built from this: account activation, password
# reset, project invites. Overleaf defaults to http://localhost, so links
# come out wrong the moment you use a port or a domain -- and you only find
# out when someone can't reset their password.
#
# local, non-standard port http://192.168.1.50:8020
# behind a reverse proxy https://overleaf.example.com
#
OVERLEAF_SITE_URL=http://localhost:8020
# ─── Basics ─────────────────────────────────────────────────────────
#OVERLEAF_APP_NAME=Overleaf
#PORT=8020
# Where Mongo, Redis and Overleaf keep their data. Projects live here --
# this is the directory to back up.
#DATA_PATH=./data
# ─── HTTPS ──────────────────────────────────────────────────────────
# Serving over TLS? Uncomment OVERLEAF_SECURE_COOKIE in compose.yaml.
# Careful: the code checks `!= null`, so setting it to "false" turns it ON.
# The only way to disable it is to leave it unset.
+190
View File
@@ -0,0 +1,190 @@
# Overleaf
Self-hosted LaTeX editor — the Community Edition of overleaf.com. Real-time
collaborative editing, compiles server-side, keeps a full TeX Live install so
you don't maintain one locally.
## Quick start
```bash
cp .env.example .env
echo "OVERLEAF_INVITE_TOKEN_SECRET=$(openssl rand -base64 32)" >> .env
$EDITOR .env # set OVERLEAF_SITE_URL
docker compose up -d
```
First boot takes a minute or two. Then create the first admin account — there
is no signup form until a user exists:
```bash
docker compose exec sharelatex bash -c \
'cd /overleaf/services/web && node modules/server-ce-scripts/scripts/create-user.mjs --admin --email=you@example.com'
```
It prints an activation URL. Open it, set a password, done.
## Ports
| Port | Proto | Purpose |
|------|-------|---------|
| 8020 | http | Web UI (container listens on 80) |
Mongo and Redis are reachable only on the internal compose network.
## Why it looks like this
**Four containers, not one.** The app, MongoDB, Redis, and a one-shot replica-set
initializer. Overleaf is a small distributed system pretending to be an app.
**Mongo runs as a replica set** (`--replSet overleaf`) even though there's one
node. Overleaf uses transactions and change streams, which Mongo only provides
in replica-set mode. Something has to call `rs.initiate()` exactly once, which
is what [`init/mongodb-init-replica-set.js`](init/mongodb-init-replica-set.js)
does — Mongo runs anything in `/docker-entrypoint-initdb.d/` on first start
against an empty data directory. Skip this and Overleaf hangs at boot with
errors that don't mention replica sets.
`extra_hosts: mongo:127.0.0.1` is part of the same trick: the init script
registers the member as `mongo:27017`, so during initialization the container
has to resolve its own service name.
**No `SANDBOXED_COMPILES`.** Upstream's sample `docker-compose.yml` ships these
*enabled*:
```yaml
SANDBOXED_COMPILES: "true"
DOCKER_RUNNER: "true"
SANDBOXED_COMPILES_SIBLING_CONTAINERS: "true"
```
Directly above them, its own comment says sandboxed compiles are Server Pro
only and "must be commented out to avoid compile issues" on Community Edition.
Copy that file as-is and your documents don't compile. They're absent here on
purpose.
**The image is pinned to `6.2.2`.** Upstream's sample is unpinned. This is a
database-backed service with schema migrations between majors — an unattended
`docker compose pull` picking up a new major is not something you want to
discover on a Sunday.
**`container_name: overleaf-mongo` / `overleaf-redis`**, while the *service*
names stay `mongo` and `redis`. The service names are load-bearing —
`OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex` resolves them — but upstream's
`container_name: mongo` would claim a very generic name on the host and collide
with the next stack that wants it.
**`stop_grace_period: 60s`** so in-flight compiles finish instead of being
killed mid-run.
## Behind your own domain
Set the public URL — this is the setting people miss:
```bash
# .env
OVERLEAF_SITE_URL=https://overleaf.example.com
```
Then uncomment `OVERLEAF_SECURE_COOKIE` in `compose.yaml`.
Caddy, which handles WebSockets automatically (Overleaf needs them for
collaborative editing and the compile log):
```caddy
overleaf.example.com {
reverse_proxy 192.168.1.50:8020
}
```
That's the whole config — Caddy gets the certificate itself. See
[reverse proxy](../docs/reverse-proxy.md).
A [Cloudflare Tunnel](../cloudflare-tunnel/) also works and passes WebSockets,
but mind the ~100 MB upload cap if you bring in large figures or datasets.
**Order matters:** change `OVERLEAF_SITE_URL` *before* creating accounts, or the
activation links you've already sent point at the wrong host.
## Running it on OpenMediaVault
Two things differ from a plain `docker compose up`, and both cost me an evening.
**Copy `init/mongodb-init-replica-set.js` across before the first start.** OMV's
compose plugin deploys the compose file and the env file, not the rest of the
directory. If that script isn't on disk when Mongo first starts, Docker creates
it as an empty *directory* — bind mounts do that for missing paths — Mongo skips
it, and the replica set is never initialized.
The symptom points nowhere near the cause: Mongo reports **healthy** while the
app restart-loops with `500_check_db_access.sh failed with status 1`. Mongo's
healthcheck only runs `db.stats().ok`, which succeeds fine without a replica set.
If you've already started it, fix it in place — the entrypoint won't re-run once
the data directory has data:
```bash
docker exec overleaf-mongo mongosh --quiet --eval \
'rs.initiate({_id:"overleaf",members:[{_id:0,host:"mongo:27017"}]})'
docker restart overleaf
```
**Use `docker exec`, not `docker compose`, for one-off commands.** OMV names the
env file `<stack>.env`, and Compose only auto-loads a file called `.env`. So a
manual compose command fails on the required variables even though the running
stack is perfectly configured:
```
required variable OVERLEAF_INVITE_TOKEN_SECRET is missing a value
```
The container already has the environment, so talk to it directly:
```bash
docker exec overleaf bash -c \
'cd /overleaf/services/web && node modules/server-ce-scripts/scripts/create-user.mjs --admin --email=you@example.com'
```
If you'd rather have compose work by hand too, symlink it once — OMV keeps
passing its own `--env-file` and won't notice:
```bash
ln -s overleaf.env .env
```
## Gotchas
- **`OVERLEAF_SECURE_COOKIE` is tested with `!= null`.** Setting it to `"false"`
turns secure cookies **on**. The only way to disable it is to leave it unset —
and with it wrongly on over plain HTTP, login silently fails to stick.
- **Mongo 8.0 needs AVX.** Mongo 5.0+ won't start on CPUs without it, and the
failure is an immediate exit with nothing useful logged. Check first:
`grep -o avx2 /proc/cpuinfo | head -1`
- **A healthy Mongo does not mean the replica set is up.** The healthcheck runs
`db.stats().ok`, which passes without one — so `depends_on: service_healthy`
is satisfied and the app starts anyway, then fails its own DB check.
- **The replica-set script only runs on an empty data directory.** If Mongo has
data but no replica set, initialize by hand:
`docker compose exec mongo mongosh --eval 'rs.initiate({_id:"overleaf",members:[{_id:0,host:"mongo:27017"}]})'`
- **No signup page until the first user exists.** Use the `create-user.mjs`
script above; the web UI won't offer to make one.
- **Back up `DATA_PATH`.** `data/mongo` holds every project. `data/overleaf`
holds uploaded files and compile output.
- **Upgrading across majors** may need migrations, and Mongo itself can't jump
several versions at once. Read the release notes rather than pulling blind.
## Health checks
The image ships its own:
```bash
docker compose exec sharelatex bash -c 'cd /overleaf/services/web && node modules/server-ce-scripts/scripts/check-mongodb.mjs'
docker compose exec sharelatex bash -c 'cd /overleaf/services/web && node modules/server-ce-scripts/scripts/check-redis.mjs'
docker compose exec mongo mongosh --quiet --eval 'rs.status().members.map(m => m.name + " " + m.stateStr)'
```
## Links
- Upstream repo: <https://github.com/overleaf/overleaf>
- Their sample compose: <https://github.com/overleaf/overleaf/blob/main/docker-compose.yml>
- The Toolkit (upstream's recommended installer): <https://github.com/overleaf/toolkit>
- Image: <https://hub.docker.com/r/sharelatex/sharelatex>
+78
View File
@@ -0,0 +1,78 @@
name: overleaf
services:
sharelatex:
image: sharelatex/sharelatex:6.2.2
container_name: overleaf
restart: unless-stopped
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_started
# Overleaf needs time to finish in-flight compiles on shutdown.
stop_grace_period: 60s
ports:
- "${PORT:-8020}:80"
volumes:
- ${DATA_PATH:-./data}/overleaf:/var/lib/overleaf
environment:
OVERLEAF_APP_NAME: ${OVERLEAF_APP_NAME:-Overleaf}
# These hostnames are compose SERVICE names, not container names.
OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex
# One setting, two variable names. Upstream's own comment: "unfortunately".
OVERLEAF_REDIS_HOST: redis
REDIS_HOST: redis
# Required for invite tokens. openssl rand -base64 32
OVERLEAF_INVITE_TOKEN_SECRET: ${OVERLEAF_INVITE_TOKEN_SECRET:?generate with openssl rand -base64 32}
# Public address. Every generated link -- activation, password reset,
# project invite -- is built from this. Defaults to http://localhost,
# which is wrong the moment you use a port or a domain.
OVERLEAF_SITE_URL: ${OVERLEAF_SITE_URL:-http://localhost:8020}
ENABLED_LINKED_FILE_TYPES: "project_file,project_output_file"
ENABLE_CONVERSIONS: "true"
EMAIL_CONFIRMATION_DISABLED: "true"
# Serving over HTTPS? Uncomment. The value is NOT parsed -- the code
# tests `!= null`, so even "false" switches it ON. Comment it out to
# disable, never set it to false.
#OVERLEAF_SECURE_COOKIE: "true"
# DO NOT add SANDBOXED_COMPILES / DOCKER_RUNNER here. Upstream's sample
# ships them enabled, but they are Server Pro only and break compiles on
# Community Edition. See the README.
mongo:
image: mongo:8.0
container_name: overleaf-mongo
restart: unless-stopped
# Overleaf uses transactions, which require replica-set mode even on one node.
command: "--replSet overleaf"
volumes:
- ${DATA_PATH:-./data}/mongo:/data/db
- ./init/mongodb-init-replica-set.js:/docker-entrypoint-initdb.d/mongodb-init-replica-set.js:ro
environment:
MONGO_INITDB_DATABASE: sharelatex
# rs.initiate() registers the member as "mongo:27017", so during init the
# container has to resolve that name to itself.
extra_hosts:
- mongo:127.0.0.1
healthcheck:
test: echo 'db.stats().ok' | mongosh localhost:27017/test --quiet
interval: 10s
timeout: 10s
retries: 5
redis:
image: redis:6.2
container_name: overleaf-redis
restart: unless-stopped
volumes:
- ${DATA_PATH:-./data}/redis:/data
-60
View File
@@ -1,60 +0,0 @@
volumes:
web:
mongo:
redis:
mongo-init-replica:
services:
web:
image: sharelatex/sharelatex:4.2.3@sha256:6c6498de22775f1227eed7cf472c764aa851f0fe5b9e640d989ea8a90837159e
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_started
links:
- mongo
- redis
ports:
- 8020:80
volumes:
- ./data/sharelatex:/var/lib/sharelatex
environment:
SHARELATEX_APP_NAME: Overleaf
SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex
SHARELATEX_REDIS_HOST: redis
REDIS_HOST: redis
ENABLED_LINKED_FILE_TYPES: "project_file,project_output_file"
ENABLE_CONVERSIONS: "true"
EMAIL_CONFIRMATION_DISABLED: "true"
TEXMFVAR: /var/lib/sharelatex/tmp/texmf-var
mongo:
image: mongo:4.4@sha256:52c42cbab240b3c5b1748582cc13ef46d521ddacae002bbbda645cebed270ec0
command: "--replSet overleaf"
expose:
- 27017
volumes:
- ./data/mongo:/data/db
healthcheck:
test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
interval: 10s
timeout: 10s
retries: 5
redis:
image: redis:6.2@sha256:5610a8ffeaf386ecc7b6c8f070662a7a9d01bc7eafe237152a7d16e05b84afc9
expose:
- 6379
volumes:
- ./data/redis:/data
mongo-init-replica:
image: mongo:4.4
depends_on:
- mongo
entrypoint: >
bash -c "
until mongo --host mongo --eval 'print(\"waiting for mongo\")'; do sleep 2; done;
mongo --host mongo --eval 'rs.initiate({_id: \"overleaf\", members: [{_id: 0, host: \"mongo:27017\"}]})'"
restart: "on-failure"
@@ -0,0 +1,8 @@
// Mounted into Mongo's /docker-entrypoint-initdb.d/ and run ONCE, on the very
// first start against an empty data directory. Overleaf needs transactions,
// which Mongo only offers in replica-set mode -- even for a single node.
//
// If you ever wipe ./data/mongo, this runs again. If Mongo has data but no
// replica set, it does NOT run and you must call rs.initiate() by hand.
rs.initiate({ _id: 'overleaf', members: [{ _id: 0, host: 'mongo:27017' }] })