Paperless-ngx на VPS: установка через Docker Compose
Пошаговая установка Paperless-ngx на VPS: официальный стек Postgres, PAPERLESS_URL, каталог consume, языки OCR, HTTPS и резервное копирование.
Что вы создаёте
Paperless-ngx на VPS превращает папку со сканами бумажных документов в архив с поиском. Вы помещаете PDF в отслеживаемый каталог, сервер запускает OCR (оптическое распознавание символов), извлекает текст, определяет дату и корреспондента, а затем сохраняет документ. Установка выполняется с помощью одного файла Docker Compose с четырьмя сервисами. После этого требуется только настройка. В руководстве ей посвящена большая часть текста, поскольку именно на этом этапе установки чаще всего возникают проблемы.
Paperless-ngx — поддерживаемый сообществом форк исходного проекта Paperless. Это бесплатное ПО для самостоятельного размещения. Документы хранятся на диске в виде обычных файлов, поэтому вы всегда сохраняете доступ к собственному архиву. Размещение на VPS вместо домашнего компьютера позволяет получать доступ к сканам из любого места без открытия порта на домашнем маршрутизаторе. Кроме того, Paperless-ngx хорошо сочетается с частным экземпляром Nextcloud для файлов, которые не являются бумажными документами.
Что фактически запускает стек
Официальный файл 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.
Предварительные требования
- VPS на Ubuntu 24.04 с KVM и доступом через sudo. Docker и плагин Compose должны быть уже установлены. Если этот этап для вас новый, начните с основ работы с Docker Compose на VPS, а затем вернитесь.
- Доменное имя с A-записью, указывающей на VPS. Paperless отказывается обслуживать имя хоста, которое не указано в его настройках. Поэтому это требуется выполнить раньше, чем может показаться.
- Основное ограничение — память. PostgreSQL, Valkey, gunicorn и один рабочий процесс Tesseract OCR одновременно занимают до 2 GB при небольшой нагрузке. Выделите 4 GB, если планируете импортировать сотни отсканированных документов. При обработке большого многостраничного PDF именно OCR может вызвать резкий рост потребления памяти и привести к завершению рабочего процесса ядром из-за нехватки памяти.
- Диск: архив хранится в двух экземплярах — в исходном файле и в архивном 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=1000При поставке PAPERLESS_SECRET_KEY содержит буквальное значение change-me. Этот ключ подписывает файлы cookie сеансов. Если оставить значение по умолчанию, любой, кто его знает, сможет подделать сеанс. Задайте значение до первого запуска. Если изменить его позже, все пользователи выйдут из системы.
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. Если значения не совпадают, потребитель не сможет прочитать файлы, скопированные в папку consume. Вместо импорта в журнале появится ошибка доступа.
Запустите стек и создайте первого пользователя
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/ означает, что стек работает нормально.
Установите HTTPS перед приложением
Стандартный файл compose публикует 8000:8000, который привязывается ко всем интерфейсам. На общедоступном VPS это открывает весь архив документов по обычному HTTP для любого пользователя, обнаружившего адрес. Измените строку с портом, чтобы привязать его только к loopback-интерфейсу:
ports:
- "127.0.0.1:8000:8000"Завершайте TLS (безопасность транспортного уровня) в reverse proxy и перенаправляйте запросы на 127.0.0.1:8000. Если это единственное приложение на сервере, подойдет любой proxy с клиентом ACME (среда автоматического управления сертификатами). Если несколько контейнеров работают за одной конфигурацией сертификатов, используйте схему reverse proxy Traefik для нескольких приложений Docker Compose и подключите сервис webserver к сети proxy, не публикуя порт вообще.
Независимо от выбранного proxy он должен передавать X-Forwarded-Proto: https. Без этого Django считает, что запрос поступил по HTTP, проверка источника в форме входа завершается ошибкой, и на внешне корректной странице появляется CSRF verification failed. Request aborted.. Вторая часть исправления — установить PAPERLESS_URL в точное значение https://, которое вы вводите в браузере.
Также увеличьте ограничение proxy на размер загружаемых файлов. Скан размером 40 MB, отправленный через proxy с ограничением тела запроса 1 MB, будет отклонен до того, как paperless его получит, а браузер сообщит об общей ошибке загрузки.
Как работает каталог consume
Файл compose монтирует каталог ./consume из каталога compose в контейнер. Любой помещённый туда файл импортируется, а затем удаляется из этого каталога, поскольку теперь он хранится в томе с медиафайлами под управлением 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 во время работы 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, а количество файлов совпадает с количеством документов в интерфейсе. Резервная копия, содержимое которой ни разу не проверяли, не является резервной копией.
FAQ
Почему после привязки домена к приложению каждая страница возвращает «Bad Request (400)»?
Django отклонил заголовок Host, потому что ваш домен не указан в ALLOWED_HOSTS. Укажите PAPERLESS_URL=https://paperless.example.com в docker-compose.env без завершающего слеша, затем выполните docker compose up -d, чтобы пересоздать контейнер. Одного редактирования файла с переменными окружения недостаточно: запущенный контейнер сохраняет окружение, с которым он был запущен.
Я поместил 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 или изображений.