Paperless-ngx su VPS: installazione con Docker Compose
Installa Paperless-ngx su un VPS con Docker Compose: stack Postgres ufficiale, PAPERLESS_URL, cartella consume, OCR, HTTPS e backup dei documenti.
Cosa stai realizzando
Paperless-ngx su un VPS trasforma una cartella di documenti cartacei scansionati in un archivio consultabile. Inserisci un PDF in una directory monitorata. Il server esegue l'OCR (riconoscimento ottico dei caratteri), estrae il testo, stima una data e un corrispondente, quindi archivia il documento. L'installazione consiste in un unico file Docker Compose con quattro servizi. Tutto ciò che segue riguarda la configurazione. La guida dedica la maggior parte dello spazio a questa fase, perché è qui che le installazioni si interrompono.
Paperless-ngx è il fork della community attivamente mantenuto del progetto Paperless originale. È gratuito, self-hosted e memorizza i documenti come file normali sul disco, quindi non perdi mai l'accesso al tuo archivio. Eseguirlo su un VPS invece che su un computer di casa rende le scansioni accessibili ovunque senza aprire una porta sul router domestico e si integra bene con un'istanza privata di Nextcloud per i file che non sono documenti cartacei.
Cosa esegue effettivamente lo stack
Il file compose ufficiale avvia quattro container. Sapere a cosa serve ciascuno rende i log più facili da interpretare.
webserver: l'immagine di paperless-ngx. Esegue l'interfaccia Web, l'API, il consumer che monitora la cartella di input e i worker delle attività Celery che eseguono l'OCR.db: PostgreSQL. Contiene i metadati, i tag, i corrispondenti e le tabelle dell'indice per la ricerca full-text. Non contiene i file PDF.broker: Valkey, un archivio chiave-valore compatibile con Redis. Funge da coda delle attività tra il processo Web e i worker.gotenbergetika: facoltativi, solo nelle varianti compose-tika. Convertono i documenti Office (.docx,.xlsx,.odt) in PDF, così paperless può indicizzarli.
A luglio 2026, il file compose per postgres fissa docker.io/library/postgres:18 e docker.io/valkey/valkey:9-alpine e scarica l'app da ghcr.io/paperless-ngx/paperless-ngx:latest.
Prerequisiti
- Un VPS KVM con Ubuntu 24.04 e accesso sudo, con Docker e il plugin Compose già installati. Se questa parte è nuova, inizia con i fondamenti di Docker Compose per un VPS e poi torna qui.
- Un nome di dominio con un record A che punta al VPS. Paperless rifiuta di servire le richieste su un hostname che non è stato configurato, quindi questo requisito è importante prima di quanto si possa pensare.
- La memoria è il vincolo principale. PostgreSQL, Valkey, gunicorn e un worker Tesseract OCR residenti contemporaneamente rientrano in 2 GB per un uso leggero. Assegna 4 GB se prevedi di importare un arretrato di centinaia di scansioni, perché l'OCR di un PDF multipagina di grandi dimensioni è il picco di memoria che può far terminare un worker da parte dell'oom-killer del kernel.
- Disco: l'archivio viene memorizzato due volte, come file originale e come PDF di archivio elaborato con OCR, quindi considera uno spazio pari a circa il doppio delle dimensioni delle scansioni.
Ottenere i file compose ufficiali
È disponibile un programma di installazione interattivo:
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"Pone alcune domande e scrive i file al posto tuo. Eseguire manualmente questa procedura richiede quattro comandi e ti permette di sapere dove si trova ogni elemento. È la situazione desiderata su un server che dovrai gestire.
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/.envLe varianti si trovano nella stessa directory: docker-compose.sqlite.yml, docker-compose.mariadb.yml e una versione -tika di ciascuna. Scegli postgres per una nuova installazione. SQLite è sufficiente per alcune centinaia di documenti, ma l'indice di ricerca full-text diventa lento molto prima di PostgreSQL.
Il file .env contiene una riga, COMPOSE_PROJECT_NAME=paperless. Questo nome diventa il prefisso di ogni container e volume. Non eliminarlo per poi chiederti perché docker compose down -v non riesce a trovare i tuoi dati.
Configurare docker-compose.env prima del primo avvio
Due impostazioni sono obbligatorie. Generare la chiave segreta con il comando documentato dal progetto:
python3 -c "import secrets; print(secrets.token_urlsafe(64))"Modificare quindi 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=1000PAPERLESS_SECRET_KEY viene fornito con il valore letterale change-me. Firma i cookie di sessione. Se si lascia questo valore, chiunque conosca quello predefinito può falsificare una sessione. Impostarlo prima del primo avvio, perché modificarlo in seguito disconnette tutti gli utenti.
PAPERLESS_URL è l'impostazione che fa risparmiare un'ora. Paperless è un'applicazione Django e Django convalida l'intestazione Host di ogni richiesta. Impostare PAPERLESS_URL per valorizzare automaticamente ALLOWED_HOSTS, CORS_ALLOWED_HOSTS e CSRF_TRUSTED_ORIGINS. Se lo si lascia vuoto, si punta un dominio al server e ogni pagina restituisce Bad Request (400), mentre nel log del container compare DisallowedHost. Inserirlo senza barra finale e senza percorso.
USERMAP_UID e USERMAP_GID impostano l'utente con cui viene eseguito il container. Impostarli in base al proprio account, verificato con id -u e id -g. Se i valori non corrispondono, i file copiati nella cartella consume non sono leggibili dal consumer e nel log compare un errore di autorizzazione invece di un'importazione.
Avviare lo stack e creare il primo utente
docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webservercreatesuperuser richiede un nome utente, un indirizzo email e una password. Non esiste un accesso predefinito. Se si salta questo passaggio, viene visualizzata una pagina di accesso che non accetterà mai alcuna credenziale. Attendere la riga del log che indica che il server è in ascolto sulla porta 8000 prima di provare ad accedere dal browser. Al primo avvio vengono eseguite anche le migrazioni del database, che richiedono uno o due minuti.
Verificare il funzionamento localmente prima di configurare un dominio:
curl -I http://127.0.0.1:8000Un reindirizzamento 302 a /accounts/login/ indica che lo stack funziona correttamente.
Anteporre HTTPS all'applicazione
Il file Compose predefinito pubblica 8000:8000, associandolo a tutte le interfacce. Su un VPS pubblico, questo espone l'intero archivio di documenti tramite HTTP non cifrato a chiunque trovi l'indirizzo. Modificate la riga della porta per associarla solo all'interfaccia di loopback:
ports:
- "127.0.0.1:8000:8000"Terminate quindi TLS (sicurezza del livello di trasporto) in un reverse proxy e inoltrate le richieste a 127.0.0.1:8000. Se questa è l'unica applicazione sul server, è sufficiente qualsiasi proxy con un client ACME (ambiente per la gestione automatica dei certificati). Se eseguite più container dietro un'unica configurazione dei certificati, seguite lo schema di reverse proxy Traefik per più applicazioni Docker Compose e collegate il servizio webserver alla rete del proxy senza pubblicare alcuna porta.
Qualunque proxy utilizziate, deve inviare X-Forwarded-Proto: https. Senza questa intestazione, Django considera la richiesta come arrivata tramite HTTP, il controllo dell'origine nel modulo di accesso non riesce e viene restituito CSRF verification failed. Request aborted. su una pagina che appare corretta. L'altra parte della correzione consiste nell'impostare PAPERLESS_URL sull'indirizzo https:// esatto che digitate nel browser.
Aumentate anche il limite delle dimensioni degli upload del proxy. Una scansione di 40 MB inviata tramite un proxy che limita il corpo delle richieste a 1 MB viene rifiutata prima che paperless possa riceverla, e il browser segnala un errore generico di caricamento.
Come funziona la directory di importazione
Il file compose monta ./consume dalla directory compose nel container. Tutto ciò che si inserisce in questa directory viene importato e poi eliminato, perché il file si trova ormai nel volume dei contenuti multimediali gestito da paperless.
cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserverDovreste vedere il consumer rilevare il nome del file, eseguire l'OCR e terminare con una riga che segnala l'aggiunta del documento. Per una scansione di una pagina, l'intero ciclo richiede pochi secondi. Per un documento lungo, può richiedere un minuto o più.
Due impostazioni modificano la ricerca dei file. PAPERLESS_CONSUMER_RECURSIVE=true indica a paperless di cercare nelle sottodirectory, mentre PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true converte il nome di ogni sottodirectory in un tag. Di conseguenza, inserire un file in consume/invoices/2026/ applica i tag invoices e 2026. È il sistema di archiviazione più economico che possiate realizzare.
Il rilevamento è l'altra parte del processo. Per impostazione predefinita, PAPERLESS_CONSUMER_POLLING_INTERVAL è 0. Questo significa che paperless usa le notifiche del filesystem del kernel, che vengono generate immediatamente. Queste notifiche non attraversano un filesystem di rete. Se la directory di importazione è una condivisione NFS o SMB nella quale uno scanner di rete può scrivere, i file non vengono mai rilevati. Per risolvere il problema, impostate l'intervallo su un numero positivo di secondi, in modo che paperless esegua la scansione della directory.
Lingue OCR e relativo costo
PAPERLESS_OCR_LANGUAGE accetta un codice Tesseract di tre lettere, eng per impostazione predefinita. Combina le lingue con un segno più, come in deu+eng. Tesseract prova quindi ciascuna lingua e mantiene il risultato migliore. Ogni lingua aggiuntiva moltiplica il tempo CPU impiegato per ogni pagina. Su una VPS con vCPU condivisa, questa può essere la differenza tra completare una scansione in dieci secondi o in un minuto. Elenca solo le lingue effettivamente presenti nei documenti.
L'immagine include inglese, tedesco, italiano, spagnolo e francese. Per qualsiasi altra lingua, aggiungila a PAPERLESS_OCR_LANGUAGES come elenco separato da spazi, ad esempio PAPERLESS_OCR_LANGUAGES=tur ces, quindi riavvia. Il container scarica i pacchetti di dati Tesseract all'avvio, quindi il primo avvio dopo questa modifica è più lento.
Eseguire il backup del database e dei file multimediali
Copiare i volumi Docker mentre PostgreSQL è in esecuzione produce un backup che potrebbe non essere ripristinabile. Paperless include un esportatore che scrive i documenti e un manifest JSON con tutti i metadati nel bind mount ./export:
docker compose exec webserver document_exporter ../export --delete --no-progress-bar--delete rimuove i file esportati che non corrispondono più a un documento attuale, così la cartella rimane un mirror invece di crescere senza limiti. --no-progress-bar mantiene pulito l'output quando il comando viene eseguito da cron.
Il ripristino consiste nell'eseguire document_importer sulla stessa cartella in un nuovo stack. Di conseguenza, l'unico elemento da proteggere è la directory di esportazione. Inviatela fuori sede secondo una pianificazione con backup restic crittografati e deduplicati dal vostro VPS ed eseguite prima l'esportazione, così restic non acquisisce mai un archivio scritto solo parzialmente.
Verificate un backup controllando che esista export/manifest.json e che il numero di file corrisponda al numero di documenti mostrato nell'interfaccia. Un backup che non avete mai elencato non è un backup.
FAQ
Perché ogni pagina restituisce "Bad Request (400)" dopo aver associato il dominio?
Django ha rifiutato l'header Host perché il dominio non è presente in ALLOWED_HOSTS. Imposta PAPERLESS_URL=https://paperless.example.com in docker-compose.env, senza slash finale, quindi esegui docker compose up -d per ricreare il container. Modificare solo il file env non basta, perché il container in esecuzione conserva l'ambiente con cui è stato avviato.
Ho inserito un PDF nella cartella consume, ma non è successo nulla. Qual è il problema?
Controlla prima docker compose logs webserver. Un errore di autorizzazione indica che USERMAP_UID e USERMAP_GID non corrispondono all'account proprietario del file. Correggi questi valori e ricrea il container. Se non compare alcuna riga nei log, l'evento relativo al file non è mai arrivato. Questo accade sulle condivisioni di rete perché le notifiche del kernel non le attraversano. Imposta PAPERLESS_CONSUMER_POLLING_INTERVAL su un valore come 30 e paperless eseguirà invece la scansione della cartella ogni 30 secondi.
Posso eseguire paperless-ngx con SQLite invece di PostgreSQL?
Sì, docker-compose.sqlite.yml è supportato e usa meno memoria, quindi è adatto a un VPS di piccole dimensioni. Il compromesso diventa evidente quando l'archivio cresce: la ricerca full-text e le modifiche collettive ai tag rallentano sensibilmente quando i documenti diventano migliaia. Una migrazione successiva richiede un'esportazione e un'importazione. Scegli PostgreSQL fin dall'inizio se prevedi che l'archivio continuerà a crescere.
Quanto spazio su disco richiede realmente un archivio di scansioni?
Circa il doppio delle dimensioni dei file originali. Paperless conserva l'originale senza modificarlo e memorizza un secondo PDF elaborato con OCR e un livello di testo ricercabile, oltre a piccole miniature. Una scansione di solo testo da 200 KB occupa poco spazio. Una scansione a colori da 30 MB di un contratto lungo occupa circa 60 MB. Aggiungi la directory di esportazione se la conservi sullo stesso disco: in questo caso lo stesso archivio occupa tre volte lo spazio.
Sono necessari i container Tika e Gotenberg?
Solo se vuoi indicizzare file Word, Excel o OpenDocument insieme ai PDF. Questi container convertono i formati in PDF, così paperless può eseguire l'OCR e cercare il testo. Aggiungono inoltre altri due container in esecuzione e alcune centinaia di megabyte di memoria. Se tutti i documenti archiviati sono già PDF o immagini, puoi ometterli su un host di piccole dimensioni.