Traefik letsencrypt SSL certificate

First of all, thank you very much for the excellent work in the development of Mayan EDMS.
I am new to the EDMS world and I am trying to implement a test server to evaluate the possibility of using it in the institution where I work.
I have been able to carry out the initial deployment, however, I am trying to implement SSL access to the EDMS and I am not succeeding.
I can’t figure out how to properly configure Traefik to use Lets’Encrypt certificates.
Any help or comments would be welcome.
Thank you very much.

First off, Welcome to the Mayan forum! Good to hear your kicking the tires so to speak. I am using Mayan as a home files solution on a single server. May I asked what deployment strategy are you using for your test and what are you looking to do for a production?

I am running now a manual install (that I am in the processes of migration to a docker compose) and this SSL is one of my next issues as well.

Solutions I am looking at is either expose the app on local host and use Nginx to reverse proxy with SSL or using another container with Nginx and Lets Encrypt built in and expose the 443 port.

I have not used Traefik and have noticed it in the config files but do not have any experience with that Maybe someone else who knows can chime in on that.

Thanks for your answer.
I used Docker Compose for the installation.
I tried traefik because it already came with the options in the config files, but I think I’m going to try using nginx.
It is unfortunate that those options are poorly documented.

Hello, I share my config with SSL Traefik letsencrypt over Docker Compose - Debian.

Is a example.

docker-compose.yml

`version: '3.9'

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

x-mayan-traefik-labels:
  &mayan-traefik-labels
  labels:
    - "traefik.enable=${MAYAN_TRAEFIK_FRONTEND_ENABLE:-false}"
    - "traefik.http.middlewares.mayan_frontend_http_redirect.redirectscheme.scheme=https"
    - "traefik.http.middlewares.mayan_frontend_http_redirect.redirectscheme.permanent=false"
    - "traefik.http.routers.mayan_frontend_http.entrypoints=http"
    - "traefik.http.routers.mayan_frontend_http.middlewares=mayan_frontend_http_redirect"
    - "traefik.http.routers.mayan_frontend_http.rule=Host(`${MAYAN_TRAEFIK_EXTERNAL_DOMAIN}`)"
    - "traefik.http.routers.mayan_frontend_https.entrypoints=https"
    - "traefik.http.routers.mayan_frontend_https.rule=Host(`${MAYAN_TRAEFIK_EXTERNAL_DOMAIN}`)"
    - "traefik.http.routers.mayan_frontend_https.service=mayan_frontend_http"
    - "traefik.http.routers.mayan_frontend_https.tls=true"
    - "traefik.http.routers.mayan_frontend_https.tls.certresolver=letsencrypt"
    - "traefik.http.services.mayan_frontend_http.loadbalancer.server.port=8000"

x-mayan-frontend-ports:
  &mayan-frontend-ports
  # Disable ports if using Traefik. Set to an empty list `[]`.
   ports:
  # - "${MAYAN_FRONTEND_HTTP_PORT:-80}:8000"
     []

networks:
  keycloak:
    driver: bridge
    # Change to true when using Traefik for increased security.
    internal: false
  mayan:
    driver: bridge
    # Change to true when using Traefik for increased security.
    internal: false
  traefik: {}`
services:
  app:
    <<: *mayan-container
    <<: *mayan-traefik-labels
    <<: *mayan-frontend-ports
    profiles:
      - all_in_one

  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:-mayanespassword}
    image: ${MAYAN_DOCKER_ELASTICSEARCH_IMAGE:-elasticsearch}:${MAYAN_DOCKER_ELASTICSEARCH_TAG:-7.17.7}
    networks:
      - mayan
    # Enable to allow external access to the database.
    # ports:
    #  - "9200:9200"
    profiles:
      - elasticsearch
    restart: unless-stopped
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - ${MAYAN_ELASTICSEARCH_VOLUME:-elasticsearch}:/usr/share/elasticsearch/data

  keycloak:
    command:
      - start
    environment:
      KEYCLOAK_ADMIN: ${MAYAN_KEYCLOAK_ADMIN:-admin}
      KEYCLOAK_ADMIN_PASSWORD: ${MAYAN_KEYCLOAK_ADMIN_PASSWORD:-admin}
      KC_DB: postgres
      KC_DB_PASSWORD: ${MAYAN_KEYCLOAK_DATABASE_PASSWORD:-keycloakdbpass}
      KC_DB_URL_DATABASE: ${MAYAN_KEYCLOAK_DATABASE_NAME:-keycloak}
      KC_DB_URL_HOST: keycloak-postgres
      KC_DB_USERNAME: ${MAYAN_DATABASE_KEYCLOAK_USER:-keycloak}
      KC_HOSTNAME_URL: https://subdomain.domain.com:8081/
      KC_HOSTNAME_STRICT: false
      KC_HTTP_ENABLED: true
    image: ${MAYAN_DOCKER_KEYCLOAK_IMAGE:-keycloak/keycloak}:${MAYAN_DOCKER_KEYCLOAK_TAG:-20.0.1}
labels:
      - "traefik.enable=${MAYAN_TRAEFIK_KEYCLOAK_ENABLE:-true}"
      - "traefik.http.middlewares.keycloak_http_redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.keycloak_http_redirect.redirectscheme.permanent=false"
      - "traefik.http.routers.keycloak_http.entrypoints=http"
      - "traefik.http.routers.keycloak_http.middlewares=keycloak_http_redirect"
      - "traefik.http.routers.keycloak_http.rule=Host(`${MAYAN_TRAEFIK_EXTERNAL_DOMAIN}`)"
      - "traefik.http.routers.keycloak_https.entrypoints=https"
      - "traefik.http.routers.keycloak_https.rule=Host(`${MAYAN_TRAEFIK_EXTERNAL_DOMAIN}`)"
      - "traefik.http.routers.keycloak_https.service=keycloak_http"
      - "traefik.http.routers.keycloak_https.tls=true"
      - "traefik.http.routers.keycloak_https.tls.certresolver=letsencrypt"
      - "traefik.http.services.keycloak_http.loadbalancer.server.port=${MAYAN_TRAEFIK_KEYCLOAK_HTTP_PORT:-8081}"
    networks:
      - keycloak
      - mayan
    # Disable ports if using Traefik.
    # ports:
    # - "${MAYAN_TRAEFIK_KEYCLOAK_HTTP_PORT:-8081}:${MAYAN_TRAEFIK_KEYCLOAK_HTTP_PORT:-8081}"
    profiles:
      - keycloak
    restart: unless-stopped

  keycloak-postgres:
    image: postgres:13.2
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${MAYAN_KEYCLOAK_DATABASE_NAME:-keycloak}
      POSTGRES_PASSWORD: ${MAYAN_KEYCLOAK_DATABASE_PASSWORD:-keycloakdbpass}
      POSTGRES_USER: ${MAYAN_DATABASE_KEYCLOAK_USER:-keycloak}
    image: postgres:${MAYAN_DOCKER_KEYCLOAK_POSTGRES_TAG:-13.8-alpine}
    networks:
      - keycloak
    profiles:
      - keycloak_postgresql
    restart: unless-stopped
    volumes:
      - ${MAYAN_KEYCLOAK_POSTGRES_VOLUME:-keycloak-postgres}:/var/lib/postgresql/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:-mayan}
      POSTGRES_PASSWORD: ${MAYAN_DATABASE_PASSWORD:-mayandbpass}
      POSTGRES_USER: ${MAYAN_DATABASE_USER:-mayan}
    image: ${MAYAN_DOCKER_POSTGRES_IMAGE:-postgres}:${MAYAN_DOCKER_POSTGRES_TAG:-13.8-alpine}
    networks:
      - mayan
    # Enable to allow external access to the database.
    # ports:
    #  - "5432:5432"
    profiles:
      - postgresql
    restart: unless-stopped
    volumes:
      - ${MAYAN_POSTGRES_VOLUME:-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:-mayanredispassword}"
    image: ${MAYAN_DOCKER_REDIS_IMAGE:-redis}:${MAYAN_DOCKER_REDIS_TAG:-7.0.5-alpine}
    networks:
      - mayan
    profiles:
      - redis
    restart: unless-stopped
    volumes:
      - ${MAYAN_REDIS_VOLUME:-redis}:/data

  # Run a frontend gunicorn container
  frontend:
    <<: *mayan-container
    <<: *mayan-traefik-labels
    <<: *mayan-frontend-ports
    command:
      - run_frontend
    profiles:
      - extra_frontend
# Enable to run standalone workers
  mountindex:
    <<: *mayan-container
    cap_add:
      - SYS_ADMIN
    devices:
      - "/dev/fuse:/dev/fuse"
    entrypoint:
      - /bin/sh
      - -c
      - 'mkdir --parents /mnt/index && chown mayan:mayan /mnt/index && /usr/local/bin/entrypoint.sh run_command "mirroring_mount_index --allow-other creation_date /mnt/index"'  # Replace "creation_date" with the index of your choice.
    profiles:
      - mountindex
    security_opt:
      - apparmor:unconfined
    volumes:
      - type: bind
        source: /mnt/mayan_indexes/creation_date  # Host location where the index will show up.
        target: /mnt/index  # Location inside the container where the index will be mounted. Must the same is in the "entrypoint" section.
        bind:
          propagation: shared

  # Run a separate class A worker
  worker_a:
    <<: *mayan-container
    command:
      - run_worker
      - worker_a
      - "--prefetch-multiplier=1"
    profiles:
      - extra_worker_a

  # Run a separate class B worker
  worker_b:
    <<: *mayan-container
    command:
      - run_worker
      - worker_b
      - "--prefetch-multiplier=1"
    profiles:
      - extra_worker_b
# Run a separate class C worker
  worker_c:
    <<: *mayan-container
    command:
      - run_worker
      - worker_c
      - "--prefetch-multiplier=1"
    profiles:
      - extra_worker_c

  # Run a separate class D worker
  worker_d:
    <<: *mayan-container
    command:
      - run_worker
      - worker_d
      - "--concurrency=1 --prefetch-multiplier=1"
    profiles:
      - extra_worker_d

  worker_custom_queue:
    <<: *mayan-container
    command:
      - /bin/sh
      - -c
      - 'MAYAN_QUEUE_LIST=${MAYAN_WORKER_CUSTOM_QUEUE_LIST} /usr/local/bin/run_worker.sh --prefetch-multiplier=1'
    profiles:
      - extra_worker_custom

  # Run a separate Celery beat container
  celery_beat:
    <<: *mayan-container
    command:
      - run_celery
      - "beat --pidfile= --loglevel=ERROR"
    profiles:
      - extra_celery_beat

  setup_or_upgrade:
    <<: *mayan-container
    command:
      - run_initial_setup_or_perform_upgrade
    profiles:
      - extra_setup_or_upgrade
    restart: "no"
rabbitmq:
    image: ${MAYAN_DOCKER_RABBITMQ_IMAGE:-rabbitmq}:${MAYAN_DOCKER_RABBITMQ_TAG:-3.11.2-management-alpine}
    environment:
      RABBITMQ_DEFAULT_USER: ${MAYAN_RABBITMQ_USER:-mayan}
      RABBITMQ_DEFAULT_PASS: ${MAYAN_RABBITMQ_PASSWORD:-mayanrabbitpass}
      RABBITMQ_DEFAULT_VHOST: ${MAYAN_RABBITMQ_VHOST:-mayan}
    labels:
      - "traefik.enable=${MAYAN_TRAEFIK_RABBITMQ_ENABLE:-false}"
      - "traefik.http.routers.rabbitmq_admin_http.entrypoints=rabbitmq_admin_http"
      - "traefik.http.routers.rabbitmq_admin_http.rule=Host(`${MAYAN_TRAEFIK_EXTERNAL_DOMAIN}`)"
      - "traefik.http.routers.rabbitmq_admin_http.service=rabbitmq_admin_http"
      - "traefik.http.routers.rabbitmq_admin_http.tls=true"
      - "traefik.http.routers.rabbitmq_admin_http.tls.certresolver=letsencrypt"
      - "traefik.http.services.rabbitmq_admin_http.loadbalancer.server.port=15672"
    networks:
      - mayan
    # Enable to allow access to the administration interface.
    # ports:
    #   - "${MAYAN_RABBITMQ_ADMIN_PORT:-15672}:15672"
    profiles:
      - rabbitmq
    restart: unless-stopped
    volumes:
      - ${MAYAN_RABBITMQ_VOLUME:-rabbitmq}:/var/lib/rabbitmq

  traefik:
    container_name: "traefik"
    command:
      # - "--log.level=DEBUG"
      - "--api.dashboard=true"
      - "--api.insecure=${MAYAN_TRAEFIK_API_INSECURE:-false}"
      - "--certificatesresolvers.letsencrypt.acme.caserver=${MAYAN_TRAEFIK_LETS_ENCRYPT_SERVER:-https://acme-staging-v02.api.letsencrypt.org/directory}"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge=${MAYAN_TRAEFIK_LETS_ENCRYPT_DNS_CHALLENGE:-false}"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=${MAYAN_TRAEFIK_LETS_ENCRYPT_DNS_CHALLENGE_PROVIDER}"
      - "--certificatesresolvers.letsencrypt.acme.email=${MAYAN_TRAEFIK_LETS_ENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/traefik-certificates-letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=${MAYAN_TRAEFIK_LETS_ENCRYPT_TLS_CHALLENGE:-false}"
      - "--entrypoints.http.address=:80"
      - "--entrypoints.https.address=:443"
      - "--entrypoints.keycloak_http.address=:${MAYAN_TRAEFIK_KEYCLOAK_HTTP_PORT:-8081}"
      - "--entrypoints.rabbitmq_admin_http.address=:15672"
      - "--entrypoints.traefik_dashboard_http.address=:8088"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
    # - Add DNS provider variables (https://doc.traefik.io/traefik/https/acme/#providers)
    # environment:
    image: ${MAYAN_DOCKER_TRAEFIK_IMAGE:-traefik}:${MAYAN_DOCKER_TRAEFIK_TAG:-v2.5}
 labels:
      - "traefik.enable=${MAYAN_TRAEFIK_DASHBOARD_ENABLE:-false}"
      - "traefik.http.middlewares.basic-auth-global.basicauth.users=${MAYAN_TRAEFIK_DASHBOARD_AUTHENTICATION}"
      - "traefik.http.routers.traefik_https.entrypoints=traefik_dashboard_http"
      - "traefik.http.routers.traefik_https.middlewares=basic-auth-global"
      - "traefik.http.routers.traefik_https.rule=Host(`${MAYAN_TRAEFIK_EXTERNAL_DOMAIN}`)"
      - "traefik.http.routers.traefik_https.service=api@internal"
      - "traefik.http.routers.traefik_https.tls=true"
      - "traefik.http.routers.traefik_https.tls.certresolver=letsencrypt"
    networks:
      - mayan
      - traefik
    ports:
      - "${MAYAN_RABBITMQ_ADMIN_HTTP_PORT:-15672}:15672"
      - "${MAYAN_TRAEFIK_DASHBOARD_HTTP_PORT:-8080}:8080"
      - "${MAYAN_TRAEFIK_KEYCLOAK_HTTP_PORT:-8081}:8081"
      - "${MAYAN_TRAEFIK_HTTP_PORT:-80}:80"
      - "${MAYAN_TRAEFIK_HTTPS_PORT:-443}:443"
    profiles:
      - traefik
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ${MAYAN_TRAEFIK_LETSENCRYPT_VOLUME:-traefik-certificates-letsencrypt}:/traefik-certificates-letsencrypt

volumes:
  app:
  elasticsearch:
  keycloak-postgres:
  postgres:
  mountindex:
  rabbitmq:
  redis:
  traefik-certificates-letsencrypt:

.env

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

# Default profiles.
COMPOSE_PROFILES=all_in_one,extra_frontend,postgresql,rabbitmq,redis,traefik

# User alternate Mayan EDMS Docker image or tag.
# MAYAN_DOCKER_IMAGE_NAME=registry.gitlab.com/custom/custom
# MAYAN_DOCKER_IMAGE_TAG=latest


# Modify this to your database server if not using the database deployed
# by the Docker Compose file.
# MAYAN_DATABASE_HOST=

# 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=
# MAYAN_DATABASE_PASSWORD=
# MAYAN_DATABASE_USER=
# MAYAN_ELASTICSEARCH_PASSWORD=
# MAYAN_RABBITMQ_USER=
# MAYAN_RABBITMQ_PASSWORD=
# MAYAN_RABBITMQ_VHOST=
# MAYAN_REDIS_PASSWORD=

# MAYAN_FRONTEND_HTTP_PORT=80

MAYAN_WORKER_CUSTOM_QUEUE_LIST=

# Change if you use external services.
MAYAN_DOCKER_WAIT="postgresql:5432 rabbitmq:5672 redis:6379"

# RabbitMQ

# MAYAN_RABBITMQ_ADMIN_HTTP_PORT=15672

# Allows running an additional worker with a custom list of queues.
MAYAN_WORKER_CUSTOM_QUEUE_LIST=

# Traefik

# Enable to use production Let's Encrypt server.
MAYAN_TRAEFIK_LETS_ENCRYPT_SERVER=https://acme-v02.api.letsencrypt.org/directory

# Enable to launch the Let's Encrypt TLS challenge.
MAYAN_TRAEFIK_LETS_ENCRYPT_TLS_CHALLENGE=true
# Enable to activate the Traefik UI.
MAYAN_TRAEFIK_API_INSECURE=true

# Configure the administrative email for the domain.
MAYAN_TRAEFIK_LETS_ENCRYPT_EMAIL=youremail@domain.com
MAYAN_TRAEFIK_EXTERNAL_DOMAIN=yourdomain.com

# Expose the Traefik secure dashboard.
MAYAN_TRAEFIK_DASHBOARD_ENABLE=false

# Traefik secure dashboard username and password.
# Obtained using: echo $(htpasswd -nB your_username_of_choice)
# Enclose in single quotes.
MAYAN_TRAEFIK_DASHBOARD_AUTHENTICATION=''

# Insecure value of admin:admin
# MAYAN_TRAEFIK_DASHBOARD_AUTHENTICATION='admin:secret'

# Expose the frontend through Traefik.
MAYAN_TRAEFIK_FRONTEND_ENABLE=true

# Expose the RabbitMQ administrative interface through Traefik.
MAYAN_TRAEFIK_RABBITMQ_ENABLE=false

MAYAN_TRAEFIK_DASHBOARD_HTTP_PORT=8080

MAYAN_TRAEFIK_LETSENCRYPT_VOLUME=traefik-certificates-letsencrypt

MAYAN_TRAEFIK_HTTP_PORT=80

MAYAN_TRAEFIK_HTTPS_PORT=443

MAYAN_TRAEFIK_LETS_ENCRYPT_DNS_CHALLENGE_PROVIDER=

Thanks for your reply and for sharing the configuration. I will compare my configuration files and try to see what I am doing wrong.

in the .evn file, in the variable MAYAN_TRAEFIK_LETS_ENCRYPT_DNS_CHALLENGE_PROVIDER should I specify a value?

I think if you use MAYAN TRAEFIK LETS_ENCRYPT_TLS_CHALLENGE=true then MAYAN_TRAEFIK_LETS_ENCRYPT_DNS_CHALLENGE_PROVIDER is not needed, I don’t use it

scv1503
After making the changes to the configuration files and performing the docker compose up, I get the following warning “services.app.ports must be a list”. However, I have checked both files and they are similar to the example. I can’t find a list of ports that I need to add.

I see the “[ ]” commented. I uncommented the lines in traefik section and add “[]” in keycloak port section.
Still does not work. Thanks for your patience. I will try another approach.

Yeah, didnt work for me either, I mean the Traefik container doesnt start with the other containers either. I started a Traefik container on a different docker-compose file and assigned the same network and ports needed, that did the trick for me

are you able to show your config ?

depending on your needs this can do the job:

setup a reverse proxy with nginx, and Nginx will do the ssl with letsencrypt…

@smeyer - You a Turnkey Linux user? I ask cause I help out over they from time to time, and I brought Mayan to TKL back in 2017 I think, I have to look at the commit logs.

no, I don t

regards,