SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-28

Cara Pasang Paperless-ngx di VPS Menggunakan Docker

Ketahui cara memasang Paperless-ngx pada VPS dengan Docker Compose. Panduan ini merangkumi konfigurasi Postgres, PAPERLESS_URL, OCR, HTTPS, serta strategi sandaran data.

Apa yang anda sedang bina

Paperless-ngx pada VPS menukarkan folder dokumen yang diimbas menjadi arkib yang boleh dicari. Anda memasukkan fail PDF ke dalam direktori yang dipantau, pelayan akan menjalankan OCR (optical character recognition) ke atasnya, mengekstrak teks, meneka tarikh serta pihak yang terlibat, dan memfailkannya. Pemasangan ini terdiri daripada satu fail Docker Compose dengan empat servis. Segala proses selepas itu adalah konfigurasi, dan panduan ini menumpukan sebahagian besar kandungannya di situ kerana di situlah pemasangan biasanya gagal. Ia bukanlah pustaka foto: OCR dan fungsi meneka pihak terlibat tidak berguna untuk folder JPEG percutian, jadi simpan fail tersebut di pelayan foto yang dibina untuk tujuan itu dan gunakan Paperless khusus untuk dokumen kertas.

Paperless-ngx ialah fork komuniti yang diselenggarakan untuk projek Paperless asal. Perisian ini percuma, boleh dihoskan sendiri dan menyimpan dokumen anda sebagai fail biasa pada cakera. Oleh itu, anda tidak akan kehilangan akses kepada arkib sendiri. Menjalankannya pada VPS dan bukannya komputer di rumah bermakna imbasan anda boleh dicapai dari mana-mana sahaja tanpa membuka port pada router rumah. Ia juga sesuai digabungkan dengan instance Nextcloud peribadi untuk fail yang bukan dokumen kertas. Logik yang sama terpakai pada komputer desktop yang disambungkan kepada pengimbas anda, kerana relay RustDesk milik anda sendiri pada VPS tersebut membolehkan anda mengawal komputer itu dari lokasi lain tanpa perlu membuka laluan pada router.

Perkara yang sebenarnya dijalankan oleh stack

Fail compose rasmi memulakan empat kontena, dan mengetahui fungsi setiap satu menjadikan log lebih mudah dibaca.

  • webserver: imej paperless-ngx itu sendiri. Ia menjalankan antara muka web, API, pengguna yang memantau folder input anda, dan pekerja tugasan Celery yang melakukan OCR.
  • db: PostgreSQL. Ia menyimpan metadata, tag, koresponden, dan jadual indeks carian teks penuh. Ia tidak menyimpan fail PDF anda.
  • broker: Valkey, stor nilai-kunci yang serasi dengan Redis. Ia merupakan baris gilir tugasan antara proses web dan pekerja.
  • gotenberg dan tika: pilihan, hanya dalam varian compose -tika. Ia menukarkan dokumen Office (.docx, .xlsx, .odt) kepada PDF supaya paperless boleh mengindeksnya.

Sehingga Julai 2026, fail compose postgres menetapkan docker.io/library/postgres:18 dan docker.io/valkey/valkey:9-alpine, serta menarik aplikasi daripada ghcr.io/paperless-ngx/paperless-ngx:latest.

Prasyarat

  • Sebuah VPS KVM Ubuntu 24.04 dengan akses sudo, serta Docker dengan plugin Compose yang telah dipasang. Jika bahagian ini baharu bagi anda, mulakan dengan asas Docker Compose untuk VPS dan kembali semula ke sini.
  • Nama domain dengan rekod A yang menghala ke VPS tersebut. Paperless tidak akan beroperasi pada hostname yang tidak ditetapkan, jadi perkara ini lebih penting daripada yang anda sangkakan.
  • Memori merupakan kekangan sebenar. PostgreSQL, Valkey, gunicorn dan worker OCR Tesseract yang berjalan serentak memerlukan 2 GB untuk penggunaan ringan. Sediakan 4 GB jika anda merancang untuk mengimport timbunan ratusan imbasan, kerana proses OCR pada fail PDF berbilang halaman yang besar akan menyebabkan lonjakan memori yang mengakibatkan worker ditamatkan oleh kernel (out-of-memory killer).
  • Cakera: arkib anda disimpan sebanyak dua kali, iaitu fail asal dan fail PDF arkib yang telah melalui OCR, jadi sediakan ruang kira-kira dua kali ganda daripada saiz imbasan anda.

Dapatkan fail compose rasmi

Terdapat pemasang interaktif:

bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"

Ia akan menanyakan beberapa soalan dan menulis fail tersebut untuk anda. Melakukannya secara manual hanya memerlukan empat arahan dan memberikan anda kefahaman tentang lokasi setiap fail, yang merupakan perkara penting bagi pelayan yang akan anda selenggara.

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

Varian tersebut berada dalam direktori yang sama: docker-compose.sqlite.yml, docker-compose.mariadb.yml, dan versi -tika bagi setiap satunya. Pilih postgres untuk pemasangan baharu. SQLite memadai untuk beberapa ratus dokumen, namun indeks carian teks penuh akan menjadi perlahan jauh lebih awal berbanding PostgreSQL.

Fail .env mengandungi satu baris, iaitu COMPOSE_PROJECT_NAME=paperless. Nama tersebut menjadi awalan bagi setiap kontena dan volum, jadi jangan padamkannya dan kemudian tertanya-tanya mengapa docker compose down -v tidak dapat menemui data anda.

Konfigurasikan docker-compose.env sebelum permulaan pertama

Dua tetapan adalah wajib. Jana kunci rahsia menggunakan arahan yang didokumentasikan oleh projek:

python3 -c "import secrets; print(secrets.token_urlsafe(64))"

Kemudian, sunting 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 dibekalkan dengan nilai literal change-me. Nilai ini menandatangani kuki sesi, jadi membiarkannya bermakna sesiapa sahaja yang mengetahui nilai lalai tersebut boleh memalsukan sesi. Tetapkan nilai ini sebelum permulaan pertama, kerana menukarnya kemudian akan menyebabkan semua pengguna dilog keluar.

PAPERLESS_URL ialah tetapan yang menjimatkan masa anda. Paperless merupakan aplikasi Django, dan Django mengesahkan pengepala Host bagi setiap permintaan. Tetapkan PAPERLESS_URL dan ia akan mengisi ALLOWED_HOSTS, CORS_ALLOWED_HOSTS serta CSRF_TRUSTED_ORIGINS untuk anda. Jika dibiarkan kosong, dan anda menghalakan domain ke pelayan tersebut, setiap halaman akan memaparkan Bad Request (400) dengan DisallowedHost dalam log kontena. Tulis nilai tersebut tanpa garis miring (trailing slash) dan tanpa path.

USERMAP_UID dan USERMAP_GID menetapkan pengguna yang menjalankan kontena tersebut. Padankan nilai ini dengan akaun anda sendiri, yang boleh disemak dengan id -u dan id -g. Jika nilai tersebut tidak sepadan, fail yang anda salin ke dalam folder consume tidak akan dapat dibaca oleh pengguna (consumer), dan log akan memaparkan ralat kebenaran (permission error) dan bukannya import.

Mulakan stack dan cipta pengguna pertama

docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webserver

createsuperuser akan meminta nama pengguna, e-mel dan kata laluan. Tiada log masuk lalai, jadi jika langkah ini dilangkau, anda akan berada pada halaman log masuk yang tidak akan menerima sebarang input. Tunggu sehingga baris log melaporkan bahawa pelayan sedang mendengar pada port 8000 sebelum anda mencuba melalui pelayar. Permulaan kali pertama juga akan menjalankan migrasi pangkalan data, yang mengambil masa satu atau dua minit.

Semak secara tempatan sebelum melibatkan domain:

curl -I http://127.0.0.1:8000

302 yang dihalakan ke /accounts/login/ bermakna stack tersebut dalam keadaan sihat.

Letakkan HTTPS di hadapannya

Fail compose standard menerbitkan 8000:8000, yang mengikat pada setiap antara muka. Pada VPS awam, ini menghidangkan seluruh arkib dokumen anda melalui HTTP biasa kepada sesiapa sahaja yang menemui alamat tersebut. Tukar baris port untuk mengikat pada loopback sahaja:

    ports:
      - "127.0.0.1:8000:8000"

Kemudian, tamatkan TLS (transport layer security) dalam reverse proxy dan majukan ke 127.0.0.1:8000. Jika ini merupakan satu-satunya aplikasi pada pelayan tersebut, mana-mana proksi dengan klien ACME (automatic certificate management environment) sudah memadai. Jika anda menjalankan beberapa kontena di belakang satu persediaan sijil, ikuti corak reverse proxy Traefik untuk berbilang aplikasi Docker Compose dan lampirkan perkhidmatan webserver ke rangkaian proksi tanpa menerbitkan sebarang port.

Walau apa pun proksi yang anda gunakan, ia mesti menghantar X-Forwarded-Proto: https. Tanpanya, Django menganggap permintaan tiba melalui HTTP, semakan asal (origin check) pada borang log masuk gagal, dan anda mendapat CSRF verification failed. Request aborted. pada halaman yang kelihatan betul. Separuh lagi daripada penyelesaian tersebut ialah PAPERLESS_URL ditetapkan kepada alamat https:// yang tepat seperti yang anda taip dalam pelayar.

Tingkatkan juga had saiz muat naik proksi. Imbasan 40 MB melalui proksi yang mengehadkan badan permintaan kepada 1 MB akan ditolak sebelum paperless sempat memprosesnya, dan pelayar akan melaporkan kegagalan muat naik umum.

Cara direktori consume berfungsi

Fail compose melakukan bind-mount pada ./consume dari direktori compose ke dalam kontena. Apa-apa sahaja yang anda letakkan di sana akan diimport dan kemudian dipadamkan daripada folder tersebut, kerana fail itu kini berada dalam volum media di bawah pengurusan paperless.

cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserver

Anda sepatutnya melihat consumer mengesan nama fail, menjalankan OCR, dan selesai dengan baris yang melaporkan dokumen telah ditambah. Keseluruhan kitaran ini mengambil masa beberapa saat untuk imbasan satu halaman dan boleh mengambil masa seminit atau lebih untuk dokumen yang panjang.

Dua tetapan mengubah cara fail ditemui. PAPERLESS_CONSUMER_RECURSIVE=true menyebabkan paperless mencari di dalam subfolder, dan PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true menukarkan setiap nama subfolder menjadi tag, jadi meletakkan fail ke dalam consume/invoices/2026/ akan melabelkannya sebagai invoices dan 2026. Itu adalah sistem pemfailan paling murah yang pernah anda bina.

Pengesanan adalah bahagian yang satu lagi. Secara lalai PAPERLESS_CONSUMER_POLLING_INTERVAL ditetapkan kepada 0, bermakna paperless menggunakan pemberitahuan sistem fail kernel, yang dicetuskan serta-merta. Pemberitahuan tersebut tidak merentasi sistem fail rangkaian. Jika folder consume anda merupakan perkongsian NFS atau SMB supaya pengimbas rangkaian boleh menulis kepadanya, tiada apa-apa yang akan dikesan, dan penyelesaiannya adalah dengan menetapkan selang masa kepada nombor saat yang positif supaya paperless mengimbas folder tersebut sebagai ganti.

Bahasa OCR dan kosnya

PAPERLESS_OCR_LANGUAGE menerima kod Tesseract tiga huruf, dengan eng sebagai lalai. Gabungkan bahasa dengan tanda tambah, seperti dalam deu+eng. Tesseract kemudian akan mencuba setiap bahasa dan mengekalkan hasil terbaik, jadi setiap bahasa tambahan akan mendarabkan masa CPU yang digunakan bagi setiap halaman. Pada VPS vCPU kongsi, ini adalah perbezaan antara imbasan yang selesai dalam sepuluh saat dengan imbasan yang mengambil masa seminit. Senaraikan hanya bahasa yang digunakan dalam dokumen anda.

Imej ini didatangkan dengan bahasa Inggeris, Jerman, Itali, Sepanyol dan Perancis. Untuk bahasa lain, tambahkan bahasa tersebut ke dalam PAPERLESS_OCR_LANGUAGES sebagai senarai yang dipisahkan oleh ruang, contohnya PAPERLESS_OCR_LANGUAGES=tur ces, kemudian mulakan semula. Kontena akan memuat turun pek data Tesseract semasa permulaan, jadi but pertama selepas perubahan tersebut akan menjadi lebih perlahan.

Sandarkan pangkalan data dan media

Menyalin Docker volume semasa PostgreSQL sedang berjalan akan menghasilkan sandaran yang mungkin tidak dapat dipulihkan. Paperless membekalkan pengeksportnya sendiri, yang menulis dokumen berserta manifes JSON bagi semua metadata ke dalam bind mount ./export:

docker compose exec webserver document_exporter ../export --delete --no-progress-bar

--delete membuang fail yang dieksport yang tidak lagi sepadan dengan dokumen semasa, supaya folder tersebut kekal sebagai cermin dan bukannya terus membesar. --no-progress-bar memastikan output kekal bersih apabila ia dijalankan melalui cron.

Pemulihan dilakukan dengan document_importer terhadap folder yang sama pada stack yang baharu, yang bermaksud direktori eksport adalah satu-satunya perkara yang perlu anda simpan dengan selamat. Hantarkannya ke lokasi luar (offsite) mengikut jadual dengan sandaran restic yang disulitkan dan dinyahduplikasi daripada VPS anda, dan jalankan eksport terlebih dahulu supaya restic tidak menangkap arkib yang belum selesai ditulis.

Sahkan sandaran dengan memeriksa bahawa export/manifest.json wujud dan bilangan fail sepadan dengan bilangan dokumen dalam antara muka. Sandaran yang tidak pernah anda senaraikan bukanlah sandaran. Eksport setiap malam yang gagal secara senyap lebih buruk lagi. Oleh itu, konfigurasikan tugas cron supaya menghantar status keluarnya ke pelayan ntfy anda sendiri. Dengan cara ini, anda akan mengetahui sandaran itu gagal pada minggu berlakunya kegagalan, bukannya pada hari anda perlu memulihkannya.

FAQ

Mengapa setiap halaman memaparkan "Bad Request (400)" selepas saya menghalakan domain saya kepadanya?

Django menolak header Host kerana domain anda tiada dalam ALLOWED_HOSTS. Tetapkan PAPERLESS_URL=https://paperless.example.com dalam docker-compose.env, tanpa garis miring (trailing slash) di hujung, kemudian jalankan docker compose up -d untuk membina semula kontena. Menyunting fail env sahaja tidak akan memberi kesan, kerana kontena yang sedang berjalan mengekalkan persekitaran semasa ia dimulakan.

Saya meletakkan fail PDF ke dalam folder consume tetapi tiada apa-apa berlaku. Apa yang tidak kena?

Periksa docker compose logs webserver terlebih dahulu. Ralat kebenaran (permission error) bermakna USERMAP_UID dan USERMAP_GID tidak sepadan dengan akaun yang memiliki fail tersebut, jadi betulkan tetapan itu dan bina semula kontena. Jika tiada baris log langsung, ini bermakna peristiwa fail tidak pernah sampai, yang sering berlaku pada perkongsian rangkaian (network shares) kerana pemberitahuan kernel tidak merentasinya. Tetapkan PAPERLESS_CONSUMER_POLLING_INTERVAL kepada nilai seperti 30 supaya paperless akan mengimbas folder tersebut setiap 30 saat.

Bolehkah saya menjalankan paperless-ngx menggunakan SQLite dan bukannya PostgreSQL?

Ya, docker-compose.sqlite.yml disokong dan menggunakan memori yang lebih sedikit, yang sesuai untuk VPS bersaiz kecil. Kekurangannya akan ketara apabila arkib anda membesar: carian teks penuh dan penyuntingan tag secara pukal akan menjadi perlahan apabila mencapai ribuan dokumen. Migrasi pada masa hadapan memerlukan proses eksport dan import, jadi pilihlah PostgreSQL sekarang jika anda menjangkakan arkib anda akan terus berkembang.

Berapakah ruang cakera yang sebenarnya diperlukan oleh arkib imbasan?

Kira-kira dua kali ganda saiz fail asal anda. Paperless menyimpan fail asal tanpa diubah dan menyimpan PDF kedua yang telah melalui OCR dengan lapisan teks yang boleh dicari, berserta lakaran kecil (thumbnails). Imbasan teks sahaja bersaiz 200 KB kekal kecil. Imbasan berwarna bagi kontrak panjang bersaiz 30 MB akan memakan ruang kira-kira 60 MB. Tambahkan direktori eksport jika anda menyimpannya pada cakera yang sama, maka arkib yang sama akan berada di atas cakera sebanyak tiga kali.

Adakah saya memerlukan kontena Tika dan Gotenberg?

Hanya jika anda mahu fail Word, Excel atau OpenDocument diindeks bersama-sama fail PDF anda. Kontena tersebut menukarkan format berkenaan kepada PDF supaya paperless boleh melakukan OCR dan mencarinya. Ia juga menambah dua lagi kontena yang berjalan serta menggunakan beberapa ratus megabait memori, jadi abaikan kontena tersebut pada pelayan kecil jika semua fail yang anda simpan sudah pun dalam format PDF atau imej.

#paperless-ngx#documents#self-hosting#docker#ocr