Triển khai Paperless-ngx trên Docker: Lưu trữ tài liệu thông minh và OCR tiếng Việt cho HomeLab

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

Bài toán thực tế: Thảm họa giấy tờ lúc 2 giờ sáng

Vào lúc 2:15 sáng Chủ nhật, bộ nguồn máy chủ trong tủ rack của tôi bị nổ tụ lọc kèm theo một tiếng tách lớn. Chiếc máy chết ngắc ngay lập tức. Tôi cần hóa đơn và phiếu bảo hành ghi số serial gấp để mở yêu cầu RMA trước thời hạn chót 8:00 sáng của nhà phân phối. Thế nhưng, tôi lại mất bốn mươi phút lục lọi trong những thùng nhựa bám bụi và hộp đựng giày, bới tung đống hóa đơn tiền điện cùng hợp đồng thuê nhà cũ. Tôi đã chẳng bao giờ tìm thấy tờ phiếu đó.

Giấy tờ vật lý là một rủi ro lớn trong vận hành. Giấy in nhiệt sẽ biến thành những mẩu giấy trắng phai mờ chỉ sau sáu tháng. Phiếu bảo hành quan trọng dễ dàng thất lạc trong ngăn bàn. Ngay cả khi bạn quét hóa đơn đưa lên ổ mạng chia sẻ (network share), các tệp PDF thô vẫn chỉ là những khối nhị phân mờ mịt. Nếu không có nhận dạng ký tự quang học (OCR), bạn không thể tìm kiếm nội dung, phân loại theo nhà cung cấp hay truy vấn theo ngày tháng.

Tài liệu tiếng Việt càng khiến vấn đề thêm phần nan giải. Phần lớn công cụ OCR mã nguồn mở đều mặc định sử dụng từ điển tiếng Anh (eng.traineddata). Khi xử lý văn bản có dấu như Hóa đơn giá trị gia tăng hay Hợp đồng lao động, chúng làm biến dạng các dấu thanh thành những chuỗi ký tự ASCII lỗi. Nếu chỉ mục tìm kiếm ghi nhận “tiền điện” thành “ti?n di?n”, mọi truy vấn của bạn sẽ hoàn toàn vô nghĩa.

Khái niệm cốt lõi: Khám phá kiến trúc của Paperless-ngx

Paperless-ngx tránh sử dụng kiến trúc monolithic (nguyên khối). Hệ thống phân tách các tác vụ nhập liệu (ingestion), phân tích cú pháp (parsing), lập chỉ mục (indexing) và phân phối dịch vụ (serving) qua các container nhẹ biệt lập:

  • Webserver & API (Django): Phục vụ giao diện dashboard, xử lý các lệnh gọi REST API, kiểm soát quyền người dùng và quản lý tag tài liệu, trường tùy chỉnh (custom field) cùng đối tác/người gửi (correspondent).
  • Hàng đợi tác vụ & Worker nhập liệu (Redis): Giám sát thư mục đầu vào để đón nhận file mới. Khi bạn thả một bản scan vào, worker sẽ lấy tác vụ từ Redis, trích xuất hình ảnh nhúng và lên lịch chạy OCR bất đồng bộ mà không làm treo giao diện web.
  • Công cụ OCR (Tesseract 5): Tạo một lớp văn bản có thể tìm kiếm trực tiếp từ các pixel trang được rasterize. Việc kết hợp vie.traineddata với eng.traineddata giúp Tesseract nhận diện chính xác các dấu thanh phức tạp và nguyên âm ghép trong tiếng Việt (như ư, ơ, ê, đ).
  • Cơ sở dữ liệu & Chỉ mục tìm kiếm (PostgreSQL & Whoosh): PostgreSQL lưu trữ metadata của tài liệu, các mối quan hệ và audit log. Whoosh quản lý inverted text index giúp thực thi các truy vấn từ khóa chỉ trong chưa đầy một giây trên hàng nghìn trang tài liệu.
  • Chuyển đổi tài liệu (Gotenberg & Apache Tika): Các dịch vụ bổ trợ tùy chọn giúp chuyển đổi file Office (.docx, .xlsx) thành định dạng lưu trữ dài hạn chuẩn PDF/A trước khi bàn giao cho Tesseract.

Thực hành: Triển khai Paperless-ngx hỗ trợ OCR tiếng Việt

Việc vận hành stack này bằng Docker Compose giúp cô lập các binary ngôn ngữ và giữ cho hệ thống file của máy chủ luôn gọn gàng. Chúng ta sẽ cấu hình OCR đa ngôn ngữ để engine có thể lập chỉ mục cho cả tài liệu tiếng Việt lẫn tiếng Anh chỉ trong một lượt quét duy nhất.

Bước 1: Cấu trúc thư mục và phân quyền

Paperless-ngx cần bộ nhớ lưu trữ cục bộ riêng cho các file nạp vào, thumbnail được tạo và cơ sở dữ liệu PostgreSQL. Bạn nên dự trù khoảng 1,5 MB đến 3 MB dung lượng lưu trữ cho mỗi trang tài liệu được lưu trữ ở độ phân giải 300 DPI.

mkdir -p ~/homelab/paperless/{consume,data,media,export,pgdata,redisdata}
cd ~/homelab/paperless

# Kiểm tra UID và GID của người dùng hiện tại (thường là 1000)
id

# Cấp quyền đọc và ghi cho người dùng thực thi container
sudo chown -R 1000:1000 ~/homelab/paperless
sudo chmod -R 755 ~/homelab/paperless

Bước 2: Soạn thảo file docker-compose.yml

Lưu cấu hình sau thành file docker-compose.yml bên trong thư mục ~/homelab/paperless. Hãy nhớ thay thế mật khẩu cơ sở dữ liệu và secret key mẫu trước khi khởi động.

services:
  broker:
    image: docker.io/library/redis:7-alpine
    container_name: paperless-redis
    restart: unless-stopped
    volumes:
      - ./redisdata:/data

  db:
    image: docker.io/library/postgres:16-alpine
    container_name: paperless-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless_admin
      POSTGRES_PASSWORD: ReplaceWithAStrongSecretDbPassword
    volumes:
      - ./pgdata:/var/lib/postgresql/data

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    container_name: paperless-web
    restart: unless-stopped
    depends_on:
      - db
      - broker
    ports:
      - "8000:8000"
    environment:
      PAPERLESS_REDIS: redis://broker:6379
      PAPERLESS_DBENGINE: postgresql
      PAPERLESS_DBHOST: db
      PAPERLESS_DBNAME: paperless
      PAPERLESS_DBUSER: paperless_admin
      PAPERLESS_DBPASS: ReplaceWithAStrongSecretDbPassword
      USERMAP_UID: 1000
      USERMAP_GID: 1000
      PAPERLESS_URL: http://192.168.1.100:8000
      PAPERLESS_SECRET_KEY: ChangeThisToRandomHexCharsForSecurity32BitMin
      PAPERLESS_TIME_ZONE: Asia/Ho_Chi_Minh
      # Cấu hình OCR tiếng Việt kèm dự phòng tiếng Anh
      PAPERLESS_OCR_LANGUAGE: vie
      PAPERLESS_OCR_LANGUAGES: "vie eng"
      PAPERLESS_OCR_MODE: skip_archive
      PAPERLESS_TASK_WORKERS: 2
    volumes:
      - ./data:/usr/src/paperless/data
      - ./media:/usr/src/paperless/media
      - ./export:/usr/src/paperless/export
      - ./consume:/usr/src/paperless/consume

Bước 3: Khởi động và thiết lập tài khoản quản trị

Khởi chạy stack ở chế độ chạy ngầm (detached mode). Trong lần khởi động đầu tiên, container sẽ tải gói ngôn ngữ vie.traineddata (~15 MB) trực tiếp từ repository gốc.

docker compose up -d

# Theo dõi log khởi động và migration cơ sở dữ liệu
docker compose logs -f webserver

Khi bạn thấy dòng Listening at: http://0.0.0.0:8000 xuất hiện trong log, hãy tạo tài khoản quản trị viên chính:

docker compose exec -it webserver python3 manage.py createsuperuser

Nhập tên người dùng, email quản trị và mật khẩu an toàn theo các lời nhắc trên màn hình.

Bước 4: Kiểm thử quá trình nạp và OCR tài liệu tiếng Việt

Truy cập vào http://<your-server-ip>:8000 trên trình duyệt và đăng nhập. Kiểm thử quá trình nạp dữ liệu bằng cách thả một hóa đơn tiếng Việt mẫu vào volume consume:

# Sao chép tệp PDF mẫu vào thư mục consume
cp ~/test_receipt_vietnamese.pdf ~/homelab/paperless/consume/

Theo dõi tiến trình worker khi nó tiếp nhận và phân tích tài liệu:

docker compose logs -f webserver | grep -E "(Consuming|Success|OCR)"

Một tài liệu hai trang thông thường sẽ mất từ 4 đến 10 giây để xử lý trên CPU x86 4 nhân đời mới. Hãy mở giao diện web, tìm tài liệu đã xử lý và thử tìm kiếm các cụm từ cụ thể như “Hóa đơn giá trị gia tăng” hoặc “Mã số thuế”. Dashboard sẽ đánh dấu nổi bật các từ khóa khớp trực tiếp ngay trên khung xem PDF.

Kết luận: Duy trì kho lưu trữ tài liệu bền bỉ và ổn định

Triển khai các container tương đối đơn giản; tuy nhiên, để duy trì kho lưu trữ hoạt động bền bỉ qua nhiều năm, bạn cần thực hiện 3 quy trình định kỳ sau:

  • Tự động hóa sao lưu hàng ngày: Chạy lệnh docker compose exec -T webserver document_exporter ../export định kỳ qua cron. Đồng bộ thư mục export vừa tạo cùng pgdata tới một máy chủ từ xa có mã hóa, chẳng hạn như bucket S3 ngoài vị trí (offsite) hoặc thiết bị NAS phụ.
  • Thiết lập quy trình khớp dữ liệu tự động (Matching Workflows): Cấu hình các quy tắc tự động gắn tag dựa trên mã số thuế (MST) của Việt Nam. Paperless-ngx có thể tự động gán đối tác và phân loại tài liệu bất cứ khi nào phát hiện mã số thuế 10 chữ số đã biết.
  • Kiểm soát phân bổ phần cứng: Tesseract rất dễ làm nghẽn toàn bộ nhân CPU sẵn có trong quá trình nạp tài liệu hàng loạt. Trên các phần cứng tiêu thụ ít điện năng như Intel N100 hoặc Raspberry Pi 5, hãy đặt PAPERLESS_TASK_WORKERS: 1 để tránh hiện tượng quá nhiệt giảm xung (thermal throttling) và sự cố tràn bộ nhớ (OOM).

Một hệ thống Paperless-ngx được tinh chỉnh chuẩn xác sẽ biến đống giấy tờ lộn xộn thành một kho tri thức có cấu trúc và dễ dàng tra cứu. Lần tới, khi có linh kiện hỏng hóc hay mùa quyết toán thuế ập đến, việc tìm kiếm sẽ chỉ tính bằng giây thay vì hàng giờ đồng hồ.

Share: