Skip to content

Configuration ​

Une instance Mankomail se configure par des variables d’environnement, complétées par quelques réglages que les administrateurs modifient dans l’interface. Cette page explique comment l’ensemble s’articule. La liste complète des variables, avec leurs valeurs par défaut et leurs valeurs acceptées, figure dans Variables d’environnement.

Comment la configuration est lue ​

  • Les variables d’environnement sont lues une fois, au démarrage du processus. Pour en changer une, modifiez-la et redémarrez l’application.

  • Une valeur invalide n’arrête jamais le démarrage. Le processus écrit une ligne qui commence par configuration: avec le nom de la variable, et utilise la valeur par défaut à la place. Lisez les journaux de démarrage après chaque modification :

    bash
    docker compose -f compose.reference.yaml logs app | grep 'configuration:'
  • Une valeur vide compte comme absente : PUBLIC_BASE_URL= se comporte comme une PUBLIC_BASE_URL manquante.

  • Certains réglages vivent dans l’interface, pas dans des variables : les clés des fournisseurs d’IA, les applications OAuth, l’interrupteur d’envoi de l’organisation, les membres. Ils sont enregistrés en base et prennent effet sans redémarrage.

Redémarrer ne suffit pas avec Docker Compose

docker compose restart redémarre le conteneur existant avec son ancien environnement. Pour appliquer une modification faite dans .env ou dans un fichier Compose, recréez le conteneur :

bash
docker compose -f compose.reference.yaml up -d app

Le fichier Compose de référence ​

Le code source livre compose.reference.yaml, un fichier Docker Compose destiné à l’auto-hébergement en production. Il fait tourner PostgreSQL, MinIO, une tâche ponctuelle qui crée le bucket de stockage, et l’application.

Ce qu’il fait pour vous :

  • Il refuse de démarrer si POSTGRES_PASSWORD, STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY, ENCRYPTION_KEY ou PUBLIC_BASE_URL manque dans .env.
  • Il ne publie que le port de l’application, sur 127.0.0.1 par défaut (APP_BIND, APP_PORT). PostgreSQL et MinIO ne sont joignables que depuis le réseau Compose.
  • Il durcit le conteneur applicatif : utilisateur non root, système de fichiers en lecture seule, aucune capacité Linux, no-new-privileges.
  • Il fixe des limites de ressources à chaque conteneur (APP_CPUS, APP_MEMORY, POSTGRES_CPUS, POSTGRES_MEMORY, MINIO_CPUS, MINIO_MEMORY).
  • Il plafonne les journaux des conteneurs à cinq fichiers de 10 Mo par conteneur.
  • Il fixe lui-même certaines variables de l’application, quoi que dise .env : NODE_ENV=production, PORT=3000, le pilote et le point d’accès du stockage, et DATABASE_URL construite à partir des variables POSTGRES_*.

Il transmet au conteneur applicatif une liste explicite de variables. Une variable de .env absente de cette liste, comme TRUST_PROXY, COOKIE_SECURE, LLM_REQUEST_TIMEOUT_MS ou TABLES_MAX_ROWS, n’atteint jamais l’application. La liste figure dans Variables du fichier Compose de référence.

Ajouter vos propres réglages ​

Pour régler une variable que le fichier de référence ne transmet pas, ou pour modifier un service, deux possibilités.

Un fichier Compose supplémentaire, chargé après celui de référence. Par exemple compose.custom.yaml :

yaml
services:
  app:
    environment:
      LLM_REQUEST_TIMEOUT_MS: '300000'
      TRUST_PROXY: '2'
bash
docker compose -f compose.reference.yaml -f compose.custom.yaml up -d

Chaque commande docker compose doit alors nommer les deux fichiers.

Votre propre copie du fichier de référence. Copiez compose.reference.yaml sous un autre nom et modifiez-la. C’est la solution la plus simple quand vous changez plusieurs services, et celle qui s’accorde le mieux avec les scripts de sauvegarde.

Scripts de sauvegarde et fichiers Compose supplémentaires

Les scripts de sauvegarde et de restauration pilotent un seul fichier Compose : compose.reference.yaml par défaut, ou celui que désigne la variable COMPOSE_FILE. Quand une restauration redémarre l’application, elle le fait avec ce seul fichier : les réglages d’un fichier supplémentaire ne s’appliquent qu’après un nouveau docker compose up -d avec tous vos fichiers. Si vous personnalisez la pile, préférez une copie unique du fichier de référence et pointez COMPOSE_FILE dessus. Voir Sauvegardes.

Les secrets en fichier ​

Chaque variable lue par l’application accepte aussi une forme <NOM>_FILE : la valeur est lue dans le fichier désigné, espaces de début et de fin retirés. Cela convient aux secrets Docker et Kubernetes.

yaml
services:
  app:
    environment:
      ENCRYPTION_KEY_FILE: /run/secrets/encryption_key
    secrets:
      - encryption_key

secrets:
  encryption_key:
    file: ./secrets/encryption_key
  • Quand NOM et NOM_FILE sont toutes deux renseignées, le fichier l’emporte.
  • Un fichier illisible ou vide produit un avertissement configuration:, et la variable simple est utilisée à la place, si elle est renseignée.
  • Le fichier Compose de référence exige ENCRYPTION_KEY dans .env. Pour passer par la forme fichier, retirez cette ligne de votre copie du fichier Compose. Les scripts de sauvegarde lisent ENCRYPTION_KEY dans .env pour en enregistrer l’empreinte : sans elle, une restauration ne peut pas vérifier que vous utilisez la bonne clé.

Adresse publique et HTTPS ​

L’application écoute en HTTP simple, sur le port 3000 de son conteneur. Les utilisateurs la joignent à travers un reverse proxy qui termine le HTTPS. Des exemples de configuration pour Caddy et nginx figurent dans Placer un reverse proxy devant.

Trois réglages doivent s’accorder avec le proxy :

RéglageValeur derrière un reverse proxy
PUBLIC_BASE_URLL’adresse HTTPS exacte que tapent les utilisateurs, schéma, hôte et port compris, sans / final. Par exemple https://mail.example.com.
TRUST_PROXYtrue (défaut). Le proxy doit transmettre X-Forwarded-For et X-Forwarded-Proto. Avec deux proxies en cascade, utilisez 2.
APP_BIND127.0.0.1 (défaut), pour que le port de l’application ne soit pas joignable depuis le réseau.

PUBLIC_BASE_URL sert à construire les URI de redirection OAuth (<PUBLIC_BASE_URL>/api/v1/oauth/google/callback, <PUBLIC_BASE_URL>/api/v1/oauth/microsoft/callback), les liens des invitations et des mails d’approbation, l’URL de notification Microsoft Graph et la Content Security Policy de l’interface web. Une valeur fausse ou absente n’arrête pas l’instance : elle démarre avec http://localhost:3000, et la connexion des boîtes échoue ensuite chez le fournisseur.

Le cookie de session porte l’attribut Secure en production. Une demande de connexion qui n’arrive pas en HTTPS est refusée avec auth.https_required. Un HTTPS terminé par le proxy compte, grâce à X-Forwarded-Proto.

Une nouvelle adresse publique ​

Si l’instance change d’adresse :

  1. Renseignez la nouvelle PUBLIC_BASE_URL et recréez le conteneur applicatif.
  2. Mettez à jour les URI de redirection des applications OAuth dans vos consoles Google et Microsoft. En tant qu’administrateur, ouvrez Administration › Applications OAuth pour copier les nouvelles valeurs. Voir Google et Microsoft.
  3. Mettez à jour le point de livraison de votre abonnement Google Cloud Pub/Sub, si vous utilisez les notifications push de Gmail.
  4. Envoyez de nouveaux liens aux personnes qui ont une invitation en attente : les liens déjà transmis portent l’ancienne adresse.

La clé de chiffrement ​

ENCRYPTION_KEY est la clé de l’instance. Elle chiffre (AES-256-GCM) chaque secret enregistré : jetons OAuth des boîtes, mots de passe IMAP, clés des fournisseurs d’IA, autres connexions et secrets des applications OAuth déclarées par l’organisation.

  • Générez-la une fois avec openssl rand -hex 32 (64 caractères hexadécimaux) ou openssl rand -base64 32.
  • Elle ne peut pas changer. La version actuelle n’a pas de rotation de clé : l’instance lit une seule clé. Avec une autre clé, chaque secret enregistré devient illisible, chaque boîte doit être reconnectée, et chaque clé de fournisseur d’IA et chaque connexion doivent être saisies de nouveau.
  • Elle n’est pas dans les sauvegardes, volontairement. Gardez-la dans un gestionnaire de mots de passe ou un coffre, à part des sauvegardes.
  • Une clé absente ou invalide n’arrête pas le démarrage. L’instance écrit ENCRYPTION_KEY is not set ou ENCRYPTION_KEY is invalid, et refuse d’enregistrer le moindre secret : aucune boîte, aucun fournisseur d’IA, aucune connexion ne peut être ajouté. Vérifiez que la ligne de démarrage authentication is ready indique "credentialsEncryption":"enabled".

Les mails envoyés par l’instance ​

Mankomail n’a pas de serveur d’envoi propre : il n’y a ni réglage SMTP ni domaine d’envoi à configurer. Chaque mail part par une boîte connectée par un membre.

MailComment il est remis
Mails envoyés par les workflows et depuis le webmailPar la boîte connectée choisie par le workflow ou par le membre.
Demandes d’approbationDepuis la boîte connectée de l’approbateur, vers cette même boîte. Un approbateur sans boîte connectée ne reçoit pas de mail, et décide depuis l’interface.
Invitations des membresPas envoyées par mail. Un administrateur crée l’invitation dans Administration › Membres, copie le lien et le transmet lui-même.

Deux interrupteurs contrôlent les envois réels, et un message ne part que si les deux sont ouverts :

  • L’interrupteur de l’instance, SEND_ENABLED. Avec false, rien ne part, et l’interface ne peut pas le rouvrir. Mettez-le à false sur toute copie d’une instance de production.
  • L’interrupteur de l’organisation, dans Administration › Envois. Un administrateur y coupe et rouvre les envois, tout de suite, sans redémarrage.

Pendant la coupure, les envois sont retenus, pas perdus : ils partent à la réouverture. Les brouillons, déplacements, étiquettes et marquages continuent de fonctionner. SEND_MAX_PER_HOUR fixe le plafond horaire ; au-delà, les messages attendent leur tour. SEND_MAX_BYTES plafonne la taille d’un message composé.

Les modèles d’IA ​

Les clés des fournisseurs d’IA ne sont pas des variables d’environnement. Un administrateur les saisit dans Connexions, section Intelligence artificielle, et elles sont enregistrées chiffrées avec ENCRYPTION_KEY. Les membres voient les modèles d’IA que l’administrateur a rendus disponibles. Voir les pages des fournisseurs : OpenAI, Anthropic, Mistral, OpenRouter, Ollama et API compatible OpenAI.

Trois variables régissent les appels eux-mêmes, pour toute l’instance :

VariableDéfautUsage
LLM_REQUEST_TIMEOUT_MS120000Durée maximale d’un appel au modèle. Augmentez-la pour un modèle local lent, par exemple Ollama sur processeur.
LLM_MAX_REQUESTS_PER_MINUTE60Appels par minute et par fournisseur, partagés par tous les processus. Un appel au-delà de la limite est différé, pas en échec. Alignez-la sur le débit de votre compte chez le fournisseur.
LLM_DEFAULT_MAX_OUTPUT_TOKENS4096Limite de sortie d’un appel qui ne fixe pas la sienne.

Aucune de ces trois variables n’est transmise par le fichier Compose de référence : ajoutez-les comme indiqué dans Ajouter vos propres réglages.

Les notifications push ​

Les boîtes sont synchronisées par une interrogation périodique toutes les POLL_INTERVAL_SECONDS (300 par défaut). Les notifications push de Gmail et de Microsoft accélèrent la synchronisation ; elles sont facultatives.

VariableEffet
PUSH_SHARED_SECRETSecret d’au moins 16 caractères, attendu dans le paramètre token des points d’entrée push /hooks/push/gmail et /hooks/push/msgraph. Sans lui, les deux points d’entrée répondent 404 et la synchronisation repose sur la seule interrogation périodique.
GMAIL_PUBSUB_TOPICSujet Google Cloud Pub/Sub, projects/<projet>/topics/<sujet>. Sans lui, les boîtes Gmail passent par l’interrogation périodique.
MSGRAPH_NOTIFICATION_URLDérivée de PUBLIC_BASE_URL et de PUSH_SHARED_SECRET. Ne la renseignez que si Microsoft doit joindre l’instance par une autre adresse publique.

Le côté fournisseur est décrit dans Google et Microsoft.

Séparer API et workers ​

Avec APP_ROLE=all, un seul conteneur sert l’interface et fait tourner le travail de fond. Pour dimensionner séparément les deux côtés, faites tourner plusieurs conteneurs à partir de la même image :

  1. Dans votre copie du fichier Compose, réglez APP_ROLE: api sur le service app. Il continue de servir l’interface et l’API.
  2. Dupliquez le service app sous un autre nom, par exemple worker, réglez APP_ROLE: worker dessus, et retirez sa section ports : il ne reçoit aucun trafic utilisateur.
  3. Démarrez la pile. Les deux services appliquent les migrations au démarrage sous un verrou PostgreSQL : un seul migre, l’autre attend.

Gardez ces règles en tête :

  • Faites tourner au moins un processus worker (ou all). Un processus api seul ne synchronise rien et n’exécute aucun workflow.
  • N’envoyez le trafic des utilisateurs qu’aux processus api ou all.
  • Chaque processus ouvre jusqu’à DATABASE_POOL_MAX connexions (10 par défaut). Le total sur tous les processus doit rester inférieur au réglage max_connections de PostgreSQL.
  • Gardez WORKER_CONCURRENCY (4 par défaut) inférieur à DATABASE_POOL_MAX sur les processus de fond.