SSD Nodes Learn 8GB de RAM — $66/an
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-02

Installer Paperless-ngx sur un VPS avec Docker Compose

Installez Paperless-ngx sur un VPS avec Docker Compose et Postgres. Configurez PAPERLESS_URL, le dossier consume, l’OCR, HTTPS et les sauvegardes.

Ce que vous allez créer

Paperless-ngx sur un VPS transforme un dossier de documents numérisés en archive interrogeable. Vous déposez un PDF dans un répertoire surveillé. Le serveur exécute l’OCR (reconnaissance optique de caractères), en extrait le texte, détermine une date et un correspondant, puis le classe. L’installation utilise un seul fichier Docker Compose avec quatre services. Tout le reste concerne la configuration. Ce guide lui consacre l’essentiel de son contenu, car c’est à cette étape que les installations échouent.

Paperless-ngx est le fork communautaire maintenu du projet Paperless d’origine. Il est gratuit, auto-hébergé et stocke vos documents sous forme de fichiers ordinaires sur le disque. Vous ne perdez donc jamais l’accès à votre propre archive. L’exécuter sur un VPS plutôt que sur un ordinateur personnel signifie que vos documents numérisés sont accessibles depuis n’importe où, sans ouvrir de port sur votre routeur domestique. Il s’associe bien à une instance Nextcloud privée pour les fichiers qui ne sont pas du papier.

Ce que la stack exécute réellement

Le fichier Compose officiel démarre quatre conteneurs. Savoir quel est le rôle de chacun facilite la lecture des logs.

  • webserver : l’image paperless-ngx elle-même. Elle exécute l’interface web, l’API, le consumer qui surveille votre dossier d’entrée et les workers de tâches Celery qui effectuent l’OCR.
  • db : PostgreSQL. Il stocke les métadonnées, les tags, les correspondants et les tables d’indexation de la recherche en texte intégral. Il ne stocke pas vos PDF.
  • broker : Valkey, un magasin clé-valeur compatible avec Redis. Il sert de file d’attente des tâches entre le processus web et les workers.
  • gotenberg et tika : facultatifs, uniquement dans les variantes Compose -tika. Ils convertissent les documents Office (.docx, .xlsx, .odt) en PDF afin que paperless puisse les indexer.

En juillet 2026, le fichier Compose pour postgres épingle docker.io/library/postgres:18 et docker.io/valkey/valkey:9-alpine, et récupère l’application depuis ghcr.io/paperless-ngx/paperless-ngx:latest.

Prérequis

  • Un VPS KVM sous Ubuntu 24.04 avec un accès sudo, et Docker avec le plugin Compose déjà installé. Si cette partie est nouvelle pour vous, commencez par les bases de Docker Compose pour un VPS, puis revenez ici.
  • Un nom de domaine avec un enregistrement A pointant vers le VPS. Paperless refuse de servir un hostname qui ne lui a pas été indiqué. Ce point est donc important plus tôt que prévu.
  • La mémoire est la contrainte principale. PostgreSQL, Valkey, gunicorn et un worker Tesseract OCR résidents simultanément tiennent dans 2 GB pour une utilisation légère. Prévoyez 4 GB si vous comptez importer un backlog de centaines de scans, car l’OCR d’un PDF multipage volumineux provoque le pic de mémoire qui peut faire tuer un worker par l’out-of-memory killer du kernel.
  • Disque : votre archive est stockée deux fois, sous la forme du fichier original et d’un PDF d’archive OCRisé. Prévoyez donc environ le double de la taille de vos scans.

Obtenir les fichiers compose officiels

Il existe un installateur interactif :

bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"

Il pose des questions et écrit les fichiers pour vous. Le faire manuellement nécessite quatre commandes et vous permet de savoir où se trouve chaque élément. C’est ce qu’il vous faut sur un serveur que vous allez maintenir.

mkdir -p ~/paperless && cd ~/paperless
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.postgres.yml
curl -fsSL -o docker-compose.env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.env
curl -fsSL -o .env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/.env

Les variantes se trouvent dans le même répertoire : docker-compose.sqlite.yml, docker-compose.mariadb.yml et une version -tika de chacune. Choisissez postgres pour une nouvelle installation. SQLite convient pour quelques centaines de documents, mais l’index de recherche en texte intégral ralentit bien avant PostgreSQL.

Le fichier .env contient une ligne, COMPOSE_PROJECT_NAME=paperless. Ce nom devient le préfixe de chaque conteneur et volume. Ne le supprimez donc pas en vous demandant ensuite pourquoi docker compose down -v ne trouve plus vos données.

Configurer docker-compose.env avant le premier démarrage

Deux paramètres sont obligatoires. Générez la clé secrète avec la commande documentée par le projet :

python3 -c "import secrets; print(secrets.token_urlsafe(64))"

Modifiez ensuite docker-compose.env :

PAPERLESS_SECRET_KEY=<the long string you just generated>
PAPERLESS_URL=https://paperless.example.com
PAPERLESS_TIME_ZONE=Europe/Berlin
PAPERLESS_OCR_LANGUAGE=deu+eng
USERMAP_UID=1000
USERMAP_GID=1000

PAPERLESS_SECRET_KEY est fourni avec la valeur littérale change-me. Il signe les cookies de session. Si vous le laissez ainsi, toute personne connaissant la valeur par défaut peut falsifier une session. Définissez-le avant le premier démarrage, car sa modification ultérieure déconnecte tous les utilisateurs.

PAPERLESS_URL est le paramètre qui vous évite une heure de diagnostic. Paperless est une application Django, et Django valide l’en-tête Host de chaque requête. Définissez PAPERLESS_URL et il renseigne automatiquement ALLOWED_HOSTS, CORS_ALLOWED_HOSTS et CSRF_TRUSTED_ORIGINS. Si vous le laissez vide, que vous associez un domaine au serveur, chaque page renvoie Bad Request (400) et le journal du conteneur contient DisallowedHost. Écrivez-le sans slash final ni chemin.

USERMAP_UID et USERMAP_GID définissent l’utilisateur sous lequel le conteneur s’exécute. Faites-les correspondre à votre propre compte, vérifié avec id -u et id -g. S’ils ne correspondent pas, les fichiers que vous copiez dans le dossier consume ne sont pas lisibles par le consumer, et le journal affiche une erreur de permission au lieu d’une importation.

Démarrer la stack et créer le premier utilisateur

docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webserver

createsuperuser demande un nom d’utilisateur, une adresse e-mail et un mot de passe. Il n’existe aucun identifiant par défaut. Si vous ignorez cette étape, vous arrivez sur une page de connexion qui n’acceptera aucun identifiant. Attendez la ligne de log indiquant que le serveur écoute sur le port 8000 avant d’ouvrir le navigateur. Le tout premier démarrage exécute également les migrations de la base de données. Cette opération prend une à deux minutes.

Vérifiez le fonctionnement en local avant d’utiliser un domaine :

curl -I http://127.0.0.1:8000

Une redirection 302 vers /accounts/login/ signifie que la stack fonctionne correctement.

Placez HTTPS devant l’application

Le fichier compose standard publie 8000:8000, qui est lié à toutes les interfaces. Sur un VPS public, votre archive documentaire complète est ainsi accessible en HTTP non chiffré à toute personne qui trouve l’adresse. Modifiez la ligne du port pour la lier uniquement à l’interface loopback :

    ports:
      - "127.0.0.1:8000:8000"

Terminez ensuite TLS (transport layer security) dans un reverse proxy et transférez les requêtes vers 127.0.0.1:8000. S’il s’agit de la seule application sur le serveur, n’importe quel proxy équipé d’un client ACME (automatic certificate management environment) convient. Si plusieurs conteneurs utilisent une même configuration de certificats, suivez le modèle de reverse proxy Traefik pour plusieurs applications Docker Compose et rattachez le service webserver au réseau du proxy, sans aucun port publié.

Quel que soit le proxy utilisé, il doit envoyer X-Forwarded-Proto: https. Sans cet en-tête, Django considère que la requête est arrivée en HTTP, le contrôle de l’origine du formulaire de connexion échoue et vous obtenez CSRF verification failed. Request aborted. sur une page qui semble correcte. L’autre moitié de cette correction consiste à définir PAPERLESS_URL sur l’adresse https:// exacte que vous saisissez dans le navigateur.

Augmentez également la limite de taille des téléversements du proxy. Un scan de 40 MB traversant un proxy qui limite la taille du corps à 1 MB est rejeté avant que paperless ne le reçoive, et le navigateur affiche une erreur générique de téléversement.

Fonctionnement du répertoire consume

Le fichier compose monte ./consume depuis le répertoire compose dans le conteneur. Tout ce que vous y placez est importé, puis supprimé du répertoire, car le fichier se trouve désormais dans le volume media géré par paperless.

cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserver

Le consumer doit détecter le nom du fichier, lancer l’OCR, puis terminer en indiquant que le document a été ajouté. Pour un scan d’une page, le cycle complet prend quelques secondes. Pour un document long, il peut prendre une minute ou plus.

Deux paramètres modifient la recherche des fichiers. PAPERLESS_CONSUMER_RECURSIVE=true demande à paperless de rechercher dans les sous-répertoires. PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true transforme le nom de chaque sous-répertoire en tag. Ainsi, déposer un fichier dans consume/invoices/2026/ lui attribue les tags invoices et 2026. C’est le système de classement le moins coûteux que vous puissiez mettre en place.

La détection constitue l’autre aspect. Par défaut, PAPERLESS_CONSUMER_POLLING_INTERVAL vaut 0. paperless utilise donc les notifications du système de fichiers du kernel, qui sont déclenchées immédiatement. Ces notifications ne traversent pas un système de fichiers réseau. Si votre répertoire consume est un partage NFS ou SMB dans lequel un scanner réseau peut écrire, aucun fichier n’est détecté. Pour corriger ce problème, définissez l’intervalle sur un nombre de secondes positif afin que paperless analyse le répertoire à intervalles réguliers.

Langues OCR et coût associé

PAPERLESS_OCR_LANGUAGE accepte un code Tesseract de trois lettres, eng par défaut. Combinez les langues avec un signe plus, comme dans deu+eng. Tesseract essaie alors chaque langue et conserve le meilleur résultat. Chaque langue supplémentaire multiplie donc le temps CPU consacré à chaque page. Sur un VPS avec vCPU partagé, cela peut faire la différence entre un scan qui se termine en dix secondes et un autre qui prend une minute. Indiquez uniquement les langues dans lesquelles vos documents sont réellement rédigés.

L’image inclut l’anglais, l’allemand, l’italien, l’espagnol et le français. Pour toute autre langue, ajoutez-la à PAPERLESS_OCR_LANGUAGES sous forme de liste séparée par des espaces, par exemple PAPERLESS_OCR_LANGUAGES=tur ces, puis redémarrez. Le conteneur télécharge les packs de données Tesseract au démarrage. Le premier démarrage suivant cette modification est donc plus lent.

Sauvegarder la base de données et les fichiers multimédias

Copier les volumes Docker pendant l’exécution de PostgreSQL produit une sauvegarde qui risque de ne pas pouvoir être restaurée. Paperless fournit son propre outil d’exportation. Celui-ci écrit les documents ainsi qu’un manifeste JSON contenant toutes les métadonnées dans le bind mount ./export :

docker compose exec webserver document_exporter ../export --delete --no-progress-bar

--delete supprime les fichiers exportés qui ne correspondent plus à un document actuel. Le dossier reste ainsi un miroir au lieu de croître indéfiniment. --no-progress-bar évite les sorties inutiles lorsque la commande est exécutée par cron.

La restauration s’effectue avec document_importer depuis ce même dossier sur une stack vierge. Le répertoire d’exportation est donc le seul élément à conserver en sécurité. Envoyez-le hors site selon un calendrier avec des sauvegardes restic chiffrées et dédupliquées depuis votre VPS, et exécutez d’abord l’exportation afin que restic ne capture jamais une archive partiellement écrite.

Vérifiez une sauvegarde en contrôlant que export/manifest.json existe et que le nombre de fichiers correspond au nombre de documents affiché dans l’interface. Une sauvegarde dont vous n’avez jamais vérifié le contenu n’est pas une sauvegarde.

FAQ

Pourquoi chaque page renvoie-t-elle « Bad Request (400) » après avoir associé mon domaine à l’application ?

Django a rejeté l’en-tête Host parce que votre domaine ne figure pas dans ALLOWED_HOSTS. Définissez PAPERLESS_URL=https://paperless.example.com dans docker-compose.env, sans slash final, puis exécutez docker compose up -d pour recréer le conteneur. Modifier uniquement le fichier d’environnement ne suffit pas, car le conteneur en cours d’exécution conserve l’environnement avec lequel il a été démarré.

J’ai déposé un PDF dans le dossier consume, mais rien ne s’est produit. Quel est le problème ?

Vérifiez d’abord docker compose logs webserver. Une erreur de permissions signifie que USERMAP_UID et USERMAP_GID ne correspondent pas au compte propriétaire du fichier. Corrigez ces valeurs, puis recréez le conteneur. L’absence totale de ligne dans les logs signifie que l’événement lié au fichier n’est jamais arrivé. Cela se produit avec les partages réseau, car les notifications du noyau ne les traversent pas. Définissez PAPERLESS_CONSUMER_POLLING_INTERVAL sur une valeur telle que 30 ; paperless analysera alors le dossier toutes les 30 secondes.

Puis-je exécuter paperless-ngx avec SQLite au lieu de PostgreSQL ?

Oui, docker-compose.sqlite.yml est pris en charge et utilise moins de mémoire, ce qui convient à un petit VPS. Le compromis devient visible lorsque l’archive augmente : la recherche en texte intégral et les modifications groupées de tags ralentissent sensiblement lorsque l’archive atteint plusieurs milliers de documents. Une migration ultérieure nécessite un export et un import. Choisissez donc PostgreSQL dès maintenant si vous prévoyez de continuer à faire croître l’archive.

Quel espace disque une archive de documents numérisés nécessite-t-elle réellement ?

Prévoyez environ deux fois la taille de vos fichiers source. Paperless conserve l’original intact et stocke un second PDF traité par OCR, avec une couche de texte interrogeable, ainsi que de petites miniatures. Un document numérisé de 200 KB contenant uniquement du texte reste peu volumineux. La numérisation couleur de 30 MB d’un long contrat occupe environ 60 MB. Ajoutez le répertoire d’export si vous le conservez sur le même disque : la même archive occupe alors trois fois plus d’espace disque.

Ai-je besoin des conteneurs Tika et Gotenberg ?

Seulement si vous voulez indexer des fichiers Word, Excel ou OpenDocument avec vos PDF. Ils convertissent ces formats en PDF afin que paperless puisse les traiter par OCR et les rechercher. Ils ajoutent aussi 2 conteneurs en cours d’exécution et quelques centaines de mégaoctets de mémoire. Ne les utilisez donc pas sur une petite machine si tous les documents que vous classez sont déjà des PDF ou des images.

#paperless-ngx#documents#auto-hébergement#docker#ocr