Ce guide documente l'architecture complète du déploiement Docker de Mayan EDMS en production, basé sur l'analyse du fork egyptian. Il couvre l'entrypoint, les variables d'environnement, les volumes, supervisord, les modes de déploiement et le scaling.
Fichiers clés :
mayan/apps/platforms_docker/templates/platforms_docker/entrypoint.tmpl — script d'entrée (généré au build)mayan/apps/platforms_docker/templates/platforms_docker/supervisord.tmpl — config supervisord (générée au build)docker/docker-compose.yml — orchestration complètedocker/rootfs/ — fichiers copiés à la racine du conteneurconfig.env — valeurs de configuration par défautUn seul conteneur fait tourner tous les services : Gunicorn (web), 5 workers Celery et le Beat Scheduler.
conteneur mayan-app-1
├── Gunicorn (port 8000)
├── Worker A — converter, events
├── Worker B — documents, parsing, workflows
├── Worker C — périodiques, trash, mailing
├── Worker D — OCR, file_metadata, signatures
├── Worker E — search
└── Celery Beat — tâches périodiques
Adapté aux déploiements mono-serveur avec ressources limitées.
multi_container)Conteneurs séparés pour chaque composant, gérés par des profils Docker Compose.
conteneur mayan-frontend → Gunicorn uniquement
conteneur mayan-worker-a → Worker A uniquement
conteneur mayan-worker-b → Worker B uniquement
conteneur mayan-worker-c → Worker C uniquement
conteneur mayan-worker-d → Worker D uniquement
conteneur mayan-worker-e → Worker E uniquement
conteneur mayan-celery-beat → Beat Scheduler uniquement
conteneur setup-or-upgrade → Migrations (one-shot, s'arrête après)
Permet de scaler indépendamment chaque composant.
À chaque démarrage, l'entrypoint exécute dans cet ordre :
wait.py vérifie PostgreSQL, Redis, RabbitMQ)mayanMAYAN_APT_INSTALLS)MAYAN_PIP_INSTALLS)fc-cache)MAYAN_DOCKER_SCRIPT_PRE_SETUP)MAYAN_DOCKER_SCRIPT_POST_SETUP)INSTALL_FLAG = /var/lib/mayan/system/SECRET_KEY
Si le fichier n'existe pas → initial_setup
└─ mayan-edms.py common_initial_setup --force --no-dependencies
(migrations, création SECRET_KEY, données par défaut, indices)
Si le fichier existe → perform_upgrade
└─ mayan-edms.py common_perform_upgrade --no-dependencies
(migrations uniquement, idempotent)
Les migrations sont automatiques à chaque démarrage en mode run_all.
| Commande | Description |
|---|---|
run_all |
Lance supervisord (gunicorn + workers + beat) — mode par défaut |
run_frontend |
Lance uniquement Gunicorn |
run_worker <worker_name> |
Lance un worker Celery spécifique |
run_celery <args> |
Commande Celery arbitraire (ex: beat) |
run_command <args> |
Commande mayan-edms.py arbitraire |
run_initial_setup |
Force l'initialisation (migrations + setup) |
run_perform_upgrade |
Force la mise à jour (migrations) |
run_tests |
Lance la suite de tests |
| Variable | Défaut | Description |
|---|---|---|
MAYAN_MEDIA_ROOT |
/var/lib/mayan |
Racine des données persistantes |
MAYAN_STATIC_ROOT |
/opt/mayan-edms/static |
Fichiers statiques (CSS, JS) |
MAYAN_INSTALL_DIR |
/opt/mayan-edms |
Répertoire d'installation |
MAYAN_SETTINGS_MODULE |
mayan.settings.production |
Module de settings Django |
| Variable | Défaut | Description |
|---|---|---|
MAYAN_GUNICORN_WORKERS |
3 |
Nombre de processus worker |
MAYAN_GUNICORN_WORKER_CLASS |
gevent |
Classe de worker (sync, gevent, gthread) |
MAYAN_GUNICORN_TIMEOUT |
120 |
Timeout en secondes avant de tuer un worker |
MAYAN_GUNICORN_MAX_REQUESTS |
500 |
Requêtes max avant redémarrage du worker |
MAYAN_GUNICORN_REQUESTS_JITTER |
50 |
Variabilité aléatoire sur max_requests |
MAYAN_GUNICORN_LIMIT_REQUEST_LINE |
4094 |
Taille max de la ligne HTTP (headers) |
Gunicorn utilise automatiquement
/dev/shmcomme répertoire temporaire si disponible (améliore les performances sous Docker).
| Variable | Défaut | Description |
|---|---|---|
MAYAN_USER_UID |
1000 |
UID de l'utilisateur mayan dans le conteneur |
MAYAN_USER_GID |
1000 |
GID de l'utilisateur mayan dans le conteneur |
MAYAN_SKIP_CHOWN_ON_STARTUP |
false |
Sauter le chown -R récursif au démarrage |
MAYAN_COMMON_DISABLE_LOCAL_STORAGE |
(non défini) | Désactiver la gestion UID/GID (storage externe) |
Si tes volumes sont montés depuis un hôte Linux avec un UID différent de 1000, définir
MAYAN_USER_UIDetMAYAN_USER_GIDpour éviter les problèmes de permissions. Sur de gros volumes, activerMAYAN_SKIP_CHOWN_ON_STARTUP=truepour éviter unchownrécursif lent à chaque démarrage.
| Variable | Format | Description |
|---|---|---|
MAYAN_APT_INSTALLS |
Paquets séparés par des espaces | Paquets Debian à installer au démarrage |
MAYAN_PIP_INSTALLS |
Paquets séparés par des espaces | Paquets Python à installer au démarrage |
# Exemple dans docker-compose.yml
environment:
MAYAN_APT_INSTALLS: tesseract-ocr-fra tesseract-ocr-deu
MAYAN_PIP_INSTALLS: mayan-edms-document-qa==1.0
Important : Ces installations se font à chaque démarrage du conteneur. Pour des images immuables en production, préférer construire une image dérivée avec ces paquets déjà installés.
| Variable | Description |
|---|---|
MAYAN_DOCKER_SCRIPT_PRE_SETUP |
Script bash exécuté avant l'installation des paquets |
MAYAN_DOCKER_SCRIPT_POST_SETUP |
Script bash exécuté après l'installation, avant le service principal |
environment:
MAYAN_DOCKER_SCRIPT_POST_SETUP: |
echo "Configuration personnalisée..."
cp /mayan-custom/settings.py /var/lib/mayan/settings.py
| Variable | Défaut | Description |
|---|---|---|
MAYAN_DOCKER_WAIT |
"postgresql:5432 rabbitmq:5672 redis:6379" |
Services à attendre au démarrage (format host:port) |
environment:
MAYAN_DOCKER_WAIT: "postgres:5432 redis:6379 elasticsearch:9200"
| Variable | Défaut | Description |
|---|---|---|
MAYAN_SUPERVISOR_AUTORESTART |
false |
Redémarrage automatique des processus supervisord |
environment:
MAYAN_DATABASES: '{"default":{"ENGINE":"django.db.backends.postgresql","NAME":"mayan","PASSWORD":"motdepasse","USER":"mayan","HOST":"postgresql","CONN_MAX_AGE":60}}'
Paramètres clés :
CONN_MAX_AGE : durée de vie des connexions en secondes (0 = nouvelle connexion par requête, 60 = pool persistant)environment:
# RabbitMQ (recommandé pour la production)
MAYAN_CELERY_BROKER_URL: amqp://mayan:password@rabbitmq:5672/mayan
# Redis (result backend + lock manager)
MAYAN_CELERY_RESULT_BACKEND: redis://:password@redis:6379/1
# Lock Manager
MAYAN_LOCK_MANAGER_BACKEND: mayan.apps.lock_manager.backends.redis_lock.RedisLock
MAYAN_LOCK_MANAGER_BACKEND_ARGUMENTS: '{"redis_url":"redis://:password@redis:6379/2"}'
| Point de montage | Volume recommandé | Description |
|---|---|---|
/var/lib/mayan |
mayan_app (volume nommé) |
Données persistantes : documents, SECRET_KEY, config |
Ce volume doit impérativement survivre aux redémarrages et mises à jour. Toujours inclure dans les sauvegardes.
| Service | Point de montage | Volume |
|---|---|---|
| PostgreSQL | /var/lib/postgresql/data |
mayan_postgres |
| Redis | /data |
mayan_redis |
| RabbitMQ | /var/lib/rabbitmq |
mayan_rabbitmq |
| Elasticsearch | /usr/share/elasticsearch/data |
mayan_elasticsearch |
volumes:
# Dossier de staging pour l'import manuel
- /opt/staging:/staging_folder
# Dossier surveillé (watchfolder source)
- /opt/incoming:/watch_folder
# Montages FUSE pour cabinets/index (nécessite privileged)
- /mnt/mayan_cabinets:/mnt/cabinets
- /mnt/mayan_indexes:/mnt/indexes
Fichiers copiés dans le conteneur lors du build :
| Chemin dans le conteneur | Rôle |
|---|---|
/usr/local/bin/entrypoint.sh |
Point d'entrée principal (généré depuis entrypoint.tmpl) |
/usr/local/bin/run_all.sh |
Lance supervisord |
/usr/local/bin/run_frontend.sh |
Lance Gunicorn uniquement |
/usr/local/bin/run_worker.sh |
Lance un worker Celery |
/usr/local/bin/run_celery.sh |
Wrapper CLI Celery |
/usr/local/bin/run_tests.sh |
Lance les tests |
/usr/local/bin/wait.py |
Vérifie la disponibilité des services dépendants |
/etc/supervisor/supervisord.conf |
Config supervisord (générée depuis supervisord.tmpl) |
/docker/rootfs/version |
Numéro de version de l'image |
Les fichiers statiques (CSS, JS, images) sont compilés au moment du build de l'image, pas au runtime :
# Dans le Dockerfile (dockerfile.tmpl)
${PROJECT_INSTALL_DIR}bin/mayan-edms.py appearance_prepare_static --link --noinput
Résultat : /opt/mayan-edms/static/ contient tous les assets, en lecture seule dans le conteneur. Aucune commande collectstatic n'est nécessaire au démarrage.
healthcheck:
test: >
CMD-SHELL /opt/mayan-edms/bin/python -c
"import sys,urllib.request;
r=urllib.request.urlopen('http://127.0.0.1:8000/', timeout=5);
sys.exit(0 if r.status<400 else 1)"
interval: 30s
timeout: 5s
retries: 10
start_period: 30s
Le healthcheck fait une requête HTTP sur le port 8000 et attend un statut < 400. Le conteneur a 30 secondes de grâce au démarrage, puis est vérifié toutes les 30 secondes. Après 10 échecs consécutifs (~5 minutes), il est marqué unhealthy.
| Port | Service | Variable |
|---|---|---|
8000 (interne) |
Gunicorn | — |
80 (hôte, défaut) |
Proxy vers Gunicorn | MAYAN_FRONTEND_HTTP_PORT |
443 (hôte, Traefik) |
HTTPS | — |
Les ports PostgreSQL (5432), Redis (6379), RabbitMQ (5672/15672) et Elasticsearch (9200) sont internes uniquement par défaut — ne pas les exposer en production sauf besoin explicite.
Au démarrage, l'entrypoint adapte dynamiquement l'utilisateur mayan :
# Toujours effectué (sauf MAYAN_COMMON_DISABLE_LOCAL_STORAGE)
chown mayan:mayan /var/lib/mayan # Top-level uniquement
groupmod mayan --gid ${MAYAN_USER_GID}
usermod mayan --uid ${MAYAN_USER_UID}
# Uniquement si UID/GID ≠ 1000
chown -R mayan:mayan /opt/mayan-edms/static
# Uniquement si UID/GID ≠ 1000 ET MAYAN_SKIP_CHOWN_ON_STARTUP != true
chown -R mayan:mayan /var/lib/mayan # Peut être lent sur gros volumes !
Recommandation : Si le volume /var/lib/mayan contient des milliers de fichiers, activer MAYAN_SKIP_CHOWN_ON_STARTUP=true et gérer les permissions en dehors du conteneur.
services:
postgresql:
image: postgres:16-alpine
environment:
POSTGRES_DB: mayan
POSTGRES_PASSWORD: ${MAYAN_DATABASE_PASSWORD:-mayandbpass}
POSTGRES_USER: mayan
volumes:
- mayan_postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mayan"]
interval: 10s
retries: 5
redis:
image: redis:7-alpine
command: >
redis-server
--requirepass ${MAYAN_REDIS_PASSWORD:-mayanredispassword}
--maxmemory-policy allkeys-lru
volumes:
- mayan_redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
retries: 5
rabbitmq:
image: rabbitmq:3-management-alpine
environment:
RABBITMQ_DEFAULT_VHOST: mayan
RABBITMQ_DEFAULT_USER: mayan
RABBITMQ_DEFAULT_PASS: ${MAYAN_RABBITMQ_PASSWORD:-mayanrabbitpass}
volumes:
- mayan_rabbitmq:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"]
interval: 30s
retries: 10
app:
image: mayanedms/mayanedms:egyptian
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
ports:
- "${MAYAN_FRONTEND_HTTP_PORT:-80}:8000"
volumes:
- mayan_app:/var/lib/mayan
environment:
MAYAN_DATABASES: >
{"default":{"ENGINE":"django.db.backends.postgresql",
"NAME":"mayan","PASSWORD":"${MAYAN_DATABASE_PASSWORD:-mayandbpass}",
"USER":"mayan","HOST":"postgresql","CONN_MAX_AGE":60}}
MAYAN_CELERY_BROKER_URL: amqp://mayan:${MAYAN_RABBITMQ_PASSWORD:-mayanrabbitpass}@rabbitmq:5672/mayan
MAYAN_CELERY_RESULT_BACKEND: redis://:${MAYAN_REDIS_PASSWORD:-mayanredispassword}@redis:6379/1
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"}
# Workers
MAYAN_WORKER_B_CONCURRENCY: 4
MAYAN_WORKER_D_CONCURRENCY: 1
# Gunicorn
MAYAN_GUNICORN_WORKERS: 3
MAYAN_GUNICORN_TIMEOUT: 120
healthcheck:
test: >
CMD-SHELL /opt/mayan-edms/bin/python -c
"import sys,urllib.request;
r=urllib.request.urlopen('http://127.0.0.1:8000/', timeout=5);
sys.exit(0 if r.status<400 else 1)"
interval: 30s
timeout: 5s
retries: 10
start_period: 30s
volumes:
mayan_app:
mayan_postgres:
mayan_redis:
mayan_rabbitmq:
# Séparation frontend / workers
services:
frontend:
image: mayanedms/mayanedms:egyptian
command: ["run_frontend"]
ports:
- "8000:8000"
environment:
MAYAN_GUNICORN_WORKERS: 6
worker_b:
image: mayanedms/mayanedms:egyptian
command: ["run_worker", "worker_b"]
environment:
MAYAN_WORKER_B_CONCURRENCY: 8
# Déployer plusieurs instances
deploy:
replicas: 2
worker_d:
image: mayanedms/mayanedms:egyptian
command: ["run_worker", "worker_d"]
environment:
MAYAN_WORKER_D_CONCURRENCY: 2
celery_beat:
image: mayanedms/mayanedms:egyptian
command: ["run_celery", "beat", "--pidfile=", "--loglevel=ERROR"]
# Un seul beat scheduler (jamais répliquer !)
deploy:
replicas: 1
setup:
image: mayanedms/mayanedms:egyptian
command: ["run_initial_setup_or_perform_upgrade"]
restart: "no"
Important : Le Beat Scheduler ne doit jamais être répliqué — une seule instance par déploiement, sinon les tâches périodiques s'exécutent en doublon.
| Données | Méthode |
|---|---|
Volume /var/lib/mayan |
rsync, snapshot de volume, ou tar |
| Base de données PostgreSQL | pg_dump mayan > backup.sql |
| Redis (optionnel) | redis-cli BGSAVE + copie de /data/dump.rdb |
# Dump PostgreSQL
docker exec mayan-postgresql-1 pg_dump -U mayan mayan > backup_$(date +%Y%m%d).sql
# Sauvegarde du volume documents
docker run --rm -v mayan_app:/data -v $(pwd):/backup alpine \
tar czf /backup/mayan_data_$(date +%Y%m%d).tar.gz /data
# Restaurer la base
docker exec -i mayan-postgresql-1 psql -U mayan mayan < backup_20260501.sql
# Restaurer les données
docker run --rm -v mayan_app:/data -v $(pwd):/backup alpine \
tar xzf /backup/mayan_data_20260501.tar.gz -C /
# 1. Tirer la nouvelle image
docker compose pull
# 2. Arrêter le conteneur
docker compose down
# 3. Relancer (les migrations s'exécutent automatiquement au démarrage)
docker compose up -d
# 4. Vérifier les logs de migration
docker logs mayan-app-1 --follow
La commande common_perform_upgrade est idempotente — elle peut être relancée plusieurs fois sans danger.
| Variable | Défaut | À changer en prod |
|---|---|---|
MAYAN_DATABASES |
SQLite | Oui — PostgreSQL obligatoire |
MAYAN_CELERY_BROKER_URL |
memory:// |
Oui — Redis ou RabbitMQ |
MAYAN_CELERY_RESULT_BACKEND |
(non défini) | Oui — Redis |
MAYAN_LOCK_MANAGER_BACKEND |
fichiers locaux | Oui — Redis (multi-conteneurs) |
MAYAN_GUNICORN_WORKERS |
3 |
Selon RAM disponible |
MAYAN_WORKER_B_CONCURRENCY |
4 |
Selon CPU disponible |
MAYAN_USER_UID / MAYAN_USER_GID |
1000 |
Si besoin de correspondre à l'hôte |
MAYAN_SKIP_CHOWN_ON_STARTUP |
false |
true si volume volumineux |
MAYAN_SUPERVISOR_AUTORESTART |
false |
Laisser à false (Docker gère le restart) |