Pendant des années, j'avais un dossier "À classer" sur mon bureau. Un vrai cimetière de PDFs. Relevés bancaires, factures EDF, avis d'imposition, contrats d'assurance — tout s'accumulait dans ce dossier en espérant qu'un jour je prendrais le temps de trier. Ce jour n'est jamais arrivé. Et le jour où j'ai eu besoin de retrouver ma déclaration d'impôts de 2021, j'ai passé 45 minutes à fouiller dans un chaos de fichiers nommés "scan0023.pdf" et "document_final_v2.pdf".
C'est là que j'ai mis en place Paperless-NGX. Aujourd'hui, chaque document que je numérise ou télécharge est classé, tagué et indexé automatiquement en moins de deux minutes. Je retrouve n'importe quelle facture en tapant deux mots dans une barre de recherche. Et tout tourne dans un conteneur Docker, derrière Traefik, avec les fichiers stockés sur mon NAS Synology.
Voilà comment j'ai monté ça.
Ce que fait Paperless-NGX
Paperless-NGX c'est un gestionnaire documentaire open-source. Vous lui donnez des documents — PDF, images, fichiers scannés — il fait l'OCR, extrait le texte, et vous permet de les retrouver via une interface web ou une API. Rien de magique là-dedans.
Ce qui est vraiment utile, c'est ce qui vient après : la classification automatique. Vous définissez des règles (ce document contient "EDF" → tag "Énergie", correspond à ce pattern → correspondant "EDF"), et Paperless applique ça à chaque nouveau document. Plus vous utilisez le système et corrigez les erreurs de classification, plus il devient précis. Il y a un classifieur intégré qui apprend de vos corrections — j'y reviens plus loin.
L'autre fonctionnalité qui m'a convaincu : le dossier de consommation. Vous déposez un fichier dans un dossier, Paperless le détecte, l'importe, le classe, et l'original disparaît. Pour moi, ce dossier est monté sur mon NAS Synology. Depuis n'importe quelle machine du réseau, je glisse un PDF dans le dossier, et deux minutes plus tard il est dans Paperless, classé, indexé, consultable.
La stack Docker
Paperless-NGX nécessite trois conteneurs : l'application principale, une instance Redis pour la gestion des tâches asynchrones, et une base de données PostgreSQL. J'ai tout mis dans un stack Portainer, avec des labels Traefik pour l'exposition HTTPS.
version: "3.4"
services:
paperless-redis:
image: docker.io/library/redis:7
container_name: paperless-redis
restart: unless-stopped
volumes:
- /home/arnaud/paperless/redis:/data
paperless-db:
image: docker.io/library/postgres:15
container_name: paperless-db
restart: unless-stopped
volumes:
- /home/arnaud/paperless/pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: ${PAPERLESS_DB_PASSWORD}
paperless-ngx:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
container_name: paperless-ngx
restart: unless-stopped
depends_on:
- paperless-db
- paperless-redis
volumes:
- /home/arnaud/paperless/data:/usr/src/paperless/data
- /home/arnaud/paperless/media:/usr/src/paperless/media
- /mnt/nas/paperless/consume:/usr/src/paperless/consume
- /home/arnaud/paperless/export:/usr/src/paperless/export
environment:
PAPERLESS_REDIS: redis://paperless-redis:6379
PAPERLESS_DBHOST: paperless-db
PAPERLESS_DBNAME: paperless
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASS: ${PAPERLESS_DB_PASSWORD}
PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
PAPERLESS_URL: https://paperless.arnaudallouche.fr
PAPERLESS_TIME_ZONE: Europe/Paris
PAPERLESS_OCR_LANGUAGE: fra+eng
PAPERLESS_FILENAME_FORMAT: "{created_year}/{correspondent}/{title}"
USERMAP_UID: 1000
USERMAP_GID: 1000
labels:
- "traefik.enable=true"
- "traefik.http.routers.paperless.rule=Host(`paperless.arnaudallouche.fr`)"
- "traefik.http.routers.paperless.entrypoints=websecure"
- "traefik.http.routers.paperless.tls.certresolver=letsencrypt"
- "traefik.http.services.paperless.loadbalancer.server.port=8000"
networks:
default:
external:
name: traefik_web
Quelques points qui méritent d'être expliqués.
PAPERLESS_FILENAME_FORMAT contrôle comment les fichiers sont nommés et organisés sur le disque. Avec {created_year}/{correspondent}/{title}, mes documents sont rangés dans media/documents/originals/2024/EDF/Facture_janvier.pdf. Si vous ne configurez pas ça dès le départ, vous vous retrouvez avec des milliers de fichiers à la racine du dossier media. Je l'ai appris à mes dépens — j'ai dû relancer l'import de 200 documents après avoir changé ce paramètre.
PAPERLESS_OCR_LANGUAGE: fra+eng — j'ai des documents en français et en anglais (factures d'hébergement, licences logicielles). Sans ça, l'OCR sur les documents français donnait des résultats catastrophiques.
Le réseau traefik_web est le réseau Docker partagé entre Traefik et tous mes services. Si vous n'avez pas encore ce setup, je renvoie vers mon article sur Traefik — c'est le prérequis.
Monter le NAS Synology comme dossier de consommation
C'est la partie qui m'a pris le plus de temps à stabiliser. L'idée : le dossier consume de Paperless pointe vers un partage du NAS Synology, accessible depuis toutes mes machines. Je dépose un PDF depuis n'importe où sur le réseau, Paperless l'ingère automatiquement.
La première approche — monter directement depuis docker-compose avec un driver CIFS — m'a donné des erreurs de permission en cascade. J'ai opté pour quelque chose de plus simple : monter le partage NAS sur l'hôte via /etc/fstab, puis faire un simple bind mount dans le conteneur.
Sur le NAS Synology, j'ai créé un dossier partagé paperless-consume avec un utilisateur dédié. Ensuite sur l'hôte Docker :
# Installer le support CIFS
sudo apt install cifs-utils
# Créer le point de montage
sudo mkdir -p /mnt/nas/paperless/consume
# Créer le fichier de credentials
sudo nano /etc/nas-credentials
# username=paperless_user
# password=votre_mot_de_passe
sudo chmod 600 /etc/nas-credentials
Puis dans /etc/fstab :
//192.168.1.10/paperless-consume /mnt/nas/paperless/consume cifs credentials=/etc/nas-credentials,uid=1000,gid=1000,file_mode=0664,dir_mode=0775,_netdev 0 0
sudo mount -a
# Vérifier que ça monte sans erreur
ls /mnt/nas/paperless/consume
Et dans docker-compose, le bind mount classique :
volumes:
- /mnt/nas/paperless/consume:/usr/src/paperless/consume
Résultat : depuis mon Mac, mon laptop Windows ou n'importe quelle machine sur le réseau local, je glisse un document dans le dossier partagé Synology. Paperless le détecte en quelques secondes, lance l'OCR, et le document est importé. Le fichier original est supprimé du dossier consume une fois l'import terminé — comportement par défaut que je n'ai pas changé.
Un truc à anticiper : si votre serveur Docker redémarre avant que le NAS soit monté (cas typique après une coupure de courant), le conteneur Paperless va démarrer avec un dossier consume vide sur l'hôte plutôt que le partage NAS. La directive _netdev dans fstab indique au système d'attendre la disponibilité du réseau avant de monter, mais ce n'est pas toujours suffisant selon l'ordre de démarrage. J'ai ajouté un healthcheck léger dans Portainer pour éviter ça.
L'apprentissage automatique en pratique
Paperless embarque un classifieur qui apprend de vos corrections. Au début, vous allez assigner manuellement les correspondants, les tags et les types de documents. Après quelques dizaines de documents corrigés, le système commence à proposer des classifications et à les appliquer automatiquement.
La configuration par défaut ré-entraîne le classifieur toutes les heures. Je n'ai rien changé à ça. Ce qui m'a pris du temps à comprendre, c'est que la qualité de l'OCR conditionne tout. Un document mal scanné — trop sombre, pages de travers, résolution trop faible — donne un OCR approximatif, et le classifieur ne peut pas apprendre grand chose d'un texte illisible. J'utilise une résolution minimum de 300 DPI pour tous mes scans depuis que j'ai compris ça.
Les règles de correspondance complètent le classifieur pour les cas simples. Pour EDF par exemple :
- Correspondant "EDF" déclenché si le texte contient "EDF" ou "Electricité De France"
- Type de document "Facture" si le texte contient "facture" et un montant
- Tag "Énergie" automatiquement associé à ce correspondant
Ces règles regex sont prioritaires sur le classifieur. Pour les documents récurrents et prévisibles (factures opérateurs, relevés bancaires), les règles regex donnent 100% de précision. Le classifieur prend le relais pour les documents moins structurés.
Après six mois d'utilisation, le classifieur reconnaît correctement environ 85% des documents sans intervention de ma part. Les 15% restants sont généralement des documents nouveaux — un nouveau correspondant, un nouveau type de document — que je classifie manuellement une fois, et qui sont reconnus correctement la fois d'après.
Ce que ça change au quotidien
Je scanne physiquement beaucoup moins qu'avant parce que la plupart de mes documents arrivent déjà en PDF par email. J'ai configuré une règle de transfert automatique dans mon client mail : tout email avec une pièce jointe PDF venant de certains expéditeurs (banque, assurances, opérateurs) est redirigé vers une adresse mail spécifique que Paperless surveille. Le document est importé automatiquement sans que j'intervienne.
Pour les documents physiques, j'utilise l'application mobile CamScanner pour numériser depuis mon téléphone et déposer directement dans le dossier Synology via l'app DS File. Trente secondes pour numériser une feuille, deux minutes plus tard elle est dans Paperless.
La recherche full-text est ce qui change le plus les habitudes. Retrouver ma garantie de réfrigérateur achetée en 2020 : je tape "réfrigérateur garantie" dans la barre de recherche. Deux secondes. Avant, j'aurais passé dix minutes à fouiller dans des dossiers en espérant avoir bien classé le document à l'époque.
Ce qui ne fonctionne pas parfaitement
L'OCR sur certains documents reste approximatif. Les relevés bancaires anciens scannés en basse résolution, les documents avec des tampons par-dessus le texte, les formulaires administratifs avec des cases à cocher — Tesseract s'en sort mal. Je les classifie manuellement et je passe à autre chose. Ce n'est pas un problème bloquant, mais il faut avoir des attentes réalistes sur ce que l'OCR peut faire.
Les mises à jour de l'image Docker peuvent casser des choses. J'utilise :latest parce que je veux les dernières corrections de sécurité, mais deux fois en un an une mise à jour a nécessité une migration de base de données manuelle. Pas dramatique, mais prévoir une sauvegarde avant chaque mise à jour. J'ai un script de backup du dossier pgdata qui tourne chaque nuit via un cron sur le NAS.
L'interface web est fonctionnelle mais pas particulièrement rapide. Sur mobile c'est utilisable, mais ce n'est pas conçu pour être une app mobile. Pour la consultation rapide, j'utilise plutôt la recherche depuis un navigateur desktop.
La configuration complète en quelques commandes
Si vous partez de zéro avec Portainer et Traefik déjà en place, voilà le minimum pour démarrer :
# Créer les dossiers sur l'hôte
mkdir -p /home/arnaud/paperless/{data,media,export,redis,pgdata}
# Générer une clé secrète
openssl rand -base64 32
Déployez le stack dans Portainer, attendez que les trois conteneurs soient healthy, puis créez le premier utilisateur :
docker exec -it paperless-ngx python3 manage.py createsuperuser
Déposez un premier document dans le dossier consume, attendez deux minutes, et vérifiez qu'il apparaît dans l'interface. Si l'OCR fonctionne, vous verrez le texte extrait dans l'aperçu. Si le document apparaît mais que le texte est vide, vérifiez le paramètre PAPERLESS_OCR_LANGUAGE — c'est souvent là que ça bloque.
Paperless-NGX c'est un des rares outils que j'ai mis en place et que j'ai vraiment utilisé ensuite. La plupart des projets d'auto-hébergement finissent par tourner dans un coin sans vraiment servir. Celui-là a changé quelque chose de concret : je ne perds plus de temps à chercher des documents administratifs. Pour un weekend de setup, c'est un des meilleurs retours sur investissement que j'ai eu.