SSD Nodes Learn 8GB 記憶體 — 每年 $66
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-01

如何在 VPS 上部署 Paperless-ngx:完整教學

透過 Docker Compose 在 VPS 自架 Paperless-ngx。本指南涵蓋 PostgreSQL 設定、PAPERLESS_URL 變數、OCR 語言配置、HTTPS 安全性及自動化備份策略,確保您的文件系統穩定運作,並解決常見的 OOM 記憶體不足問題。

您將建置的內容

在 VPS 上部署 Paperless-ngx 可將掃描文件資料夾轉換為可搜尋的封存庫。您只需將 PDF 放入監控目錄,伺服器便會執行 OCR(光學字元辨識)、擷取文字、推斷日期與對應者,並進行歸檔。此安裝包含一個包含四項服務的 Docker Compose 檔案。後續步驟皆為設定,本指南將大部分篇幅用於此處,因為安裝失敗通常源於設定問題。

Paperless-ngx 是原版 Paperless 專案經社群維護的分支版本。它是免費且可自架的,並將文件以純檔案形式儲存於磁碟,確保您不會被鎖定在自己的封存庫之外。在 VPS 而非家用主機上執行此服務,意味著您無需在家用路由器上開啟連接埠,即可隨時隨地存取掃描檔,且它能與 非紙本檔案的私人 Nextcloud 執行個體 完美搭配。

此堆疊實際執行的內容

官方的 compose 檔案會啟動四個容器,了解每個容器的功能有助於閱讀日誌。

  • webserver:paperless-ngx 映像檔本身。它執行網頁介面、API、監控輸入資料夾的消費者,以及執行 OCR 的 Celery 任務工作程序。
  • db:PostgreSQL。它儲存元資料、標籤、通訊對象以及全文檢索索引表。它不會儲存您的 PDF 檔案。
  • broker:Valkey,一個與 Redis 相容的鍵值儲存庫。它是網頁程序與工作程序之間的任務佇列。
  • gotenbergtika:選用元件,僅存在於 -tika 的 compose 變體中。它們將 Office 文件(.docx.xlsx.odt)轉換為 PDF,以便 paperless 進行索引。

截至 2026 年 7 月,postgres compose 檔案鎖定 docker.io/library/postgres:18docker.io/valkey/valkey:9-alpine 版本,並從 ghcr.io/paperless-ngx/paperless-ngx:latest 拉取應用程式。

先決條件

  • 一台已安裝 Ubuntu 24.04 的 KVM VPS,需具備 sudo 權限,並已安裝 Docker 與 Compose 外掛程式。若對此部分不熟悉,請先參考 VPS 的 Docker Compose 基礎教學,完成後再回來。
  • 一個已設定 A 紀錄並指向該 VPS 的網域名稱。Paperless 若未設定對應的主機名稱將無法提供服務,因此此步驟比預期更為重要。
  • 記憶體是主要的限制因素。PostgreSQL、Valkey、gunicorn 與 Tesseract OCR worker 同時執行時,輕量使用下需 2 GB 記憶體。若計畫匯入大量掃描檔案,建議配置 4 GB,因為處理大型多頁 PDF 的 OCR 作業會產生記憶體尖峰,可能導致 worker 被核心的 OOM (Out-of-Memory) 機制終止。
  • 磁碟空間:系統會儲存兩份檔案,分別為原始檔案與經過 OCR 處理的歸檔 PDF,因此請預留約為掃描檔案總量兩倍的磁碟空間。

取得官方 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.ymldocker-compose.mariadb.yml 以及各版本的 -tika。新安裝建議選擇 postgres。SQLite 適用於數百份文件,但全文檢索索引的效能會比 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。此參數用於簽署工作階段 Cookie,若保留預設值,任何知悉此預設值的人皆可偽造工作階段。請務必在首次啟動前進行設定,因為後續變更將導致所有使用者被強制登出。

PAPERLESS_URL 是最關鍵的設定。Paperless 屬於 Django 應用程式,Django 會驗證每個請求的 Host 標頭。設定 PAPERLESS_URL 後,系統會自動填入 ALLOWED_HOSTSCORS_ALLOWED_HOSTSCSRF_TRUSTED_ORIGINS。若留空並將網域指向該伺服器,所有頁面將回傳 Bad Request (400) 錯誤,且容器日誌中會出現 DisallowedHost。請填寫網域,結尾勿包含斜線或路徑。

USERMAP_UIDUSERMAP_GID 用於設定容器執行的使用者身分。請將其設定為您自己的帳號,可透過 id -uid -g 指令確認。若兩者不符,您複製到 consume 資料夾的檔案將無法被消費者讀取,且日誌會顯示權限錯誤而非匯入成功。

啟動堆疊並建立第一個使用者

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

createsuperuser 會提示輸入使用者名稱、電子郵件與密碼。系統沒有預設登入帳號,若跳過此步驟,登入頁面將無法接受任何憑證。請等待日誌顯示伺服器正在監聽 port 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 (transport layer security),並將流量轉發至 127.0.0.1:8000。若此伺服器僅執行此單一應用程式,任何具備 ACME (automatic certificate management environment) 用戶端的代理伺服器皆可勝任。若您在單一憑證設定下執行多個容器,請遵循 適用於多個 Docker Compose 應用程式的 Traefik 反向代理模式,並將 webserver 服務連接至代理網路,且不發布任何連接埠。

無論使用何種代理伺服器,都必須傳送 X-Forwarded-Proto: https。若未傳送,Django 會認為請求是透過 HTTP 傳入,導致登入表單的來源檢查失敗,並在看似正常的頁面上出現 CSRF verification failed. Request aborted.。此問題的另一半修正方式是將 PAPERLESS_URL 設定為您在瀏覽器中輸入的確切 https:// 位址。

此外,請提高代理伺服器的上傳大小限制。若透過限制主體大小為 1 MB 的代理伺服器傳輸 40 MB 的掃描檔,該檔案會在到達 paperless 之前被拒絕,導致瀏覽器回報一般的上傳失敗。

consume 目錄的運作方式

此 compose 檔案將 ./consume 從 compose 目錄掛載(bind-mount)至容器內。放入該處的任何檔案都會被匯入,隨後從該資料夾中刪除,因為該檔案現已移至由 paperless 管理的 media 儲存區中。

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/ 會自動為其加上 invoices2026 標籤。這是您能建立成本最低的歸檔系統。

偵測機制是另一半的關鍵。預設情況下 PAPERLESS_CONSUMER_POLLING_INTERVAL0,代表 paperless 使用核心檔案系統通知(kernel filesystem notifications),此機制會立即觸發。然而,這些通知無法跨越網路檔案系統。若您的 consume 資料夾是 NFS 或 SMB 共享目錄(以便網路掃描器寫入),系統將無法偵測到任何檔案。解決方法是將間隔時間設定為大於 0 的秒數,讓 paperless 改以輪詢方式掃描該資料夾。

OCR 語言及其成本

PAPERLESS_OCR_LANGUAGE 採用三字母的 Tesseract 代碼,預設為 eng。若要組合多種語言,請使用加號連接,例如 deu+eng。Tesseract 會嘗試每一種語言並保留最佳結果,因此每增加一種語言,都會成倍增加處理每頁文件所需的 CPU 時間。在共用 vCPU 的 VPS 上,這會導致掃描時間從 10 秒增加到 1 分鐘。請僅列出文件實際使用的語言。

此映像檔內建英文、德文、義大利文、西班牙文與法文。若需其他語言,請將其加入 PAPERLESS_OCR_LANGUAGES 並以空格分隔,例如 PAPERLESS_OCR_LANGUAGES=tur ces,隨後重新啟動。容器會在啟動時下載 Tesseract 資料包,因此變更設定後的首次開機速度會較慢。

備份資料庫與媒體

在 PostgreSQL 執行期間複製 Docker 儲存卷(volumes)可能導致備份無法還原。Paperless 內建匯出工具,會將文件及所有元數據的 JSON 清單寫入 ./export 掛載點:

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

--delete 會移除不再符合現有文件的匯出檔案,確保資料夾保持鏡像狀態,而非無限擴張。--no-progress-bar 可在透過 cron 排程執行時,保持輸出內容簡潔。

還原時,僅需針對全新堆疊中的相同資料夾執行 document_importer,這意味著匯出目錄是您唯一需要妥善保存的項目。請透過 來自 VPS 的加密且去重複的 restic 備份 定期將其傳輸至異地,並務必先執行匯出,以確保 restic 不會擷取到寫入一半的封存檔。

請檢查 export/manifest.json 是否存在,並確認檔案數量與介面中的文件總數相符,藉此驗證備份。未經檢核的備份等同於無備份。

FAQ

為什麼將網域指向該服務後,每個頁面都顯示「Bad Request (400)」?

Django 拒絕了 Host 標頭,因為您的網域不在 ALLOWED_HOSTS 中。請在 docker-compose.env 中設定 PAPERLESS_URL=https://paperless.example.com(結尾請勿包含斜線),然後執行 docker compose up -d 以重建容器。僅編輯 env 檔案無效,因為執行中的容器會保留其啟動時的環境變數。

我將 PDF 放入 consume 資料夾後沒有任何反應,出了什麼問題?

請先檢查 docker compose logs webserver。權限錯誤表示 USERMAP_UIDUSERMAP_GID 不符合擁有該檔案的帳戶,請修正這些設定並重建容器。若完全沒有日誌紀錄,表示檔案事件未送達,這通常發生在網路共享磁碟上,因為核心通知無法跨越網路傳遞。請將 PAPERLESS_CONSUMER_POLLING_INTERVAL 設定為類似 30 的值,paperless 就會改為每 30 秒掃描一次該資料夾。

我可以使用 SQLite 而非 PostgreSQL 來執行 paperless-ngx 嗎?

可以,docker-compose.sqlite.yml 受到支援且佔用較少記憶體,適合小型 VPS。代價會在封存檔變大時顯現:當文件數量達到數千份時,全文檢索與批次標籤編輯的速度會明顯變慢。日後遷移需要執行匯出與匯入,因此如果您預期封存檔會持續增長,請現在就選擇 PostgreSQL。

掃描檔案的封存檔實際需要多少磁碟空間?

大約是原始檔案大小的兩倍。Paperless 會保留原始檔案,並儲存一份包含可搜尋文字層的 OCR 後 PDF,以及小型縮圖。純文字掃描檔約 200 KB,體積很小。一份 30 MB 的彩色長合約掃描檔會佔用約 60 MB。如果您將匯出目錄也放在同一個磁碟上,則該封存檔在磁碟上會佔用三倍的空間。

我需要 Tika 與 Gotenberg 容器嗎?

只有在您希望將 Word、Excel 或 OpenDocument 檔案與 PDF 一併索引時才需要。它們會將這些格式轉換為 PDF,以便 paperless 進行 OCR 與搜尋。這會增加兩個執行中的容器並消耗數百 MB 記憶體,因此如果您的歸檔檔案皆已是 PDF 或圖片,在小型主機上可以省略這些容器。

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