Overleaf is an online collaborative LaTeX editing platform. This article covers ShareLaTeX — a tool with the same functionality as Overleaf — deployed on a server using Docker.

1. Repackaging the ShareLaTeX Docker Image

ShareLaTeX has since been renamed Overleaf, and the official recommendation is to install it using the Overleaf Toolkit. The Overleaf Toolkit modifies the Linux environment, which is fairly involved. Here, Docker is used to deploy it instead, minimizing the impact on the existing Linux environment.

Reference the official docker-compose.yaml file.

However, running docker-compose directly can’t typeset Chinese, mainly because the xelatex-related packages are missing. Since this is inside a Docker container, working with it directly isn’t very convenient. Here, a new Docker image is built directly on top of the original ShareLaTeX Docker image.

In the new Docker image, texlive gets updated, and the xelatex-related packages get added to the environment. The corresponding Dockerfile is as follows:

Dockerfile
FROM sharelatex/sharelatex
RUN tlmgr option repository https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/tlnet
RUN tlmgr update --self --all
RUN tlmgr install scheme-full
WORKDIR /
ENTRYPOINT ["/sbin/my_init"]

In the folder holding the Dockerfile, build the new Docker image:

docker build --tag chinglin/sharelatex:latest --progress=plain . 2>&1 | tee build.log

2. Solving the MongoDB Issue

An additional image needs to be added alongside MongoDB, to initialize it:

mongoinit:
  restart: "no"
  image: mongo:4.4
  depends_on:
    mongo:
      condition: service_healthy
  entrypoint:
    [
      "mongo",
      "--host",
      "mongo:27017",
      "--eval",
      'rs.initiate({ _id: "overleaf", members: [ { _id: 0, host: "mongo:27017" } ] })',
    ]

3. Running docker-compose

Reference the official docker-compose.yaml file, plus our modification above:

docker-compose.yaml
version: '2.2'
services:
    sharelatex:
        restart: always
        # Server Pro users:
        # image: quay.io/sharelatex/sharelatex-pro
        image: chinglin/sharelatex
        container_name: sharelatex
        depends_on:
            mongo:
                condition: service_healthy
            redis:
                condition: service_started
        ports:
            - 8089:80
        links:
            - mongo
            - redis
        stop_grace_period: 60s
        volumes:
            - ./data/sharelatex_data:/var/lib/sharelatex
            # - ./data/sharelatex_texlive:/texlive
            ########################################################################
            ####  Server Pro: Uncomment the following line to mount the docker  ####
            ####             socket, required for Sibling Containers to work    ####
            ########################################################################
            # - /var/run/docker.sock:/var/run/docker.sock
        environment:

            SHARELATEX_APP_NAME: Overleaf Community Edition
            SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex

            # Same property, unfortunately with different names in
            # different locations
            SHARELATEX_REDIS_HOST: redis
            REDIS_HOST: redis

            ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file'

            # Enables Thumbnail generation using ImageMagick
            ENABLE_CONVERSIONS: 'true'

            # Disables email confirmation requirement
            EMAIL_CONFIRMATION_DISABLED: 'true'

            # temporary fix for LuaLaTex compiles
            # see https://github.com/overleaf/overleaf/issues/695
            TEXMFVAR: /var/lib/sharelatex/tmp/texmf-var

            ## Set for SSL via nginx-proxy
            #VIRTUAL_HOST: 103.112.212.22

            # SHARELATEX_SITE_URL: http://sharelatex.mydomain.com
            # SHARELATEX_NAV_TITLE: Our ShareLaTeX Instance
            # SHARELATEX_HEADER_IMAGE_URL: http://somewhere.com/mylogo.png
            # SHARELATEX_ADMIN_EMAIL: [email protected]

            # SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"},{"text": "Another page I want to link to can be found <a href=\"here\">here</a>"} ]'
            # SHARELATEX_RIGHT_FOOTER: '[{"text": "Hello I am on the Right"} ]'

            # SHARELATEX_EMAIL_FROM_ADDRESS: "[email protected]"

            # SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID:
            # SHARELATEX_EMAIL_AWS_SES_SECRET_KEY:

            # SHARELATEX_EMAIL_SMTP_HOST: smtp.mydomain.com
            # SHARELATEX_EMAIL_SMTP_PORT: 587
            # SHARELATEX_EMAIL_SMTP_SECURE: false
            # SHARELATEX_EMAIL_SMTP_USER:
            # SHARELATEX_EMAIL_SMTP_PASS:
            # SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
            # SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false
            # SHARELATEX_EMAIL_SMTP_NAME: '127.0.0.1'
            # SHARELATEX_EMAIL_SMTP_LOGGER: true
            # SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by department x"

            # ENABLE_CRON_RESOURCE_DELETION: true

            ################
            ## Server Pro ##
            ################

            # SANDBOXED_COMPILES: 'true'

            # SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true'
            # SANDBOXED_COMPILES_HOST_DIR: '/var/sharelatex_data/data/compiles'

            # DOCKER_RUNNER: 'false'

            # Works with test LDAP server shown at bottom of docker compose
            # SHARELATEX_LDAP_URL: 'ldap://ldap:389'
            # SHARELATEX_LDAP_SEARCH_BASE: 'ou=people,dc=planetexpress,dc=com'
            # SHARELATEX_LDAP_SEARCH_FILTER: '(uid={{username}})'
            # SHARELATEX_LDAP_BIND_DN: 'cn=admin,dc=planetexpress,dc=com'
            # SHARELATEX_LDAP_BIND_CREDENTIALS: 'GoodNewsEveryone'
            # SHARELATEX_LDAP_EMAIL_ATT: 'mail'
            # SHARELATEX_LDAP_NAME_ATT: 'cn'
            # SHARELATEX_LDAP_LAST_NAME_ATT: 'sn'
            # SHARELATEX_LDAP_UPDATE_USER_DETAILS_ON_LOGIN: 'true'
            # SHARELATEX_TEMPLATES_USER_ID: "578773160210479700917ee5"
            # SHARELATEX_NEW_PROJECT_TEMPLATE_LINKS: '[ {"name":"All Templates","url":"/templates/all"}]'
            # SHARELATEX_PROXY_LEARN: "true"

    mongo:
        restart: always
        image: mongo:4.4
        container_name: mongo
        command: "--replSet overleaf"
        expose:
            - 27017
        volumes:
            - ./data/mongo_data:/data/db
        healthcheck:
            test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
            interval: 10s
            timeout: 10s
            retries: 5

    mongoinit:
        restart: "no"
        image: mongo:4.4
        depends_on:
          mongo:
            condition: service_healthy
        entrypoint:
          [
            "mongo",
            "--host",
            "mongo:27017",
            "--eval",
            'rs.initiate({ _id: "overleaf", members: [ { _id: 0, host: "mongo:27017" } ] })',
          ]

    redis:
        restart: always
        image: redis:6.2
        container_name: redis
        expose:
            - 6379
        volumes:
            - ./data/redis_data:/data

    # ldap:
    #    restart: always
    #    image: rroemhild/test-openldap
    #    container_name: ldap
    #    expose:
    #        - 389

    # See https://github.com/jwilder/nginx-proxy for documentation on how to configure the nginx-proxy container,
    # and https://github.com/overleaf/overleaf/wiki/HTTPS-reverse-proxy-using-Nginx for an example of some recommended
    # settings. We recommend using a properly managed nginx instance outside of the Overleaf Server Pro setup,
    # but the example here can be used if you'd prefer to run everything with docker-compose

    # nginx-proxy:
    #     image: jwilder/nginx-proxy
    #     container_name: nginx-proxy
    #     ports:
    #       #- "80:80"
    #       - "443:443"
    #     volumes:
    #       - /var/run/docker.sock:/tmp/docker.sock:ro
    #       - /home/sharelatex/tmp:/etc/nginx/certs

Run docker-compose:

docker-compose up -d

To make the deployed service reachable from other machines, the corresponding port on the server also needs to be opened — 8090 in this case.

4. Creating a User

The previous step already started ShareLaTeX with docker-compose; at this point, an account still needs to be created from inside the container. On the server, run:

docker exec -i -t sharelatex /bin/bash
grunt user:create-admin --email [email protected]

At this point a URL is shown. Replace the localhost part of the URL with the server’s address, and enter the account and password in the browser to log in. From there, happy writing.

5. Experience

Pros:

  1. Works out of the box — no need to install and configure texlive on every computer.
  2. Shareable, making collaboration easy.
  3. Data is deployed on your own server (which, on the surface, looks more secure).

Cons:

  1. The web front-end editor is too naive — it can’t really be a great editor after all.
  2. Also related to the editor front-end: there doesn’t seem to be a way to take advantage of current large language models’ code-generation capabilities.

6. Summary

This article covered setting up ShareLaTeX on a server, solving ShareLaTeX’s inability to compile Chinese by repackaging the image, and fixing the MongoDB issue by modifying the official docker-compose.yaml file.