Automate your document workflows with MayanEDMS - installing without Traefik

Document management for teams who need powerful workflow automation. Here's how to set up your own open-source DMS

A docker walkthrough tested on Synology Diskstations to install Mayan EDMS, ready for production inlocal or reverse proxy use

Recently I went on the hunt for a good, open source Document Management System. My criteria were relatively straightforward:

  • Must have a docker image
  • Must support workflows (ideally automated)
  • Must support multiple users, groups and roles
  • Must be able to either upload documents directly from the GUI, or otherwise drop documents into a folder which would then be automatically pulled into the software

I found a couple of contenders, and after playing with them all for a little while (or not, I spent a couple of hours trying to get two of them working without success) I settled on Mayan. The GUI is generally intuitive, and the built in workflow manager is leaps and bounds easier to use than something like OpenKM which requires a completely separate program to manage. Mayan just looks better too.

Before we get into it, it's worth noting that while my walkthrough below is based on the docker-compose.yml and .env file found in the Mayan documentation, I've modified the files a fair bit to remove some bloat, and more importantly, remove the reliance on Traefik. This can still be used with Traefik, but this is for all of you who may already have your own load balancer or reverse proxy set up, whether that's an existing traefik configuration, or an nginx-based service.

Let's get into it.


Prerequisites

  1. Docker and docker-compose installed on your machine
  2. An ability to use docker-compose.yml files with .env files, either via SSH access to your NAS (click the link if you don't know how to do that) or by using Portainer
  3. Sudo/root access (normally by using the same password as any user with admin permissions)
  4. Folder and file creation permissions

Creating the folder system

The docker-compose.yml we'll use will create named volumes inside your /@docker/volumes directory for most that we need, so we only need to create a few ourselves.

  • SSH into your server and navigate to your docker directory
  • Create a folder called mayan, and inside this folder create two more, one called staging and one called watch
  • We'll also create our docker-compose.yml and .env files inside our mayan directory
  • All of the above can be done with the following single command from your docker directory:
mkdir mayan && cd mayan && mkdir staging watch && touch docker-compose.yml .env

Done.


Populating our compose file

Whichever way you prefer to edit your files, copy paste the following into your docker-compose.yml:

version: '3.9'

networks:
  default:
    name: mayan
    ipam:
      config:
        - subnet: 172.${SUBNET}.0.0/28 

volumes:
  app:
  elasticsearch:
  postgres:
  rabbitmq:
  redis:

x-mayan-container:
  &mayan-container
  env_file: .env
  environment:
    MAYAN_CELERY_BROKER_URL: amqp://${MAYAN_RABBITMQ_USER}:${MAYAN_RABBITMQ_PASSWORD}@rabbitmq:5672/${MAYAN_RABBITMQ_VHOST}
    MAYAN_CELERY_RESULT_BACKEND: redis://:${MAYAN_REDIS_PASSWORD}@redis:6379/1
    MAYAN_DATABASES: "{'default':{'ENGINE':'django.db.backends.postgresql','NAME':'${MAYAN_DATABASE_NAME}','PASSWORD':'${MAYAN_DATABASE_PASSWORD}','USER':'${MAYAN_DATABASE_USER}','HOST':'${MAYAN_DATABASE_HOST}'} }"
    MAYAN_LOCK_MANAGER_BACKEND: mayan.apps.lock_manager.backends.redis_lock.RedisLock
    MAYAN_LOCK_MANAGER_BACKEND_ARGUMENTS: "{'redis_url':'redis://:${MAYAN_REDIS_PASSWORD}@redis:6379/2'}"
  image: mayanedms/mayanedms:${MAYAN_DOCKER_IMAGE_TAG}
  restart: unless-stopped
  volumes:
    - app:/var/lib/mayan
    # Optional volumes to access external data like staging or watch folders
    - ./staging:/staging_folder
    - ./watch:/watch_folder

x-mayan-frontend-ports:
  &mayan-frontend-ports
  ports:
    - "${PORT}:8000"

services:
  mayan-app:
    <<: *mayan-container
    <<: *mayan-frontend-ports

  elasticsearch:
    environment:
      - bootstrap.memory_lock=true
      - discovery.type=single-node
      - http.max_content_length=400mb
      - xpack.security.enabled=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - ELASTIC_PASSWORD=${MAYAN_ELASTICSEARCH_PASSWORD}
    image: elasticsearch:7.17.7
    # Enable to allow external access to the database.
    # ports:
    #  - "9200:9200"
    restart: unless-stopped
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - elasticsearch:/usr/share/elasticsearch/data

  postgresql:
    command:
      - "postgres"
      - "-c"
      - "checkpoint_completion_target=0.6"
      - "-c"
      - "default_statistics_target=200"
      - "-c"
      - "maintenance_work_mem=128MB"
      - "-c"
      - "max_connections=150"
      - "-c"
      - "shared_buffers=256MB"
      - "-c"
      - "work_mem=8MB"
    environment:
      POSTGRES_DB: ${MAYAN_DATABASE_NAME}
      POSTGRES_PASSWORD: ${MAYAN_DATABASE_PASSWORD}
      POSTGRES_USER: ${MAYAN_DATABASE_USER}
    image: postgres:13.8-alpine
    # Enable to allow external access to the database.
    # ports:
    #  - "5432:5432"
    restart: unless-stopped
    volumes:
      - postgres:/var/lib/postgresql/data

  redis:
    command:
      - redis-server
      - --appendonly
      - "no"
      - --databases
      - "3"
      - --maxmemory
      - "100mb"
      - --maxclients
      - "500"
      - --maxmemory-policy
      - "allkeys-lru"
      - --save
      - ""
      - --tcp-backlog
      - "256"
      - --requirepass
      - "${MAYAN_REDIS_PASSWORD}"
    image: redis:7.0.5-alpine
    restart: unless-stopped
    volumes:
      - redis:/data

  rabbitmq:
    image: rabbitmq:3.11.2-management-alpine
    environment:
      RABBITMQ_DEFAULT_USER: ${MAYAN_RABBITMQ_USER}
      RABBITMQ_DEFAULT_PASS: ${MAYAN_RABBITMQ_PASSWORD}
      RABBITMQ_DEFAULT_VHOST: ${MAYAN_RABBITMQ_VHOST}
    # Enable to allow access to the administration interface.
    # ports:
    #   - "15672:15672"
    restart: unless-stopped
    volumes:
      - rabbitmq:/var/lib/rabbitmq
our full stack

There's a lot to look at here, but the main points to take away:

  • The devs who created the original compose are using both a .env and x-container function to pass variables to the relevant containers. Using the x-container function isn't really necessary here imho but I've decided not to change it so that our compose doesn't change more than necessary from the original
  • The above doesn't need anything changed  (other than if you want to uncomment out some ports for rabbitmq, postgres or elasticsearch to access them or their interfaces directly). We'll be doing all changes in our .env in 3, 2, 1...

Configuring our .env file

...now.

  • Open up your empty .env file and paste the following into it:
##################################################################
# These variables are passed to Docker Compose to change how the #
# `docker-compose.yml` file is interpreted.                      #
##################################################################

# Default project name. Can also change this using the
# docker-compose `-p, --project-name NAME` option
COMPOSE_PROJECT_NAME=mayan

# User alternate Mayan EDMS Docker tag
MAYAN_DOCKER_IMAGE_TAG="s4.4" #pay attention to versions which may change in the future

#Networks. You MUST input an empty docker network subnet here so that the network can be created properly
SUBNET=60

#Ports. Change this if necessary to avoid a port mapping clash on your system
PORT=8000

# Database. No need to change unless you've changed the postgresql `service` name (they need to match)
MAYAN_DATABASE_HOST=postgresql

# Security. Change these before the first run.
# Once these are set do not change them here. If you wish to change the
# passwords or usernames after the installation has completed, follow the
# documentation of each component individually and then update the password
# or username in this file. _PASSWORD_START_MARKER
MAYAN_DATABASE_NAME=mayandatabase #change if necessary
MAYAN_DATABASE_PASSWORD=amayanpassword #changeme
MAYAN_DATABASE_USER=amayandbuser #changeme
MAYAN_ELASTICSEARCH_PASSWORD=anelastisearchpwd #changeme
MAYAN_RABBITMQ_USER=arabbitmqusr #changeme
MAYAN_RABBITMQ_PASSWORD=arabbitmqpwd #changeme
MAYAN_RABBITMQ_VHOST=mayan #changeme
MAYAN_REDIS_PASSWORD=aredispassword #changeme

# Change if you use external services, otherwise don't touch
MAYAN_DOCKER_WAIT="postgresql:5432 rabbitmq:5672 redis:6379"

# Uncomment if you want to access RabbitMQ admin gui. Don't forget to uncomment in the `docker-compose.yml` too

# MAYAN_RABBITMQ_ADMIN_HTTP_PORT=15672

#########################################################
# These variables are passed to the running containers. #
# They are interpreted by Mayan EDMS.                   #
# Uncomment if you want to use them                     #
#########################################################

# To use block storage
# MAYAN_DOCUMENTS_STORAGE_BACKEND="storages.backends.s3boto3.S3Boto3Storage"
# MAYAN_DOCUMENTS_STORAGE_BACKEND_ARGUMENTS="{'access_key':'<access key>','secret_key':'<secret key>','bucket_name':'mayan','endpoint_url':'http://<URL:port>','verify':'False'}"  # 'verify':'False' for local servers with self signed SSL certificate

# To add operating system packages, like additional OCR language,
# packages, put then in the variable below
# MAYAN_APT_INSTALLS="tesseract-ocr-deu tesseract-ocr-nld"

# To use LDAP authentication.
# Create a folder named `user_settings` in the `media` folder.
# Copy the file `contrib/settings/ldap_connection_settings.py` from the online
# repository and place in the new `user_settings` folder.
# Edit the `user_settings/ldap_connection_settings' file to work with your
# LDAP server.
# Example: https://django-auth-ldap.readthedocs.io/en/latest/example.html
# Restart the stack and test.

Again, quite a bit going on here. Change the following variables before spinning up the container stack:

  • SUBNET. If you're unsure which subnets you have free, choose a high number in the 10s, something around 60
  • PORT. Only change this if necessary to avoid a port conflict on the host
  • Almost everything under Security (passwords, usernames etc.

Once you've modified the file, you're now ready to spin up the stack with docker-compose up -d.


First steps after creation

It will take about 3-5 minutes to fully spin up all the containers. If you follow along in the logs you'll notice a few errors as the programs go through their various startups, some are to be expected.  

Once you see the following in the mayan-app container logs you're good to go:

 -------------- celery@mayan-edms-worker_d.1e8f7ab00ea8 v5.2.7 (dawn-chorus)
--- ***** ----- 
-- ******* ---- Linux-4.4.180+-x86_64-with-glibc2.31 2023-04-04 03:41:08
- *** --- * --- 
- ** ---------- [config]
- ** ---------- .> app:         mayan:0x7f049b596bb0
- ** ---------- .> transport:   amqp://arabbitmqusr:**@rabbitmq:5672/mayan
- ** ---------- .> results:     redis://:**@redis:6379/1
- *** --- * --- .> concurrency: 1 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** ----- 
 -------------- [queues]
                .> ocr              exchange=ocr(direct) key=ocr
                .> storage_periodic exchange=storage_periodic(direct) key=storage_periodic
                .> tools            exchange=tools(direct) key=tools
(it may differ a little to your set up depending on versions etc.)

Open your browser and navigate to http://yourhostIP:8000 (if you changed PORT in the .env then make sure to use that port here in the browser).

The following screen will pop up with a randomly generated password:

Type in the credentials and you're in!

The very first thing you should do is change your admin password, which can be done from User->Change password in the top right corner.

Take a look around, look at some of the tutorials and information in the documentation, and you'll have automated your document workflows in no time!


Portainer - Easy Container Management for Docker
A step-by-step docker walkthrough to installing and configuring Portainer, your one-stop container-management resource
Preshared Keys and more - SSH on Linux and Synology
An easy guide which explains how to access and use your SSH sessions to their fullest, including clients and logging in with preshared keys