آموزش نصب Paperless-ngx روی VPS با Docker Compose
راهنمای کامل نصب Paperless-ngx روی VPS با استفاده از Docker Compose. پیکربندی صحیح Postgres، تنظیم PAPERLESS_URL، مدیریت پوشه consume، OCR و روشهای پشتیبانگیری از اسناد.
آنچه در حال ساخت آن هستید
نصب Paperless-ngx روی یک VPS، پوشهای از اسناد اسکنشده را به یک آرشیو قابل جستجو تبدیل میکند. شما یک فایل PDF را در دایرکتوری تحت نظارت (watched directory) قرار میدهید، سرور روی آن OCR (تشخیص نوری کاراکترها) اجرا میکند، متن را استخراج کرده، تاریخ و فرستنده را حدس میزند و آن را بایگانی میکند. این نصب شامل یک فایل Docker Compose با چهار سرویس است. هر آنچه پس از آن انجام میشود، پیکربندی است و این راهنما بخش عمدهای از حجم خود را به آن اختصاص داده، زیرا نصبها معمولاً در این مرحله دچار مشکل میشوند. این ابزار یک کتابخانه عکس نیست: OCR و حدس فرستنده برای پوشهای از تصاویر تعطیلات (JPEG) کاربردی ندارند، بنابراین آنها را در یک سرور عکس مخصوص این کار قرار دهید و Paperless را فقط برای اسناد کاغذی نگه دارید.
Paperless-ngx فورک جامعهمحور و تحت نگهداری پروژه اصلی Paperless است. این نرمافزار رایگان و self-hosted است و اسناد شما را بهصورت فایلهای معمولی روی دیسک ذخیره میکند؛ بنابراین هرگز از آرشیو خودتان خارج نمیمانید. اجرای آن روی یک VPS بهجای یک سیستم خانگی باعث میشود اسکنهای شما از هرجا قابل دسترسی باشند، بدون آنکه لازم باشد پورتی را روی روتر خانه باز کنید. این روش با یک نمونه خصوصی Nextcloud برای فایلهایی که کاغذی نیستند نیز سازگاری خوبی دارد. همین منطق درباره دسکتاپی که scanner شما به آن متصل است نیز صدق میکند، زیرا یک RustDesk relay متعلق به خودتان روی همان VPS امکان کنترل آن سیستم از مکان دیگری را فراهم میکند، بدون آنکه لازم باشد در روتر نیز مسیری باز کنید.
پشته نرمافزاری دقیقاً چه چیزی را اجرا میکند
فایل compose رسمی، چهار کانتینر را راهاندازی میکند. دانستن وظیفه هر یک از آنها باعث میشود لاگها قابلفهمتر شوند.
webserver: خودِ ایمیج paperless-ngx. این کانتینر رابط وب، API، پردازشگرِ نظارتکننده بر پوشه ورودی و workerهای وظیفه Celery که عملیات OCR را انجام میدهند، اجرا میکند.db: دیتابیس PostgreSQL. این بخش متادیتا، برچسبها، مخاطبین و جداول ایندکس جستجوی متن کامل را نگهداری میکند. فایلهای PDF شما در اینجا ذخیره نمیشوند.broker: دیتابیس Valkey، یک ذخیرهساز کلید-مقدار سازگار با Redis. این بخش صف وظایف بین پردازش وب و workerها را مدیریت میکند.gotenbergوtika: اختیاری هستند و فقط در نسخههای-tikaاز فایل compose وجود دارند. آنها اسناد آفیس (.docx،.xlsx،.odt) را به PDF تبدیل میکنند تا paperless بتواند آنها را ایندکس کند.
از ژوئیه 2026، فایل compose مربوط به postgres از نسخههای ثابت docker.io/library/postgres:18 و docker.io/valkey/valkey:9-alpine استفاده میکند و اپلیکیشن را از ghcr.io/paperless-ngx/paperless-ngx:latest دریافت مینماید.
پیشنیازها
- یک سرور مجازی KVM با سیستمعامل Ubuntu 24.04، دسترسی sudo، و Docker به همراه افزونه Compose نصبشده. اگر این بخش برای شما جدید است، با اصول Docker Compose برای سرور مجازی شروع کنید و سپس به اینجا بازگردید.
- یک نام دامنه که رکورد A آن به سرور مجازی اشاره میکند. Paperless از ارائه سرویس روی نام دامنهای که برای آن تعریف نشده باشد خودداری میکند، بنابراین این مورد اهمیت بیشتری نسبت به آنچه تصور میکنید دارد.
- حافظه رم، محدودیت اصلی است. PostgreSQL، Valkey، gunicorn و یک worker برای OCR (Tesseract) در مجموع برای استفاده سبک در 2 GB رم جای میگیرند. اگر قصد دارید حجم زیادی از اسناد قدیمی را وارد کنید، 4 GB رم اختصاص دهید؛ زیرا عملیات OCR روی فایلهای PDF بزرگ و چندصفحهای باعث جهش ناگهانی مصرف حافظه میشود که ممکن است منجر به کشته شدن worker توسط قابلیت out-of-memory killer در هسته سیستمعامل شود.
- دیسک: آرشیو شما دو بار ذخیره میشود؛ یک بار فایل اصلی و یک بار فایل PDF آرشیو شده که OCR روی آن انجام شده است. بنابراین فضایی تقریباً معادل دو برابر حجم اسناد خود در نظر بگیرید.
دریافت فایلهای رسمی compose
یک نصبکننده تعاملی وجود دارد:
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"این ابزار سوالاتی میپرسد و فایلها را برای شما ایجاد میکند. انجام دستی این کار تنها شامل 4 دستور است و باعث میشود دقیقاً بدانید هر چیزی در کجا قرار دارد؛ این همان چیزی است که برای سروری که قصد نگهداری آن را دارید، به آن نیاز دارید.
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 برای چند صد سند مناسب است، اما ایندکس جستجوی تماممتن (full-text search) خیلی زودتر از PostgreSQL کند میشود.
فایل .env شامل یک خط است: COMPOSE_PROJECT_NAME=paperless. این نام به پیشوند تمام کانتینرها و volumeها تبدیل میشود، بنابراین آن را حذف نکنید و بعد تعجب نکنید که چرا 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 است. این مقدار برای امضای کوکیهای نشست (session cookies) استفاده میشود، بنابراین باقی گذاشتن آن به این معناست که هر کسی که مقدار پیشفرض را بداند میتواند یک نشست جعلی ایجاد کند. این مقدار را پیش از اولین اجرا تنظیم کنید، زیرا تغییر آن در آینده باعث خروج (logout) تمامی کاربران میشود.
تنظیم PAPERLESS_URL همان گزینهای است که یک ساعت از وقت شما را صرفهجویی میکند. Paperless یک برنامه Django است و Django هدر Host هر درخواست را اعتبارسنجی میکند. مقدار PAPERLESS_URL را تنظیم کنید تا بهطور خودکار مقادیر ALLOWED_HOSTS، CORS_ALLOWED_HOSTS و CSRF_TRUSTED_ORIGINS برای شما تکمیل شوند. اگر آن را خالی بگذارید و یک دامنه را به سرور متصل کنید، هر صفحه با خطای Bad Request (400) مواجه شده و در لاگ کانتینر خطای DisallowedHost ثبت میشود. این مقدار را بدون اسلش در انتها و بدون مسیر (path) بنویسید.
مقادیر USERMAP_UID و USERMAP_GID کاربری را که کانتینر با آن اجرا میشود تعیین میکنند. آنها را با حساب کاربری خود که با دستورات id -u و id -g قابل مشاهده است، مطابقت دهید. اگر این مقادیر مطابقت نداشته باشند، فایلهایی که در پوشه consume کپی میکنید برای مصرفکننده (consumer) غیرقابل خواندن خواهند بود و در لاگ بهجای عملیات وارد کردن (import)، خطای دسترسی (permission error) نمایش داده میشود.
راهاندازی پشته و ایجاد اولین کاربر
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/ تغییر مسیر (redirect) میدهد، به این معنی است که پشته سالم است.
قرار دادن HTTPS در مقابل آن
فایل compose پیشفرض، 8000:8000 را منتشر میکند که به تمام رابطهای شبکه متصل میشود. در یک VPS عمومی، این کار باعث میشود کل آرشیو اسناد شما از طریق پروتکل HTTP ساده برای هر کسی که آدرس را پیدا کند، در دسترس قرار گیرد. خط مربوط به پورت را تغییر دهید تا فقط به loopback متصل شود:
ports:
- "127.0.0.1:8000:8000"سپس TLS (امنیت لایه انتقال) را در یک reverse proxy خاتمه دهید و درخواستها را به 127.0.0.1:8000 هدایت کنید. اگر این تنها برنامه روی سرور است، هر پروکسی که دارای کلاینت ACME (محیط مدیریت خودکار گواهی) باشد، کارساز خواهد بود. اگر چندین کانتینر را پشت یک تنظیمات گواهی اجرا میکنید، از الگوی reverse proxy ترافیک برای چندین برنامه Docker Compose پیروی کنید و سرویس webserver را بدون انتشار هیچ پورتی، به شبکه پروکسی متصل کنید.
از هر پروکسی که استفاده میکنید، باید X-Forwarded-Proto: https را ارسال کند. بدون این هدر، Django تصور میکند که درخواست از طریق HTTP رسیده است، بررسی مبدأ (origin check) در فرم ورود با شکست مواجه میشود و شما با CSRF verification failed. Request aborted. در صفحهای که ظاهر درستی دارد، مواجه میشوید. بخش دیگر این اصلاح، تنظیم PAPERLESS_URL بر روی دقیقاً همان آدرس https:// است که در مرورگر تایپ میکنید.
همچنین محدودیت حجم آپلود پروکسی را افزایش دهید. یک اسکن 40 مگابایتی که از پروکسی با محدودیت بدنه 1 مگابایت عبور میکند، پیش از آنکه به paperless برسد رد میشود و مرورگر یک خطای عمومی آپلود گزارش میدهد.
نحوه عملکرد دایرکتوری consume
فایل compose، مسیر ./consume را از دایرکتوری compose به داخل container متصل (bind-mount) میکند. هر فایلی که در آنجا قرار دهید، وارد سیستم شده و سپس از آن پوشه حذف میشود، زیرا فایل اکنون در volume مربوط به media تحت مدیریت 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 نام هر زیرپوشه را به یک برچسب (tag) تبدیل میکند؛ بنابراین اگر فایلی را در consume/invoices/2026/ قرار دهید، آن فایل با برچسبهای invoices و 2026 نشانگذاری میشود. این ارزانترین سیستم بایگانی است که تا به حال ساختهاید.
بخش دیگر، تشخیص فایلها است. بهصورت پیشفرض PAPERLESS_CONSUMER_POLLING_INTERVAL روی 0 تنظیم شده است، به این معنی که paperless از اعلانهای سیستمفایل هسته (kernel filesystem notifications) استفاده میکند که بلافاصله فعال میشوند. این اعلانها از سیستمفایلهای شبکه عبور نمیکنند. اگر پوشه consume شما یک share از نوع NFS یا SMB است تا یک اسکنر تحت شبکه بتواند در آن بنویسد، هیچ فایلی تشخیص داده نخواهد شد. راهحل این است که بازه زمانی (interval) را روی یک عدد مثبت (بر حسب ثانیه) تنظیم کنید تا paperless بهجای آن، پوشه را بهصورت دورهای اسکن کند.
زبانهای OCR و هزینههای آنها
PAPERLESS_OCR_LANGUAGE یک کد سه حرفی Tesseract دریافت میکند که مقدار پیشفرض آن eng است. برای ترکیب زبانها از علامت مثبت استفاده کنید، مانند deu+eng. در این حالت، Tesseract هر زبان را امتحان کرده و بهترین نتیجه را حفظ میکند؛ بنابراین هر زبان اضافه، زمان پردازنده (CPU) صرفشده برای هر صفحه را چند برابر میکند. در یک VPS با vCPU اشتراکی، این موضوع تفاوت بین اتمام اسکن در 10 ثانیه و یک دقیقه است. فقط زبانهایی را فهرست کنید که اسناد شما واقعاً به آن زبانها نوشته شدهاند.
این ایمیج بهصورت پیشفرض شامل زبانهای انگلیسی، آلمانی، ایتالیایی، اسپانیایی و فرانسوی است. برای هر زبان دیگر، آن را به PAPERLESS_OCR_LANGUAGES به صورت فهرستی با جداکننده فاصله اضافه کنید (مثلاً PAPERLESS_OCR_LANGUAGES=tur ces) و سپس سرویس را مجدداً راهاندازی کنید. کانتینر بستههای داده Tesseract را در زمان شروع به کار دانلود میکند، بنابراین اولین بوت پس از این تغییر، کندتر خواهد بود.
پشتیبانگیری از پایگاه داده و رسانهها
کپی کردن Docker volumeها در حالی که PostgreSQL در حال اجرا است، ممکن است منجر به پشتیبان غیرقابل بازیابی شود. Paperless ابزار صادرکننده (exporter) اختصاصی خود را دارد که اسناد را به همراه یک فایل manifest با فرمت JSON از تمام متادیتها در bind mount مربوط به ./export مینویسد:
docker compose exec webserver document_exporter ../export --delete --no-progress-barدستور --delete فایلهای صادرشدهای را که دیگر با سند فعلی مطابقت ندارند حذف میکند تا این پوشه به جای رشد بیرویه، یک نسخه آینهای باقی بماند. --no-progress-bar نیز خروجی را هنگام اجرا از طریق cron تمیز نگه میدارد.
بازیابی با استفاده از document_importer در برابر همان پوشه روی یک stack جدید انجام میشود؛ این یعنی دایرکتوری export تنها چیزی است که باید از آن محافظت کنید. آن را طبق زمانبندی با پشتیبانهای رمزنگاریشده و deduplicate شده restic از VPS خود به خارج از سایت منتقل کنید و پیش از آن، دستور export را اجرا کنید تا restic هرگز یک آرشیو ناقص را کپی نکند.
با بررسی وجود export/manifest.json و مطابقت تعداد فایلها با تعداد سندهای نمایشدادهشده در رابط کاربری، یک backup را راستیآزمایی کنید. backupی که هرگز فهرست فایلهای آن را بررسی نکردهاید، backup محسوب نمیشود. بدتر از آن، export شبانهای است که بیسروصدا دچار خطا میشود؛ بنابراین کاری کنید cron job وضعیت خروجی خود را به سرور ntfy خودتان ارسال کند تا در همان هفتهای که فرایند از کار میافتد مطلع شوید، نه روزی که به restore نیاز پیدا میکنید.
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 با کاربری که مالک فایل است مطابقت ندارند؛ پس آنها را اصلاح کرده و کانتینر را بازسازی کنید. اگر هیچ لاگی ثبت نمیشود، به این معناست که رویداد فایل هرگز دریافت نشده است؛ این اتفاق در اشتراکهای شبکه (network shares) رخ میدهد، زیرا اعلانهای هسته (kernel notifications) از طریق شبکه منتقل نمیشوند. مقدار PAPERLESS_CONSUMER_POLLING_INTERVAL را روی عددی مانند 30 تنظیم کنید تا paperless هر 30 ثانیه پوشه را اسکن کند.
آیا میتوانم paperless-ngx را به جای PostgreSQL با SQLite اجرا کنم؟
بله، docker-compose.sqlite.yml پشتیبانی میشود و حافظهٔ کمتری مصرف میکند که برای یک VPS کوچک مناسب است. نقطه ضعف این انتخاب با رشد آرشیو مشخص میشود: جستجوی تماممتن و ویرایش دستهجمعی برچسبها با رسیدن به هزاران سند، بهطور محسوسی کند میشود. مهاجرت در آینده مستلزم خروجی گرفتن و وارد کردن مجدد دادههاست، بنابراین اگر انتظار دارید آرشیو شما رشد کند، از همان ابتدا PostgreSQL را انتخاب کنید.
آرشیو اسکنها واقعاً به چه مقدار فضای دیسک نیاز دارد؟
تقریباً دو برابر حجم فایلهای اصلی. Paperless فایل اصلی را دستنخورده نگه میدارد و یک فایل PDF دوم حاوی لایه متنی قابل جستجو (OCR شده) به همراه تصاویر بندانگشتی کوچک ذخیره میکند. یک اسکن متنی 200 کیلوبایتی حجم کمی اشغال میکند. یک اسکن رنگی 30 مگابایتی از یک قرارداد طولانی، حدود 60 مگابایت فضا میگیرد. اگر دایرکتوری export را هم روی همان دیسک نگه دارید، عملاً همان آرشیو سه بار روی دیسک ذخیره میشود.
آیا به کانتینرهای Tika و Gotenberg نیاز دارم؟
فقط اگر میخواهید فایلهای Word، Excel یا OpenDocument در کنار PDFها ایندکس شوند. این سرویسها فایلهای مذکور را به PDF تبدیل میکنند تا paperless بتواند آنها را OCR کرده و جستجو کند. این کار دو کانتینر در حال اجرا و چند صد مگابایت مصرف حافظه اضافه میکند؛ بنابراین اگر تمام فایلهای شما از قبل PDF یا تصویر هستند، در یک سرور کوچک از نصب آنها صرفنظر کنید.