Paperless-ngx na VPS: instalacja i konfiguracja
Instalacja paperless-ngx na VPS z Docker Compose obejmuje stos PostgreSQL, PAPERLESS_URL, katalog consume, OCR, HTTPS oraz kopie zapasowe.
Co jest instalowane
Paperless-ngx na VPS zamienia folder ze zeskanowanymi dokumentami papierowymi w przeszukiwalne archiwum. Wystarczy umieścić plik PDF w monitorowanym katalogu. Serwer uruchamia na nim OCR (optyczne rozpoznawanie znaków), wyodrębnia tekst, określa prawdopodobną datę i korespondenta, a następnie archiwizuje dokument. Instalacja wymaga jednego pliku Docker Compose z czterema usługami. Wszystkie dalsze czynności dotyczą konfiguracji. Poświęcono jej większość tego poradnika, ponieważ właśnie na tym etapie instalacje najczęściej kończą się niepowodzeniem.
Paperless-ngx to utrzymywana przez społeczność gałąź oryginalnego projektu Paperless. Jest bezpłatny, można go hostować samodzielnie i przechowuje dokumenty jako zwykłe pliki na dysku. Dzięki temu dostęp do własnego archiwum nie jest nigdy blokowany. Uruchomienie go na VPS zamiast na komputerze domowym oznacza, że skany są dostępne z dowolnego miejsca bez otwierania portu na routerze domowym. Dobrze współpracuje również z prywatną instancją Nextcloud dla plików, które nie mają postaci papierowej.
Co faktycznie uruchamia stos
Oficjalny plik Compose uruchamia cztery kontenery. Znajomość funkcji każdego z nich ułatwia analizę dzienników.
webserver: obraz paperless-ngx. Uruchamia interfejs internetowy, API, konsumenta monitorującego folder wejściowy oraz procesy robocze Celery wykonujące OCR.db: PostgreSQL. Przechowuje metadane, tagi, korespondentów oraz tabele indeksu wyszukiwania pełnotekstowego. Nie przechowuje plików PDF.broker: Valkey, magazyn klucz-wartość zgodny z Redis. Jest kolejką zadań między procesem internetowym a procesami roboczymi.gotenbergitika: opcjonalne, występują tylko w wariantach Compose-tika. Konwertują dokumenty Office (.docx,.xlsx,.odt) do formatu PDF, aby paperless mógł je indeksować.
Według stanu na lipiec 2026 plik Compose dla postgres przypina wersje docker.io/library/postgres:18 i docker.io/valkey/valkey:9-alpine oraz pobiera aplikację z ghcr.io/paperless-ngx/paperless-ngx:latest.
Wymagania wstępne
- VPS KVM z Ubuntu 24.04 i dostępem przez sudo oraz z zainstalowanym Dockerem i wtyczką Compose. Jeśli jest to nowy temat, należy rozpocząć od podstaw Docker Compose dla VPS i następnie wrócić do tego przewodnika.
- Nazwa domenowa z rekordem A wskazującym na VPS. Paperless odmawia obsługi nazwy hosta, która nie została skonfigurowana w aplikacji, dlatego należy uwzględnić ten wymóg wcześniej, niż może się wydawać.
- Pamięć jest rzeczywistym ograniczeniem. PostgreSQL, Valkey, gunicorn i worker Tesseract OCR działające jednocześnie mieszczą się w 2 GB przy niewielkim obciążeniu. Należy przydzielić 4 GB, jeśli planowane jest zaimportowanie zaległego zbioru obejmującego setki skanów, ponieważ przetwarzanie OCR dużego wielostronicowego pliku PDF powoduje największy wzrost zużycia pamięci i może doprowadzić do zakończenia workera przez systemowy mechanizm OOM killer.
- Dysk: archiwum jest przechowywane dwukrotnie — jako plik oryginalny oraz jako plik PDF archiwum z wykonanym OCR — dlatego należy zaplanować około dwukrotnie większą ilość miejsca niż rozmiar skanów.
Pobierz oficjalne pliki compose
Dostępny jest instalator interaktywny:
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"Instalator zadaje pytania i zapisuje pliki. Ręczne wykonanie tej czynności wymaga czterech poleceń i zapewnia wiedzę o lokalizacji wszystkich elementów. Jest to istotne w przypadku serwera, który będzie utrzymywany.
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/.envWarianty znajdują się w tym samym katalogu: docker-compose.sqlite.yml, docker-compose.mariadb.yml oraz wersja -tika każdego z nich. W przypadku nowej instalacji należy wybrać postgres. SQLite sprawdza się w przypadku kilkuset dokumentów, ale indeks wyszukiwania pełnotekstowego zaczyna działać wolno znacznie wcześniej niż PostgreSQL.
Plik .env zawiera jeden wiersz: COMPOSE_PROJECT_NAME=paperless. Ta nazwa staje się prefiksem każdego kontenera i woluminu. Nie należy jej usuwać, a następnie zastanawiać się, dlaczego docker compose down -v nie może znaleźć danych.
Skonfiguruj docker-compose.env przed pierwszym uruchomieniem
Dwa ustawienia są wymagane. Wygeneruj klucz tajny za pomocą polecenia opisanego w dokumentacji projektu:
python3 -c "import secrets; print(secrets.token_urlsafe(64))"Następnie edytuj 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 jest dostarczana z dosłowną wartością change-me. Służy do podpisywania plików cookie sesji. Pozostawienie wartości domyślnej oznacza, że każdy, kto ją zna, może sfałszować sesję. Ustaw ją przed pierwszym uruchomieniem, ponieważ późniejsza zmiana wyloguje wszystkich użytkowników.
PAPERLESS_URL pozwala zaoszczędzić godzinę pracy. Paperless jest aplikacją Django, a Django sprawdza nagłówek Host każdego żądania. Ustaw PAPERLESS_URL, a wartości ALLOWED_HOSTS, CORS_ALLOWED_HOSTS i CSRF_TRUSTED_ORIGINS zostaną uzupełnione automatycznie. Jeśli pozostanie puste, po wskazaniu domeny na ten serwer każda strona zwróci Bad Request (400), a w logu kontenera pojawi się DisallowedHost. Wartość należy zapisać bez końcowego ukośnika i bez ścieżki.
USERMAP_UID i USERMAP_GID określają użytkownika, z którego uprawnieniami działa kontener. Należy ustawić je zgodnie z własnym kontem, sprawdzonym za pomocą id -u i id -g. Jeśli wartości się nie zgadzają, pliki skopiowane do folderu consume będą nieczytelne dla procesu consumer, a w logu pojawi się błąd uprawnień zamiast importu.
Uruchomienie stosu i utworzenie pierwszego użytkownika
docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webservercreatesuperuser wyświetla monity o nazwę użytkownika, adres e-mail i hasło. Nie ma domyślnych danych logowania, dlatego pominięcie tego kroku prowadzi do strony logowania, która nie zaakceptuje żadnych danych. Przed użyciem przeglądarki należy zaczekać na wpis w dzienniku informujący, że serwer nasłuchuje na porcie 8000. Przy pierwszym uruchomieniu wykonywane są również migracje bazy danych. Potrwa to minutę lub dwie.
Przed skonfigurowaniem domeny należy sprawdzić działanie lokalnie:
curl -I http://127.0.0.1:8000Przekierowanie 302 do /accounts/login/ oznacza, że stos działa prawidłowo.
Umieść HTTPS przed aplikacją
Standardowy plik compose publikuje 8000:8000, który nasłuchuje na wszystkich interfejsach. Na publicznym VPS udostępnia to całe archiwum dokumentów przez zwykły HTTP każdemu, kto znajdzie ten adres. Zmień wiersz portu tak, aby wiązał port tylko z interfejsem loopback:
ports:
- "127.0.0.1:8000:8000"Następnie zakończ TLS (transport layer security) w odwrotnym proxy i przekazuj żądania do 127.0.0.1:8000. Jeśli jest to jedyna aplikacja na serwerze, wystarczy dowolne proxy z klientem ACME (automatic certificate management environment). Jeśli uruchamianych jest kilka kontenerów za pomocą jednej konfiguracji certyfikatów, postępuj zgodnie z wzorcem odwrotnego proxy Traefik dla wielu aplikacji Docker Compose i dołącz usługę webserver do sieci proxy, bez publikowania portu.
Niezależnie od używanego proxy musi ono przesyłać X-Forwarded-Proto: https. Bez tego Django uznaje, że żądanie dotarło przez HTTP, kontrola origin formularza logowania kończy się niepowodzeniem, a na stronie, która wygląda poprawnie, pojawia się CSRF verification failed. Request aborted.. Drugą częścią tej poprawki jest ustawienie PAPERLESS_URL na dokładny adres https:// wpisywany w przeglądarce.
Należy również zwiększyć limit rozmiaru przesyłanych danych w proxy. Skan o rozmiarze 40 MB przesyłany przez proxy, które ogranicza rozmiar treści do 1 MB, zostanie odrzucony, zanim paperless go otrzyma, a przeglądarka wyświetli ogólny błąd przesyłania.
Jak działa katalog consume
Plik compose montuje powiązany katalog ./consume z katalogu compose w kontenerze. Każdy umieszczony tam plik jest importowany, a następnie usuwany z tego katalogu, ponieważ plik znajduje się już w wolumenie multimediów zarządzanym przez paperless.
cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserverPowinno być widoczne, że consumer wykrywa nazwę pliku, uruchamia OCR i kończy działanie komunikatem informującym o dodaniu dokumentu. Cały cykl trwa kilka sekund w przypadku skanu jednostronicowego. W przypadku długiego dokumentu może trwać minutę lub dłużej.
Dwa ustawienia zmieniają sposób wyszukiwania plików. PAPERLESS_CONSUMER_RECURSIVE=true powoduje, że paperless przeszukuje podkatalogi. PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true zamienia nazwę każdego podkatalogu na tag. Umieszczenie pliku w consume/invoices/2026/ przypisuje mu tag invoices oraz 2026. Jest to najprostszy system porządkowania plików, jaki można utworzyć.
Drugą częścią konfiguracji jest wykrywanie plików. Domyślnie PAPERLESS_CONSUMER_POLLING_INTERVAL ma wartość 0. Oznacza to, że paperless używa powiadomień systemu plików jądra, które są generowane natychmiast. Te powiadomienia nie są przekazywane przez sieciowy system plików. Jeśli katalog consume jest udziałem NFS lub SMB, do którego skaner sieciowy zapisuje pliki, pliki nie będą wykrywane. Należy wtedy ustawić interwał na dodatnią liczbę sekund, aby paperless skanował katalog zamiast korzystać z powiadomień.
Języki OCR i ich koszt
PAPERLESS_OCR_LANGUAGE przyjmuje trzyznakowy kod Tesseract, domyślnie eng. Języki należy łączyć znakiem plus, na przykład deu+eng. Tesseract sprawdza wtedy każdy z nich i zachowuje najlepszy wynik, dlatego każdy dodatkowy język zwiększa czas procesora potrzebny do przetworzenia każdej strony. Na VPS z współdzielonym vCPU oznacza to różnicę między ukończeniem skanowania w dziesięć sekund a ukończeniem go w minutę. Należy podawać tylko języki, w których faktycznie są napisane dokumenty.
Obraz zawiera języki angielski, niemiecki, włoski, hiszpański i francuski. W przypadku innych języków należy dodać język do PAPERLESS_OCR_LANGUAGES jako listę rozdzielaną spacjami, na przykład PAPERLESS_OCR_LANGUAGES=tur ces, a następnie ponownie uruchomić usługę. Kontener pobiera pakiety danych Tesseract podczas uruchamiania, dlatego pierwsze uruchomienie po tej zmianie trwa dłużej.
Tworzenie kopii zapasowej bazy danych i plików multimedialnych
Kopiowanie wolumenów Docker podczas działania PostgreSQL tworzy kopię zapasową, której przywrócenie może się nie powieść. Paperless zawiera własne narzędzie eksportujące, które zapisuje dokumenty oraz manifest JSON zawierający wszystkie metadane w dowiązaniu typu bind mount ./export:
docker compose exec webserver document_exporter ../export --delete --no-progress-bar--delete usuwa wyeksportowane pliki, które nie odpowiadają już aktualnemu dokumentowi, dzięki czemu katalog pozostaje kopią lustrzaną zamiast stale rosnąć. --no-progress-bar utrzymuje porządek w danych wyjściowych, gdy polecenie jest uruchamiane z cron.
Przywracanie polega na użyciu document_importer względem tego samego katalogu w nowym stosie, dlatego jedynym elementem wymagającym bezpiecznego przechowywania jest katalog eksportu. Należy okresowo wysyłać go poza lokalizację za pomocą szyfrowanych kopii zapasowych restic z deduplikacją z VPS, uruchamiając najpierw eksport, aby restic nigdy nie przechwycił częściowo zapisanego archiwum.
Kopię zapasową należy zweryfikować, sprawdzając istnienie export/manifest.json oraz zgodność liczby plików z liczbą dokumentów wyświetlaną w interfejsie. Kopia zapasowa, której nigdy nie przetestowano przez wyświetlenie zawartości, nie jest kopią zapasową.
FAQ
Dlaczego każda strona zwraca „Bad Request (400)” po wskazaniu na nią domeny?
Django odrzuca nagłówek Host, ponieważ domeny nie ma w ALLOWED_HOSTS. Ustaw PAPERLESS_URL=https://paperless.example.com w docker-compose.env bez końcowego ukośnika, a następnie uruchom docker compose up -d, aby odtworzyć kontener. Sama edycja pliku env niczego nie zmienia, ponieważ uruchomiony kontener zachowuje środowisko, z którym został uruchomiony.
W folderze consume umieszczono plik PDF, ale nic się nie stało. Co jest nie tak?
Najpierw sprawdź docker compose logs webserver. Błąd uprawnień oznacza, że USERMAP_UID i USERMAP_GID nie odpowiadają kontu będącemu właścicielem pliku. Należy je poprawić, a następnie odtworzyć kontener. Całkowity brak wpisu w logu oznacza, że zdarzenie dotyczące pliku nie dotarło. Dzieje się tak w przypadku udziałów sieciowych, ponieważ powiadomienia jądra systemu nie są przez nie przekazywane. Ustaw PAPERLESS_CONSUMER_POLLING_INTERVAL na wartość taką jak 30. paperless będzie wtedy skanować folder co 30 sekund.
Czy można uruchomić paperless-ngx z SQLite zamiast PostgreSQL?
Tak. docker-compose.sqlite.yml jest obsługiwany i zużywa mniej pamięci, dlatego nadaje się do małego VPS. Problem pojawia się wraz ze wzrostem archiwum: wyszukiwanie pełnotekstowe i zbiorcza edycja tagów wyraźnie zwalniają przy tysiącach dokumentów. Późniejsza migracja wymaga eksportu i importu. Jeśli archiwum ma nadal rosnąć, należy od razu wybrać PostgreSQL.
Ile miejsca na dysku rzeczywiście wymaga archiwum skanów?
Zazwyczaj około dwukrotności rozmiaru plików źródłowych. Paperless zachowuje niezmieniony oryginał i przechowuje drugi plik PDF po OCR z przeszukiwalną warstwą tekstową oraz niewielkie miniatury. Skan tekstowy o rozmiarze 200 KB pozostaje niewielki. Kolorowy skan długiej umowy o rozmiarze 30 MB zajmuje około 60 MB. Jeśli katalog eksportu jest przechowywany na tym samym dysku, należy uwzględnić dodatkowe miejsce. W takim przypadku to samo archiwum zajmuje na dysku trzykrotnie więcej miejsca.
Czy potrzebne są kontenery Tika i Gotenberg?
Tylko jeśli pliki Word, Excel lub OpenDocument mają być indeksowane razem z plikami PDF. Kontenery konwertują te formaty do PDF, aby paperless mógł wykonywać OCR i wyszukiwać ich zawartość. Dodają także dwa kolejne uruchomione kontenery i kilkaset megabajtów pamięci. Na małym serwerze można je pominąć, jeśli wszystkie przechowywane pliki są już plikami PDF lub obrazami.