Paperless-ngx на VPS: встановлення через Docker Compose
Запустіть Paperless-ngx на VPS через Docker Compose: офіційний стек Postgres, PAPERLESS_URL, каталог consume, мови OCR, HTTPS і резервні копії.
Що ви створюєте
Paperless-ngx на VPS перетворює папку зі сканами паперових документів на архів із повнотекстовим пошуком. Ви додаєте PDF у каталог, за яким ведеться спостереження, сервер запускає OCR (оптичне розпізнавання символів), витягує текст, визначає ймовірні дату та кореспондента і класифікує документ. Установлення складається з одного файла Docker Compose із чотирма сервісами. Після цього потрібно виконати налаштування. Саме цьому присвячено більшу частину цього посібника, оскільки саме на цьому етапі найчастіше виникають проблеми. Paperless-ngx не є фототекою: OCR і визначення кореспондента не допоможуть із папкою святкових JPEG-файлів, тому зберігайте їх у фотосервері, призначеному для цього, а Paperless використовуйте для документів.
Paperless-ngx — це підтримуваний спільнотою форк оригінального Paperless. Це безкоштовне self-hosted рішення, яке зберігає документи як звичайні файли на диску, тому ви завжди маєте доступ до власного архіву. Якщо запускати його на VPS, а не на домашньому комп’ютері, скани будуть доступні з будь-якого місця без відкриття порту на домашньому маршрутизаторі. Це добре поєднується з приватним екземпляром Nextcloud для файлів, які не є паперовими. Такий самий підхід працює і для комп’ютера, до якого підключено сканер: власний relay RustDesk на цьому VPS дає змогу керувати цим комп’ютером віддалено, також без відкриття порту на маршрутизаторі.
Що фактично запускає стек
Офіційний compose-файл запускає чотири контейнери. Розуміння призначення кожного з них допомагає читати журнали.
webserver: безпосередньо образ paperless-ngx. Він запускає вебінтерфейс, API, consumer, який стежить за вхідною папкою, і робочі процеси Celery, що виконують OCR.db: PostgreSQL. Він зберігає метадані, теги, кореспондентів і таблиці індексу повнотекстового пошуку. PDF-файли в ньому не зберігаються.broker: Valkey, сховище ключ-значення, сумісне з Redis. Воно працює як черга завдань між вебпроцесом і робочими процесами.gotenbergіtika: необов’язкові компоненти, які використовуються лише у варіантах compose-файла-tika. Вони перетворюють документи Office (.docx,.xlsx,.odt) на PDF, щоб paperless міг їх індексувати.
Станом на July 2026 compose-файл для postgres фіксує версії docker.io/library/postgres:18 і docker.io/valkey/valkey:9-alpine та завантажує застосунок із ghcr.io/paperless-ngx/paperless-ngx:latest.
Передумови
- Ubuntu 24.04 KVM VPS із доступом через sudo та вже встановленим Docker із Compose plugin. Якщо ця частина для вас нова, почніть із основ Docker Compose для VPS, а потім поверніться сюди.
- Доменне ім’я з A-записом, який вказує на VPS. Paperless відмовляється обслуговувати hostname, про який йому не повідомили, тому це потрібно налаштувати раніше, ніж може здаватися.
- Реальним обмеженням є обсяг пам’яті. PostgreSQL, Valkey, gunicorn і worker Tesseract OCR можуть одночасно працювати в межах 2 GB за невеликого навантаження. Виділіть 4 GB, якщо плануєте імпортувати backlog із сотень сканів, оскільки OCR великого багатосторінкового PDF створює пік споживання пам’яті, через який kernel OOM killer завершує worker.
- Диск: ваш архів зберігається двічі — як оригінальний файл і як PDF-архів із виконаним OCR, тому закладайте приблизно подвійний обсяг від розміру сканів.
Отримайте офіційні compose-файли
Є інтерактивний інсталятор:
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"Він ставить запитання та записує файли замість вас. Ручне виконання потребує чотирьох команд і дає змогу розуміти, де саме розташовані всі файли. Це важливо для сервера, який ви будете обслуговувати.
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Варіанти розташовані в одному каталозі: docker-compose.sqlite.yml, docker-compose.mariadb.yml і версія -tika для кожного з них. Для нового встановлення виберіть postgres. SQLite підходить для кількох сотень документів, але індекс повнотекстового пошуку стає повільним значно раніше, ніж PostgreSQL.
Файл .env містить один рядок: COMPOSE_PROJECT_NAME=paperless. Це ім’я стає префіксом для кожного контейнера й тому, тому не видаляйте його, а потім не дивуйтеся, чому docker compose down -v не може знайти ваші дані.
Налаштуйте docker-compose.env перед першим запуском
Два параметри є обов’язковими. Згенеруйте секретний ключ командою, яку документує проєкт:
python3 -c "import secrets; print(secrets.token_urlsafe(64))"Потім відредагуйте 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 постачається з буквальним значенням change-me. Цей ключ підписує cookies сеансів, тому стандартне значення дає змогу будь-кому, хто його знає, підробити сеанс. Задайте його до першого запуску, оскільки подальша зміна призведе до виходу всіх користувачів із системи.
PAPERLESS_URL — це параметр, який заощаджує годину роботи. Paperless — це застосунок Django, а Django перевіряє заголовок Host кожного запиту. Задайте PAPERLESS_URL, і він автоматично заповнить для вас ALLOWED_HOSTS, CORS_ALLOWED_HOSTS та CSRF_TRUSTED_ORIGINS. Якщо залишити його порожнім, вказати домен на цей сервер, кожна сторінка повертатиме Bad Request (400), а в журналі контейнера з’являтиметься DisallowedHost. Записуйте значення без кінцевої косої риски та без шляху.
USERMAP_UID і USERMAP_GID визначають користувача, від імені якого працює контейнер. Установіть їх відповідно до свого облікового запису. Перевірити його можна за допомогою id -u і id -g. Якщо значення не збігаються, файли, скопійовані до папки споживання, будуть недоступні для читання процесу імпорту, а в журналі замість імпорту з’явиться помилка доступу.
Запустіть стек і створіть першого користувача
docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webservercreatesuperuser запитує ім’я користувача, адресу електронної пошти та пароль. Облікових даних за замовчуванням немає, тому пропуск цього кроку залишить вас на сторінці входу, яка ніколи не прийме жодні дані. Перш ніж відкривати сторінку в браузері, дочекайтеся повідомлення в журналі про те, що сервер прослуховує порт 8000. Під час першого запуску також виконуються міграції бази даних. Це займає одну-дві хвилини.
Перевірте стек локально, перш ніж підключати домен:
curl -I http://127.0.0.1:8000Перенаправлення з 302 на /accounts/login/ означає, що стек працює коректно.
Завершіть TLS перед застосунком
Стандартний файл compose публікує 8000:8000, прив’язуючи його до всіх інтерфейсів. На публічному VPS це відкриває весь архів документів для доступу через звичайний HTTP усім, хто знайде цю адресу. Змініть рядок із портом, щоб прив’язати його лише до loopback:
ports:
- "127.0.0.1:8000:8000"Завершуйте TLS (transport layer security) у reverse proxy та передавайте запити на 127.0.0.1:8000. Якщо це єдиний застосунок на сервері, підійде будь-який проксі з ACME (automatic certificate management environment) client. Якщо ви запускаєте кілька контейнерів за однією конфігурацією сертифікатів, скористайтеся схемою reverse proxy Traefik для кількох застосунків Docker Compose і під’єднайте сервіс webserver до мережі проксі без опублікованого порту.
Незалежно від вибраного проксі, він має передавати X-Forwarded-Proto: https. Без цього Django вважає, що запит надійшов через HTTP, перевірка origin у формі входу не проходить, і ви отримуєте CSRF verification failed. Request aborted. на сторінці, яка виглядає правильно. Інша частина виправлення — встановити PAPERLESS_URL на точну адресу https://, яку ви вводите в браузері.
Також збільште обмеження проксі на розмір завантаження. Скан розміром 40 MB через проксі, який обмежує тіло запиту до 1 MB, буде відхилено ще до того, як його побачить paperless, а браузер повідомить про загальну помилку завантаження.
Як працює каталог consume
Файл compose монтує ./consume з каталогу compose у контейнер. Усе, що ви туди поміщаєте, імпортується, а потім видаляється з каталогу, оскільки файл уже зберігається в media volume під керуванням paperless.
cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserverВи маєте побачити, як consumer знаходить файл за його назвою, запускає OCR і завершує обробку рядком про додавання документа. Для односторінкового сканування весь цикл займає кілька секунд, а для довгого документа може тривати хвилину або довше.
Два параметри змінюють спосіб пошуку файлів. PAPERLESS_CONSUMER_RECURSIVE=true змушує paperless шукати файли у вкладених каталогах, а PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true перетворює назву кожного вкладеного каталогу на тег. Тому файл, переміщений у consume/invoices/2026/, отримує теги invoices і 2026. Це найпростіша система впорядкування файлів, яку ви коли-небудь створите.
Виявлення файлів — інша частина процесу. За замовчуванням PAPERLESS_CONSUMER_POLLING_INTERVAL має значення 0, тобто paperless використовує сповіщення файлової системи ядра, які надходять негайно. Такі сповіщення не передаються через мережеву файлову систему. Якщо каталог consume є спільним ресурсом NFS або SMB, у який мережевий сканер записує файли, нічого не буде виявлено. Щоб це виправити, задайте для інтервалу додатне число секунд, щоб paperless натомість періодично сканував каталог.
Мови OCR і їхня вартість
PAPERLESS_OCR_LANGUAGE приймає трилітерний код Tesseract; за замовчуванням використовується eng. Поєднуйте мови знаком плюс, наприклад deu+eng. Після цього Tesseract перевіряє кожну з них і зберігає найкращий результат, тому кожна додаткова мова збільшує час використання CPU для кожної сторінки. На VPS зі спільним vCPU це може означати, що сканування триватиме не десять секунд, а хвилину. Указуйте лише ті мови, якими фактично написані ваші документи.
Образ містить англійську, німецьку, італійську, іспанську та французьку мови. Для інших мов додайте потрібну мову до PAPERLESS_OCR_LANGUAGES як список, розділений пробілами, наприклад PAPERLESS_OCR_LANGUAGES=tur ces, і перезапустіть контейнер. Контейнер завантажує пакети даних Tesseract під час запуску, тому перший запуск після цієї зміни триватиме довше.
Резервне копіювання бази даних і медіафайлів
Копіювання Docker volumes під час роботи PostgreSQL створює резервну копію, яку може бути неможливо відновити. Paperless має власний експортер. Він записує документи та JSON-маніфест усіх метаданих у bind mount ./export:
docker compose exec webserver document_exporter ../export --delete --no-progress-bar--delete видаляє експортовані файли, які більше не відповідають поточному документу. Завдяки цьому каталог залишається дзеркальною копією, а не зростає без обмежень. --no-progress-bar не виводить зайві повідомлення, коли команда запускається з cron.
Відновлення виконується document_importer з тієї самої папки в новому стеку. Отже, безпечно зберігати потрібно лише каталог експорту. Регулярно надсилайте його до іншого розташування за допомогою зашифрованих дедуплікованих резервних копій restic з VPS і спочатку запускайте експорт, щоб restic не захопив архів, записаний лише частково.
Перевіряйте резервну копію, переконавшись, що export/manifest.json існує, а кількість файлів відповідає кількості документів в інтерфейсі. Резервна копія, вміст якої ви жодного разу не перевіряли, не є резервною копією. Ще гірше, якщо nightly export непомітно починає завершуватися з помилкою. Тому налаштуйте cron job на надсилання свого коду завершення на власний ntfy server. Так ви дізнаєтеся про проблему того тижня, коли вона виникла, а не в день, коли знадобиться відновлення.
FAQ
Чому після спрямування домену на сервер кожна сторінка повертає «Bad Request (400)»?
Django відхилив заголовок Host, оскільки ваш домен не вказано в ALLOWED_HOSTS. Задайте PAPERLESS_URL=https://paperless.example.com у docker-compose.env без завершального слеша, а потім виконайте docker compose up -d, щоб повторно створити контейнер. Саме редагування env-файлу нічого не змінює, оскільки запущений контейнер використовує середовище, з яким його було запущено.
Я поклав PDF у папку consume, але нічого не сталося. У чому проблема?
Спочатку перевірте docker compose logs webserver. Помилка доступу означає, що USERMAP_UID і USERMAP_GID не відповідають обліковому запису, якому належить файл. Виправте їх і повторно створіть контейнер. Повна відсутність запису в журналі означає, що подія створення файлу не надійшла. Таке трапляється на мережевих шарах, оскільки сповіщення ядра не передаються через них. Задайте PAPERLESS_CONSUMER_POLLING_INTERVAL, наприклад, як 30, і paperless натомість скануватиме папку кожні 30 секунд.
Чи можна запускати paperless-ngx із SQLite замість PostgreSQL?
Так, docker-compose.sqlite.yml підтримується та споживає менше пам’яті, тому підходить для невеликого VPS. Компроміс стає помітним зі зростанням архіву: повнотекстовий пошук і масове редагування тегів суттєво сповільнюються, коли кількість документів вимірюється тисячами. Подальша міграція потребує експорту та імпорту, тому одразу виберіть PostgreSQL, якщо очікуєте подальшого зростання архіву.
Скільки дискового простору насправді потрібен для архіву сканів?
Орієнтовно вдвічі більше, ніж займають вихідні файли. Paperless зберігає оригінал без змін і створює другий PDF після OCR із текстовим шаром для пошуку, а також невеликі мініатюри. Чорно-білий скан лише текстового документа розміром 200 KB залишається невеликим. Кольоровий скан довгого контракту розміром 30 MB займатиме близько 60 MB. Якщо каталог експорту зберігається на тому самому диску, додайте і його. Тоді той самий архів займатиме на диску приблизно втричі більше місця.
Чи потрібні мені контейнери Tika і Gotenberg?
Лише якщо потрібно індексувати файли Word, Excel або OpenDocument разом із PDF. Вони конвертують ці формати у PDF, щоб paperless міг виконати OCR і додати документи до пошуку. Водночас вони додають ще два запущені контейнери та потребують кількох сотень мегабайт пам’яті. Тому на невеликому сервері їх можна не запускати, якщо всі файли вже мають формат PDF або є зображеннями.