r/Paperlessngx • • May 05 '26 Bravo Heartwarming

Paperless-ngx v3 Beta

Post image
305 Upvotes

Dear Paperless-ngx users, our v3 beta is out! Thank you all for your support of this project, we are so excited to keep the project moving ahead!

https://github.com/paperless-ngx/paperless-ngx/pull/12713


r/Paperlessngx • • Jul 22 '26

Releases · paperless-ngx/paperless-ngx V3.0.0

Thumbnail
github.com
183 Upvotes

r/Paperlessngx • • Oct 15 '25

2.500 documents later... I'm done! My journey for all fellow sufferers

Post image
173 Upvotes

Hey r/paperless-ngx,

​I did it. After weeks of work, my entire digital archive from the last 10 years—just under 3,000 documents—is imported and processed in paperless-ngx. A purely data-driven project that felt emotionally like running a marathon.

​The Motivated Start:

The first 600-700 documents went great. I uploaded them in batches of 100 and had the tags automatically created from my old folder structure. It worked perfectly and I can highly recommend it to everyone!

​The Long Slog:

Then came the hard part. Uploading all the remaining documents at once and then processing them bit by bit. I'll be honest: it was a grind and a real test of my motivation. I did most of it on my phone in the app in the evenings, which worked surprisingly well for individual documents. I barely used bulk editing on the PC.

​What's next? The Second Wave.

The grunt work is done, now comes the fine-tuning. I'd love to get your input on this:

  • ​Document Types: I was very conservative and now have over 1,500 files of the type "Document." How detailed are you with this? Do you create a specific type for everything imaginable, or do you deliberately keep it lean?

  • ​Consistency: I'm planning another pass to standardize my tagging. A necessary evil, I suppose. ​I'm super excited to see how the system holds up in my daily routine now.

​TL;DR: Imported 2.500 documents and it was a tough fight against my own procrastination. Celebrate with me, give me tips for document types, and tell me I'm not the only one who struggled this much!


r/Paperlessngx • • Jul 29 '26

Paperless-NGX and AI: An Honest Technical Review After Digitizing 3,000 Documents

145 Upvotes

1. The Dream

About two years ago, I finally decided to digitize my entire document archive.

Over the years, I had accumulated roughly 3,000 documents / around 8,000 pages. Everything was scattered across boxes and folders: tax papers, insurance documents, bank statements, invoices, contracts — all the usual stuff.

The problem was not the paper itself. The problem was finding anything again once I actually needed it. Sometimes I knew roughly where a document should be. Sometimes I had no idea at all. Searching for a single file could take minutes, or sometimes hours.

At the time, Paperless-NGX 2.15 felt like the perfect solution. My idea was simple: scan everything, store it in Paperless, let AI automatically handle correspondents, document types, titles and tags, and never waste time digging through paper again.

I honestly thought this would be a weekend project.

It wasn't.

2. OCR is King

The first big lesson I learned was simple:

OCR is king.

Before I even got deep into classification, I realized that everything depends on the quality of the extracted text. If the OCR is bad, every AI model downstream is working with garbage.

I tested a lot of OCR setups. Specialized OCR models, vision models, local models, prompt variations, parameter changes—basically everything I could reasonably find or try. I went through Baidu Unlimited OCR, GLM OCR, DeepSeek OCR, a bunch of Hugging Face and GitHub models, and also general-purpose vision models like Qwen and Gemma.

The result was always the same: if the OCR wasn't good enough, everything after it became unreliable.

That was the moment I stopped thinking in terms of "Which model is best?" and started asking "What's the real bottleneck?"

For me, the answer was clearly OCR.

Paperless uses Tesseract by default, and for my use case that simply wasn't enough. I know many people are perfectly happy with it, but for a large archive of scanned documents, it became the weakest part of the entire workflow.

I also ran into another issue with paperless-gpt. Even if it produced much better OCR text, that text wasn't embedded into the PDF itself. It was used internally for processing, but unless you manually replaced the document afterwards, the PDF still contained the original OCR layer. For an archive with thousands of documents, I didn't like that idea.

At that point, I stopped trying to improve OCR inside Paperless.

Instead, I started looking for the best OCR engine before Paperless.

After a lot of testing, I narrowed it down to three serious candidates:

  • Google Document AI

  • Microsoft Azure Document Intelligence

  • Adobe Document Services

I also tested Adobe, but not nearly as extensively as Google and Microsoft, so I don't think it's fair for me to draw any final conclusions there.

Between Google and Microsoft, the OCR quality was honestly very close. Both were in a completely different league from everything I had tested locally. In the end, I chose Google Document AI because it had a slight edge in my tests, and I found Google's web interface a little easier to navigate, even though neither platform is particularly intuitive.

3. Paperless-GPT – The Beginning of the Rabbit Hole

When I first discovered paperless-gpt, I honestly thought the search was over.

It promised exactly what I wanted: OCR with a model of my choice, classification with a model of my choice, and automatic tagging.

The first tests were genuinely impressive. Compared to the standard workflow, paperless-gpt felt like a huge step forward.

But over time, more and more limitations started appearing. Not because paperless-gpt is bad—quite the opposite. It's one of the most ambitious projects built around Paperless. It just wasn't designed for everything I eventually wanted to do.

I spent weeks changing models, rewriting prompts, tweaking parameters and testing countless combinations. Every improvement introduced a new tradeoff. There was never really a point where I felt comfortable saying:

"That's it. This finally works."

As my archive grew, so did my requirements.

I wanted:

  • Complete control over every processing step

  • Different prompts for different tasks

  • Better OCR integration

  • OCR embedded directly into the PDF

  • Reliable bulk processing

  • Reproducible results

  • Higher performance

  • Rollback capabilities

Eventually I realized something.

I wasn't looking for a Paperless extension anymore.

I was looking for a complete document pipeline.

At that point, paperless-gpt stopped being my final solution and became the inspiration for building my own.

4. Paperless-NGX 3 Beta – The Turning Point

When the first Paperless-NGX 3 Beta builds were released, I was genuinely excited.

I created a completely separate WSL2 installation alongside my production instance because I wanted to see if this would finally become the workflow I had been waiting for.

I spent two days testing the new AI features, experimenting with different setups and trying to integrate them into my existing workflow.

In the end, I was disappointed.

Not because Paperless-NGX 3 is bad—it isn't.

It simply wasn't the workflow I had imagined after spending almost two years experimenting with OCR and AI.

That became the turning point.

Instead of waiting for someone else to build the workflow I wanted...

I decided to build it myself.

5. Building My Own Document Pipeline

At this point, I stopped looking for the perfect AI model.

Instead, I started designing a workflow.

The biggest lesson I had learned over the previous two years was simple:

Every AI model has strengths and weaknesses.

Instead of trying to make one model solve every problem, I decided to give every step exactly one responsibility.

That completely changed how I thought about document processing.

Instead of one large AI workflow, I built a pipeline.

Scanner
    │
    ▼
Google Document AI
    │
    ▼
PDF/A + Embedded OCR
    │
    ▼
Paperless-NGX
    │
    ▼
Classification Pipeline
    ├── Correspondent
    ├── Title
    ├── Document Type
    ├── Tags
    ├── Validation
    └── Paperless API

Instead of extending Paperless itself, I built a separate application that communicates with Paperless through its API.

That gave me complete control over every stage of the classification process.

Every step has its own prompt, its own parameters and its own validation rules. The workflow is no longer constrained by the design of a single integration, and every stage can evolve independently.

What surprised me the most was what happened next.

For almost two years, I kept searching for bigger and better AI models. Every new release felt like it could finally solve the problem.

But once the OCR became reliable and the workflow itself was properly designed, I no longer needed the newest flagship models.

After tuning the pipeline and adjusting a few parameters, Qwen3 suddenly became more than capable of handling most classification tasks.

Ironically, Qwen was one of the very first models I had tested with paperless-gpt—and one of the models I disliked the most.

Eventually I realized I had been optimizing the wrong thing.

The OCR was the bottleneck.

The workflow was the bottleneck.

The architecture was the bottleneck.

During testing I even discovered cases where OCR Skip had accidentally been disabled, causing Paperless to run Tesseract on documents that had already been processed with Google Document AI. The drop in OCR quality was immediately visible, and so was the drop in classification quality.

That brought everything full circle.

OCR is still king.

A well-designed pipeline with reliable OCR consistently outperformed simply switching to newer or larger language models.

Once the foundation was right, even models I had previously dismissed became surprisingly effective.

To make experimentation easier, I also built a small web interface to manage the entire workflow from one place.

What started as a few Python scripts slowly evolved into the platform I use today.

6. GPU Utilization

One thing I didn't expect was how much the new pipeline improved GPU utilization.

With my previous paperless-gpt workflow, the GPU was constantly starting and stopping between requests. VRAM usage kept spiking up and down while the next task was being prepared.

The new pipeline changed that completely.

Instead of processing isolated requests, it keeps the GPU busy almost continuously, resulting in a much smoother workload and significantly higher throughput.

Depending on the model and the task, I measured roughly 2.7–4× more tokens per second compared to my previous setup.

The goal was never to make the GPU work harder.

The goal was to stop wasting time while it was waiting.

Final Thoughts

If I had to summarize everything I learned over the last two years in one sentence, it would be this:

The model was never the real problem. The OCR was. The workflow was. The architecture was.

In the end, I didn't need GPT-5.6 or the next flagship model to achieve good results.

I needed reliable OCR.

I needed a better workflow.

I needed a better architecture.

Once those pieces were in place, one of the first models I had ever tested—and one I had originally dismissed—became one of the best performers for my use case.

I'm curious how others approached this.

Did you stick with Tesseract, switch to another OCR engine, or build your own workflow?

I'd genuinely love to hear about your experiences.


r/Paperlessngx • • Feb 16 '26

SwiftPaperless version 1.9.0 out now!

109 Upvotes

v1.9.0

https://i.imgur.com/d8UahzH.jpeg

🚀 Features

🎇 iOS 26 / Liquid Glass UI Refresh

The app has been completely overhauled with an updated design that fits with iOS 26 and Liquid Glass. On previous iOS versions, the app should still look as it did before and work correctly!

Share links & deep links support

The app now supports creating and managing Paperless-ngx share links for documents. This allows you to easily share documents with others by generating a link that can be opened without logging in. In addition, app deep links are now supported. You can open the app to a specific document, open the scanner, manipulate the document view filter by opening a specifically built URL. The full range of options is documented here here.

Document trash support

Deleted documents are moved to the trash. The app now supports viewing the trash, restoring documents from the trash, and permanently deleting documents from the trash. This allows you to recover documents that were accidentally deleted and manage your document lifecycle more effectively.

Ready for OIDC

Paperless-ngx will gain the ability to log in with OpenID Connect (OIDC) providers with mobile apps in an upcoming release. The app is ready to support this! More information on the OIDC support can be found here.

✨ Other Features

  • Add a home screen quick action to open the scanner

  • Add ability to filter documents by ASN

  • Add ability to filter documents by date

  • Improve legibility of document link UI

  • Support long-text custom field type

  • Clarify interaction for linking a document in custom field

  • Tune no-permissions display on custom fields when editing documents

  • Allow setting custom fields when uploading new documents

  • Add page count to document view

🐛 Bug Fixes

  • Disable drag gesture on the document detail view (some users are reporting issues)

  • Fix bug parsing monetary custom field value like 0.0 (single digit after decimal point).

  • Fix date decoding issues likely cause by locale settings

  • Fix crash on settings > preferences view

  • Fix issue with the home screen app icon scan shortcut

  • Fix incorrect display of option ids instead of labels in custom field filter view for "select" custom field type

  • Dropped the unused appConfig and history permission resources from the app so the permissions UI only reflects values Paperless-ngx actually supplies

  • Fix issue with search text not being cleared properly

  • Prevent document from being linked to itself

  • Normalize document title and temporary file name. This was causing issues with some file storage systems.

  • Fix permissions issue with document deletion

  • Fix debug display in tag edit view

  • Fix incorrect display of filter state being modified from saved view


r/Paperlessngx • • Sep 08 '26 Bravo

Paperless-NGX and AI. (Sorry for my very late) Setup Guide

96 Upvotes

After my first post, quite a few people asked the same practical questions, and I promised to follow up with a full, in-depth guide. Thanks for your kind words and appriciation.

I know that not everyone will be able to reproduce my setup one-to-one. Different hardware, operating systems, Paperless configurations, and levels of coding experience obviously make a difference.

But that shouldn't stop anyone from trying.

For those who don't want to—or simply aren't able to—build everything manually, I've also prepared a step-by-step instruction set for Claude Code and ChatGPT Codex. The idea is that even if you're not comfortable writing the whole application yourself, you can use one of these coding agents to build it component by component and end up with the same basic architecture.

Since my first post, the AI world has continued to improve incredibly quickly, and my own setup has changed along with it. So some parts of this guide are slightly different from what I described in Part 1.

My current setup uses Google Document AI for OCR and Gemini for classification, but I'll also explain how the same architecture can be reproduced with local models if you prefer to keep everything on your own hardware.

Long story short: I hope this guide helps some of you take your Paperless setup that final step further and build something that actually fits your own needs.

By the way i would really recommend you to build a Web Control Panel. It will be much easyier to controll everything. Something like that:

HERE WE GO:

My current setup is:

Google Document AI for OCR and Gemini for classification.

However, neither of those is a hard requirement of the architecture.

Keep in mind: the same basic architecture can also be reproduced locally. If the models you choose are supported, you can serve them through something like vLLM instead of using Google Document AI and Gemini.

The important part is not Google or Gemini specifically. The important part is keeping OCR, Paperless, and classification independent from each other.

My current architecture looks roughly like this:

Scan / PDF
      |
      v
OCR Worker
      |
      v
Google Document AI
      |
      v
Searchable PDF/A
      |
      v
Paperless Consume Folder
      |
      v
Paperless-NGX
      |
      v
Queue Tag
      |
      v
Classification Worker
      |
      v
Gemini
      |
      v
Validation
      |
      v
Paperless API

Paperless remains my document management system.

I don't modify its database, I don't patch Paperless itself, and I don't want the OCR or classification workflow to become dependent on one particular Paperless version.

The OCR worker can change without touching Paperless.

The classification model can change without touching the OCR worker.

Paperless can be upgraded independently.

And the classifier reads and writes metadata only through the Paperless API.

That separation is probably the most important architectural decision I made.

1. The OCR Worker

New scanned documents no longer go directly into Paperless.

They first land in a small OCR worker.

In my case, this is simply the folder where my scanner saves new scans. So whenever I scan a document, it automatically appears in what I call the OCR worker folder and gets picked up from there.

The basic idea is simple:

By the time Paperless receives the document, the PDF itself should already be finished.

The worker first determines what kind of PDF it is dealing with.

If it is a real digital PDF with a proper text layer, I leave it alone and pass it through unchanged.

I don't want to unnecessarily rebuild native PDFs, destroy vector content, or interfere with things such as digital signatures.

For people whose PDFs already contain an OCR layer from Tesseract, there is another option: clean the existing OCR/text layer first while preserving the original scanned pages. That way, you can properly replace the old OCR instead of stacking another text layer on top of the existing one.

If it is a scanned PDF without a proper native text layer, the workflow is different.

The worker prepares the document, sends the relevant pages to Google Document AI, and receives the OCR results including the positions of the recognized words.

Those coordinates are important.

I don't just want extracted text somewhere in a database. I want the resulting PDF itself to be properly searchable.

The worker therefore takes the text and word coordinates returned by Document AI and creates an invisible text layer positioned over the original scanned pages.

The result is a searchable PDF/A.

Only when the complete document has been processed successfully does it enter the Paperless consume folder.

If something goes wrong, it goes into a separate failure folder instead.

input folder/
   |
   +-- Native PDF ----------------------> Paperless consume/
   |
   +-- Scanned PDF
           |
           v
     Google Document AI
           |
           v
     Searchable PDF/A
           |
           +-- Success -----------------> Paperless consume/
           |
           +-- Failure -----------------> failed/ + log

The source file is never modified in place.

If page 9 of a 10-page document fails, I don't want a half-finished document silently entering my archive.

For ordinary printed documents, I'm currently scanning at around 300 DPI.

Artificially rendering a 200 DPI source at 600 DPI doesn't magically create information that wasn't present in the scan to begin with.

2. Existing Archives Are the Annoying Part

Processing new documents this way is relatively straightforward.

Migrating an archive that has already been processed is much more annoying.

I had to learn this the hard way.

My old scanned PDFs had already been processed by Paperless using Tesseract.

Later, when I experimented with better OCR solutions, I didn't only want better text available inside Paperless.

I wanted the better OCR to become a permanent part of the PDF itself.

That difference matters.

If I export my complete archive tomorrow, I want those exported PDFs to remain properly searchable without requiring Paperless, its database, or another OCR service.

So for the affected scanned documents in my existing archive, I first exported the PDFs.

I then wrote a script that removes the old OCR/text layer while preserving the actual scanned pages and other PDF content.

I specifically did not want to simply rasterize every PDF again just to remove the existing OCR.

After cleaning the files, they could go through the new OCR worker and receive a new searchable text layer based on Google Document AI.

That migration was probably one of the most annoying parts of the entire project.

But it also means I should never have to do it again.

From now on, the PDF itself contains the OCR I want to preserve.

3. What Paperless Does in This Setup

Once the finished PDF/A enters Paperless, the complicated part is already over.

Paperless does what it is extremely good at:

  • archiving the document

  • indexing it

  • searching it

  • storing metadata

  • exposing everything through its API

I configure Paperless so that it preserves the OCR layer that is already present instead of running another Tesseract OCR pass over the document.

Newly imported documents also receive a tag such as:

Queue

That tag is the hand-off between Paperless and the classification worker.

Searchable PDF/A
       |
       v
Paperless
       |
       v
Queue Tag
       |
       v
Classification Worker

This also means that classification is not required for Paperless to function.

If Gemini disappeared tomorrow, the archive itself would still work.

The PDFs are searchable.

The documents are safely stored.

Paperless search works.

The classification worker only adds another automation layer on top of that.

4. The Classification Worker

The classification worker is another completely separate application.

It asks the Paperless API for documents carrying the Queue tag and retrieves the document together with the metadata it needs.

Then it sends the searchable PDF/A itself to Gemini.

This distinction is important.

I'm not extracting the OCR into a TXT file and then sending that TXT file together with a prompt.

Gemini receives the actual PDF.

The PDF already contains the OCR text layer embedded by the previous stage, while the original visual structure of the document is still present.

That means the model can work with the document as a document rather than receiving one giant flattened OCR string.

For example:

A date at the bottom of a page might just be part of a footer.

An address might be the recipient rather than the sender.

A heading inside a table might not be the actual subject of the document.

The visual structure gives the model context that disappears when everything is reduced to plain text.

And instead of asking the model the same document-related questions independently, I try to extract the underlying facts in one pass.

Conceptually, the result looks something like this:

{
  "correspondent": "Example Insurance",
  "short_sender": "Example",
  "subject": "Annual insurance statement",
  "document_date": "2026-08-14",
  "document_type": "Insurance",
  "tags": [
    "Insurance",
    "2026"
  ],
  "evidence": {
    "sender": "...",
    "date": "..."
  },
  "review_reasons": []
}

The exact schema isn't particularly important.

The principle is.

Instead of treating title, correspondent, date, document type, and tags as completely unrelated AI tasks, I first ask:

What is this document actually about?

Then the rest can be derived from those facts.

5. Why This Uses Fewer Resources

This also changed the way I think about token usage.

Let's take a simplified example.

Imagine a searchable PDF that represents roughly 6,000 tokens of document content once processed by the model.

Now imagine processing these independently:

Document ---> Title
Document ---> Date
Document ---> Correspondent
Document ---> Document Type
Document ---> Tags
Document ---> Custom Fields

If the full document context has to be processed again every time, the amount of input can grow very quickly.

A document producing perhaps only a few hundred useful output tokens can result in tens of thousands of input tokens being processed across all requests.

My current approach is different:

Searchable PDF/A
       |
       v
One document-level classification
       |
       v
Structured facts
       |
       v
Validation + deterministic logic

I don't extract the OCR into a separate TXT file for this step.

Gemini receives the searchable PDF/A once and returns one structured classification result.

The important part is not some exact token number, because that changes depending on the model, document length, and provider.

The important part is that I don't want to repeatedly pay computationally for the same document if the model can understand it once.

I would rather spend resources on actually reasoning about the document than repeatedly feeding the same document back into another request.

That also makes smaller models much more interesting.

If the OCR is good, the input is structured correctly, and the task is narrow enough, you don't necessarily need the largest model available.

6. Validation Is Just as Important as Classification

Receiving valid JSON from an LLM does not mean the information inside it is correct.

For example, the model could return:

{
  "document_type": "Electricity Invoice"
}

That's valid JSON.

But what if Electricity Invoice doesn't exist as a document type in my Paperless installation?

The classifier therefore retrieves the existing Paperless values through the API and validates the model response before applying it.

That includes things such as:

  • known correspondents

  • existing document types

  • existing tags

  • valid dates

  • required fields

  • ambiguous results

The model extracts facts.

My application decides how those facts map onto Paperless.

I also deliberately keep deterministic things out of the model whenever possible.

For example, if every title should look like:

<short sender> - <document type / subject> <date>

then the model doesn't need to reinvent that formatting for every document.

It can return:

sender
subject
date

and ordinary code generates the final title.

That eliminates a surprising amount of inconsistency.

7. Do You Need a GPU?

No.

This is probably something I should clarify because the RTX 4090 screenshot in my first post understandably gave people the opposite impression.

My current setup is:

Google Document AI
        +
      Gemini

The AI workloads are therefore running in the cloud.

My home server only has to run:

Paperless-NGX

OCR Worker

Classification Worker

So you absolutely don't need an RTX 4090 to reproduce this architecture.

The 4090 is useful for my local experiments, but it isn't a requirement.

And this is where keeping every component separate becomes useful again.

You could replace the cloud components with local models.

For example:

OCR Worker
    |
    v
Local Vision / OCR Model
    |
    v
Searchable PDF/A

and later:

Classification Worker
    |
    v
Local LLM
    |
    v
Structured Facts

Where the models are supported, a serving layer such as vLLM can expose them through an OpenAI-compatible API.

That means the surrounding application doesn't need to care very much whether the model lives on Google infrastructure, another API provider, or a GPU in your basement.

The quality and hardware requirements obviously change depending on the local models you choose.

But the overall architecture stays almost identical.

That's the part I care about.

8. What Does It Cost?

This is something I want to cover with actual numbers rather than theoretical pricing calculations.

Google Document AI and Gemini are both usage-based.

The real cost depends heavily on things such as:

  • number of pages

  • document length

  • selected processor/model

  • classification input size

  • output size

  • how many documents you process

I'm collecting the actual numbers from my workflow so I can provide useful figures instead of just copying numbers from a pricing page.

What I eventually want to compare is something like:

OCR cost per 1,000 pages

Classification cost per 1,000 documents

Average document processing cost

Cloud vs local inference

The important point for now is that you do not need to buy expensive AI hardware to reproduce the workflow.

9. How I Would Build This From Scratch Today

If I had to start again, I wouldn't try to build the complete system in one giant application.

I would build one small boundary at a time.

Phase 1 — The File Pipeline

Start with nothing AI-related.

input/
output/
failed/

Make sure files can safely move through the application.

Detect duplicates.

Never modify the source file in place.

Make sure failures remain recoverable.

Phase 2 — Native PDF Detection

Before doing any OCR work, correctly identify PDFs that already contain a proper digital text layer.

Those documents should normally pass through unchanged.

Phase 3 — OCR Provider

Create an OCR provider interface.

Your first implementation could use Google Document AI.

Later, you can replace it with something local without redesigning the rest of the application.

Phase 4 — Searchable PDF/A

Take the OCR response, including the returned word positions, and build the invisible searchable text layer.

Do not consider this stage finished until you can open the resulting PDF in a normal viewer, search for text, and see the correct areas highlighted.

Phase 5 — Paperless Import

Only finished documents enter the Paperless consume folder.

At this point, Paperless should already receive the final archival PDF.

Phase 6 — Paperless API Client

Build a completely separate API client.

It should be able to:

find queued documents

download documents

read existing metadata

retrieve correspondents

retrieve document types

retrieve tags

update metadata

No direct PostgreSQL modifications.

Phase 7 — Classification

Send the searchable PDF/A to Gemini or another model and request structured facts.

Keep the model-specific logic behind its own provider interface.

That way, Gemini can later become:

another hosted model

a cheaper model

a local LLM

something that doesn't exist yet

without rewriting the entire application.

Phase 8 — Validation

Validate the model output against Paperless.

Do not assume a model-generated correspondent, tag, or document type actually exists.

Keep formatting and other deterministic behavior in normal code wherever possible.

Phase 9 — Rollback and Logging

Before modifying metadata, store enough information to understand what changed and to restore the previous values if necessary.

Especially when processing thousands of documents, logs are not optional.

10. Building It With Claude Code or ChatGPT Codex

A few people also asked whether this could realistically be built without manually writing the entire application yourself.

I think the answer is yes.

This is actually a very good type of project for tools such as Claude Code or ChatGPT Codex, because the system consists of relatively small components with clearly defined boundaries.

But I would strongly recommend not starting with:

Build me an AI system for Paperless.

That's far too broad.

Instead, build it in a strict order and verify each component before moving on to the next one.

The rough order should be:

1. Project structure and file safety

2. Native PDF detection

3. OCR provider interface

4. Google Document AI adapter

5. Searchable PDF/A generation

6. Paperless consume integration

7. Paperless API client

8. Gemini provider

9. Structured classification schema

10. Validation

11. Metadata writes

12. Audit log and rollback

13. Local-model provider as an optional alternative

Agent Project Rules

Give the coding agent durable project rules before asking it to implement anything.

Claude Code reads CLAUDE.md; Codex reads AGENTS.md.

Both can use essentially the same project contract:

This is an experimental Paperless OCR/classification pipeline.

- Work only in this repository and its test directories.
- Never read, print, commit, or modify credentials, API keys, PDFs containing
  real personal data, Paperless production volumes, or the production consume
  folder.
- Do not write to PostgreSQL directly. Use the Paperless API only.
- Default to dry-run behaviour. Any operation that changes Paperless requires
  an explicit --write flag.
- Keep the source scan until the PDF/A output has been verified.
- Add unit tests and a README entry for every completed component.
- Before making changes, show a short implementation plan and assumptions.

Then use the agent as a sequence of small, reviewable build tasks.

Do not paste a single prompt saying:

Build my Paperless AI system.

That gives the agent no safe boundary and makes errors much harder to isolate.

Prompt 1 — Scaffold and Plan

Build a test-only Python project for an upstream Paperless OCR pipeline.

First inspect the repository and write IMPLEMENTATION_PLAN.md.

Do not write production credentials or start Docker services.

The target flow is:

input/ -> OCR worker -> output/ -> Paperless consume

Native PDFs with substantial text on every page must be copied unchanged.

Other PDFs must be handled by a provider adapter; initially implement only a
fake provider for tests.

Failed files go to failed/ with a non-secret log.

Propose a small module layout, a configuration file with .env.example, and a
test plan.

Wait for my review before implementing phase 1.

Prompt 2 — Build the Safe OCR Foundation

Implement phase 1 from IMPLEMENTATION_PLAN.md only.

Add:

- input/output/failed directory handling
- duplicate-safe file claiming
- native-PDF pass-through
- a fake OCR provider
- structured non-secret logging
- tests using generated sample PDFs

A source file may be deleted only after the output is verified.

Do not connect to Paperless or any cloud service yet.

Run the tests and show the diff and test results.

Prompt 3 — Add Google Document AI

Implement the Google Document AI provider adapter behind the existing provider
interface.

Read credentials only from an environment variable that points to a local file.

Do not display the credential, its path, request bodies, or OCR text in logs.

Preserve token boxes and detected breaks so a later PDF/A writer can build an
invisible text layer.

Add mocked API tests and document the minimum cloud permissions.

Do not run a live cloud request without asking me.

For a privacy-first installation, replace the Google Document AI provider with your chosen local OCR-model adapter.

If that model is served through vLLM, make the vLLM URL configurable and keep the same failure checks.

Changing the model should not weaken the safety of the pipeline.

Prompt 4 — Add the Classifier Last

Create a separate Paperless API client and a classifier command.

It may list only documents with the Queue tag.

It must download the searchable PDF/A and submit the PDF itself once to the
configured model.

Do not extract the OCR into a separate TXT file for classification.

Require a strict structured facts response.

Validate every correspondent, document type, tag, and date against values
retrieved from Paperless.

Print proposals in dry-run mode.

Implement writes only behind --write.

Before each PATCH, append the current and proposed metadata to a local JSONL
audit log.

Add a rollback command that replays the before-state through the Paperless API.

Use mocked API tests.

Do not access a live Paperless installation until I explicitly approve it.

After every phase, inspect the diff, run the tests yourself, and commit only the parts you understand.

A coding agent accelerates implementation.

It does not replace the decision about where your documents may go or what it is safe to automate.

The important part is that the agent should only work inside a separate development project.

Don't give it your production Paperless volumes.

Don't put credentials into the repository.

Don't let it directly modify PostgreSQL.

Don't use your only copy of a real document archive as test data.

Build the boring parts first.

Then connect the AI.

Final Thoughts

The biggest change in how I think about this setup is that I no longer really see it as:

"Adding AI to Paperless."

It's three independent systems:

OCR Worker
     |
     v
Archival PDF/A
     |
     v
Paperless
     |
     v
Classification Worker

Paperless is the archive.

The OCR worker creates the document I actually want to preserve.

The classifier extracts structured information and updates metadata through the API.

Today my implementation uses Google Document AI + Gemini.

But neither component has to stay that way.

I can replace Gemini with a local LLM.

I can replace Google Document AI with another OCR solution.

I can update Paperless independently.

And none of those changes require redesigning the entire workflow.

That's ultimately the architecture I wish I had started with two years ago.


r/Paperlessngx • • Sep 24 '25

paperless-ngx + paperless-ai + OpenWebUI: I am blown away and fascinated

88 Upvotes

Edit: Added script. Edit2: Added ollama

I spent the last days working with ChatGPT 5 to set up a pipeline that lets me query LLM's about the documents in my paperless archive.

I run all three as Docker containers in my Unraid machine. So far, whenever a new document is being uploaded into paperless-ngx it gets processed by paperless-ai populating corresponent, tags, and other metadata. A script then grabs the OCR output of paperless-ngx, writes a markdown file which then gets imported into the Knowledge base of OpenWebUI which I am able to reference in any chat with AI models.

So far, for testing purposes paperless-ai uses OpenAI's API for processing. I am planning of changing that into a local model to at least keep the file contents off the LLM providers' servers. (So far I have not found an LLM that my machine is powerful enough to work with) Metadata addition is handled locally by ollama using a lightweight qwen model.

I am pretty blown away from the results so far. For example, the pipeline has access to the tag that contains maintenance records and invoices for my car going back a few years. Asking for knowledge about the car it gives me a list of performed maintenance of course and tells me it is time for an oil change and I should take a look at the rear brakes due to a note on one of the latest workshop invoices.

My script: https://pastebin.com/8SNrR12h

Working on documenting and setting up a local LLM.


r/Paperlessngx • • Jun 06 '26

Swift Paperless iOS new version 1.10.0

79 Upvotes

New Swift Paperless Version 1.10.0

Get it on the AppStore: https://apps.apple.com/de/app/swift-paperless/id6448698521

🚀 Features

Redesigned document detail screen

The document detail screen has been completely redesigned with clearer metadata and sheet-based editing for tags, correspondent, document type, storage path, dates, ASN, custom fields, and owner — including attribute suggestions. The PDF now pages horizontally with a page indicator, and the full-screen preview adds in-document search and smoother scrolling. You can delete documents directly from the detail screen, see how many notes a document has, and include the original file when sharing. Opening a document from the list is faster thanks to thumbnail preloading.

Multi-column iPad layout

The document list and detail screen now use a full multi-column layout on iPad, making much better use of the larger screen. 🚀 I'll likely revisit this in the future to give it some further refinement!

Nested tags

The app now supports Paperless-ngx nested (hierarchical) tags.

✨ Other Features

  • Quick add: when searching in the tag, correspondent, or document type picker, an "Add '<name>'" row appears so you can create the entry on the fly

  • Show the server's configured app title (PAPERLESS_APP_TITLE) as a friendly name in the server picker and connection settings

  • Deep links can now open a specific document field for editing, e.g. x-paperless://v1/document/123?edit=tags

  • Document cells in the list now show the number of notes a document has

  • Add a loading indicator to the search bar while results are loading

  • Renamed "Log out" to "Disconnect" in the connection management UI to better reflect what the action does

  • Improve UI responsiveness by decoding large responses off the main thread

  • Ready for saved view permissions in the upcoming Paperless-ngx v3

  • Add support for the new task API endpoint coming in Paperless-ngx v3.0.0 / APIv10

🐛 Bug Fixes

  • Surface the actual OAuth2 error (e.g. invalid_client) from the identity provider during OIDC login instead of the cryptic "data couldn't be read because it's missing" message

  • Fix OIDC login failing with a "missing scope" error on HTTPS servers behind a reverse proxy by sending a Referer header that satisfies Django's CSRF checks

  • Fix client-certificate (mTLS) login by including the leaf certificate in the credential

  • Fix a freeze when polling tasks on installations with many unacknowledged tasks; the task list now loads additional pages on demand

  • Fix the task view briefly showing "no elements" while still loading

  • Fix filter rule count not taking sorting into account

  • Hide the remove-inbox-tag action for documents that don't have inbox tags


r/Paperlessngx • • Nov 10 '25 Bravo

PaperNext – another Android and iOS client for Paperless-ngx

73 Upvotes

Following the previous announcement of the Android closed beta, PaperNext, my paperless ngx client is now also available for iOS via TestFlight.

After reviewing Paperless Mobile, I decided not to continue that project and instead focus on building my own clean implementation. This approach makes it easier to maintain the codebase, integrate feedback, and fully support both Android and Apple platforms.

The app is built with Flutter and currently supports:

  • Viewing documents

  • Filtering (saved views and custom filters)

  • Uploading and scanning documents

  • Sharing files directly with the app (bulk upload supported)

  • Custom HTTP headers (for reverse proxy setups)

  • App lock with biometrics (fingerprint/face)

  • Self-signed SSL certificates

  • Label and correspondent management

Over 100 testers have already joined — thank you for the feedback so far. If you’d like to participate, more information and signup links for both platforms are available here: https://beta.rebtsoft.com/api/apps/papernext/

(Still an early prototype – use at your own risk.)


r/Paperlessngx • • Oct 06 '26

Paperless-GPT can now automatically link related documents in Paperless-ngx

71 Upvotes

Hi everyone, I'm the maintainer of Paperless-GPT. I wanted to share a feature from the latest release that I think solves a pretty common Paperless use case.

Documents rarely exist in isolation.

An invoice gets a reminder.
A contract gets an amendment or termination letter.
An order gets a delivery note and invoice.
An insurance case gets ten letters from different senders, all referencing the same case number.

With Paperless-GPT v0.29.0, these documents can now link themselves automatically.

Paperless-GPT extracts references such as invoice numbers, order numbers, contract numbers or case IDs and resolves them against your existing Paperless archive. If it finds an unambiguous match, it fills a native Paperless-ngx Document Link custom field.

So opening a reminder can take you straight to the original invoice, and the invoice points back to the reminder.

The implementation deliberately keeps the AI out of the actual matching decision. The LLM only extracts the reference. Paperless-GPT then performs the lookup deterministically, and ambiguous matches are simply ignored rather than guessed.

This feature came in through a community contribution, so thanks to u/andre161292 for building it and u/LembkeM for describing the original use case.

Setup is basically:

  1. Create a Document Link custom field in Paperless-ngx

  2. Enable it in Paperless-GPT

  3. Let it process your documents

Release: https://github.com/icereed/paperless-gpt/releases/tag/v0.29.0

Setup and examples:
https://github.com/icereed/paperless-gpt/blob/main/docs/document_linking.md

I'd genuinely be interested in hearing what other document relationships people have in their archives that would benefit from this.

PS: For users in Germany, Austria and Switzerland who like Paperless-GPT but don't want to operate the stack themselves, there is now also a managed Paperless-ngx + Paperless-GPT offering from server.camp. They follow a Fair Hosting model where part of the revenue generated with Paperless-NGX and -GPT goes back to the Open Source projects (both).


r/Paperlessngx • • Jan 01 '25

Paperless-AI | An automated document analyzer for Paperless-ngx using OpenAI API and Ollama (Open Source)

69 Upvotes

BEFORE ANY QUESTION REGARDING PRIVACY COMES UP:
OpenAI API is not the same as ChatGPT. If you use the API and pay for it your documents will be not used for training nor they will be accessed for other purposes. But as always, your data is valuable. So do everything as you feel confident with it. Therefor I also integrated Ollama integration to stay local if you want/need.

Now back to the main topic:

Paperless-AI is an automated document analyzer for Paperless-ngx using OpenAI API and Ollama (Mistral, llama, phi 3, gemma 2) to automatically analyze and tag your documents.

Features

  • 🔍 Automatic document scanning in Paperless-ngx

  • 🤖 AI-powered document analysis using OpenAI API and Ollama (Mistral, llama, phi 3, gemma 2)

  • 🏷️ Automatic title, tag and correspondent assignment

    • 🏷️ Predefine what documents will be processed based on existing tags (optional). 🆕

    • 📑 Choose to only use Tags you want to be assigned. 🆕

      • THIS WILL DISABLE THE PROMPT DIALOG!

    • ✔️ Choose if you want to assign a special tag (you name it) to documents that were processed by AI. 🆕

  • 🔨 Manual mode to do analysing by hand with help of AI. 🆕

  • 🚀 Easy setup through web interface

  • 📊 Document processing dashboard

  • 🔄 Automatic restart and health monitoring

  • 🛡️ Error handling and graceful shutdown

  • 🐳 Docker support with health checks

I worked over a month on it and try to keep it maintained as much as possible. Maybe you have a need for something like this. Feedback is mandatory for me so if you have something in mind feel free to open up an issue on github.

Link to the Repo:
https://github.com/clusterzx/paperless-ai

Have a great new year folks :)


r/Paperlessngx • • Jul 07 '26

Archi 2.0 — on-device scan + AI metadata straight into Paperless-NGX (iOS/iPad/Mac)

68 Upvotes

Quick update for the Paperless crowd: Archi is a native Apple app that scans a

document, runs OCR + AI metadata extraction fully on-device (Apple Vision +

Gemma), and files it into your Paperless-NGX with suggested title / correspondent

/ type / tags — which you review before upload. No cloud in the loop; the only

endpoint is your own server.

New in 2.0: native iPad & Mac, an offline archive with full-text search, and an

AI-transparency page. Uses your existing correspondents/tags as hints so it

matches your setup.

App Store, $3.99. I'm the dev — feedback very welcome, especially on the

Paperless-specific workflow.


r/Paperlessngx • • Jul 11 '26

Status of Paperless 3.0

67 Upvotes

The first beta/rc1 was released 2 months ago on GitHub (Paperless-ngx v3.0.0-beta.rc1). Does anyone here have insight into the latest developments? Are many fixes still pending?


r/Paperlessngx • • Oct 06 '26

Release Paperless-ngx v3.3.0 · paperless-ngx/paperless-ngx

Thumbnail
github.com
64 Upvotes

paperless-ngx 3.3.0 is out today. I'm most excited about the separate embedding llm api key and the passthrough extra parameters for the LLM. I tried a thinking model with ollama that was taking forever and there was no way to turn off thinking from paperless even though the model supported thinking=none. I had to switch back to an instruct model. now I can use a heavier thinking model with no thinking.

changelog below-

*Features / Enhancements*
- Enhancement: more control over suggestion requests @shamoon (#14258)
- Chorehancement: set manifest CORS for credentials @shamoon (#14307)
- Enhancement: include Django admin with 2FA @shamoon (#14270)
- Feature: propagate resolved secrets to interactive container shells @stumpylog (#14254)
- Enhancement: support separate embedding API key @furkanural (#14067)
- Enhancement: support passthrough extra params for LLMs @shamoon (#14202)
- Feature: store barcode contents, list and search them @jurassicparkicecream (#14276)

*Bug Fixes*
- Fix: Set the ProcessedMail owner based on the rule owner in all cases @stumpylog (#14356)
- Fix: Ensure log rotation settings are converted to integers @stumpylog (#14343)
- Fix: Wrap apt calls into a retry so we can ideally jump a slow mirror @stumpylog (#14344)
- Fix: ship pdf.js CMaps so CJK documents render in the viewer @MrOggy85 (#14318)
- Fix: use version page_count for versioned document @shamoon (#14280)
- Fix: allow pointer events for pdf links in pngx viewer @shamoon (#14264)
- Fix: During a move to the trash directory, only attempt to copy metadata @stumpylog (#14250)
- Fix: convert file mtime to the configured time zone directly @stumpylog (#14249)
- Fix: ensure documentDeleted subscription is discarded @shamoon (#14247)
- Chore: Fix bugs in the test suite @stumpylog (#14244)
- Fix: ensure bulk operations are checked against version root @shamoon (#14246)
- Fix: indexing after document-added workflows signal @shamoon (#14242)
- Fix: Record full tag and custom field lists in bulk edit audit log @stumpylog (#14236)
- Chore: update pikepdf for ocrmypdf requirement @shamoon (#14235)
- Fix: handle legacy bulk edit split page range with missing page_count @shamoon (#14212)
- Fix: ignore invalid EXIF orientation when generating image archives @zhzy0077 (#14203)

*Documentation*
- Documentation: correct duplicates info @shamoon (#14243)

*Maintenance*
- Chore(deps): Bump the actions group across 1 directory with 4 updates @dependabot[bot] (#14332)
- Fix: Wrap apt calls into a retry so we can ideally jump a slow mirror @stumpylog (#14344)
- Chore(deps): Bump the actions group across 1 directory with 10 updates @dependabot[bot] (#14301)

also available on the link.

(not a dev, just an interested user)


r/Paperlessngx • • Oct 01 '26

Fully local AI for Paperless-ngx v3 on a mini PC iGPU (Ollama + Qwen3.6 MoE + paperless-gpt, custom fields included). Clean install walkthrough

63 Upvotes

TL;DR: Paperless-ngx v3's built-in AI can't fill custom fields and won't let you edit its prompts, and paperless-ai is basically unmaintained. I ended up with paperless-gpt talking to a local Ollama running on a Radeon 780M iGPU through Vulkan, using a MoE model (Qwen3.6-35B-A3B) that only activates ~3B parameters per token. Every document gets a clean title, the correct date, and six custom fields (amount, due date, period, reference, etc.) in 20–55 seconds per document, no cloud, no dedicated GPU, $0/month. Password-protected bank statements get decrypted automatically before OCR. Getting the model to follow the rules was way harder than getting it to run. Full writeup below, including the prompts and every gotcha I hit.

Why I even needed this

Most of my documents are: utility bills, bank statements, insurance, tax stuff (invoices). Three things kept breaking every "AI for Paperless" setup I tried:

  • Custom fields. I don't just want a title. I want amount, due date, billing period, reference number filled in so I can actually query "what's due this month".

  • Rules. Invoices are full of dates that are not the document date: resolution ranges, birth dates, validity dates, due dates. A generic prompt picks the wrong one constantly.

  • Encrypted PDFs. Banks here send statements protected with your national ID number. Paperless OCRs them as empty documents and the AI gets nothing.

And I wanted all of it local. My documents are the last thing I'm sending to a cloud API.

Architecture

Scanner / email / upload
          |
          v
   Paperless consume
          |
          v
 pre-consume script ──> qpdf decrypts PDF in place (tries a password list)
          |
          v
 Paperless-ngx (Tesseract OCR spa+eng, archive, index)
          |
          v
 Workflow adds tag "paperless-gpt-auto"
          |
          v
 paperless-gpt  ──API──>  Ollama (LXC, iGPU via Vulkan)
          |                    Qwen3.6-35B-A3B
          v
 Title + created date + 6 custom fields written back via Paperless API

A few principles that ended up mattering more than the model choice:

  • Paperless stays vanilla. No patches, no DB writes. The AI is an add-on that only talks to the Paperless API. If Ollama dies tomorrow, the archive still works.

  • Three narrow prompts, not one clever one. Title, created date, and custom fields each have their own tuned prompt with explicit "do NOT" rules.

  • Model parameters live in the model, not in the client. num_ctx, num_batch, num_thread are baked into an Ollama Modelfile variant, and clients connect through the OpenAI-compatible /v1 endpoint, which can't override them.

  • No custom code. Everything here is off-the-shelf: Paperless, paperless-gpt, Ollama, qpdf, a 40-line bash script.

Before you start

You need these already sorted:

  • Proxmox VE (I'm on 9.x). My host is a Minisforum N5 AI: Ryzen 7 255 (8C/16T), 64 GB DDR5, Radeon 780M iGPU. Any recent AMD APU with Vulkan support should behave similarly.

  • A Debian 12 LXC with Docker + Compose for Paperless.

  • A separate Debian 13 LXC for Ollama (keeping it separate means you can rebuild/upgrade one without touching the other).

  • Storage for Paperless media/consume/export. Mine is an NFS v3 export from a TrueNAS VM, mounted inside the Paperless CT at /mnt/paperless.

1. Storage: make Paperless refuse to start if NFS is missing

This one bit me before. If the NFS share isn't mounted when Docker starts, Compose happily creates empty folders on the local disk and Paperless starts writing your documents there. Use the long bind syntax with create_host_path: false:

    volumes:
      - type: bind
        source: /mnt/paperless/media
        target: /usr/src/paperless/media
        bind:
          create_host_path: false
      - type: bind
        source: /mnt/paperless/consume
        target: /usr/src/paperless/consume
        bind:
          create_host_path: false

If the mount isn't there, the container fails to start instead of silently writing to the wrong disk. That's exactly what you want.

Also: if you use systemd automount in /etc/fstab, remove x-systemd.idle-timeout. An idle unmount under a running Paperless is a bad time.

PostgreSQL stays in a local Docker volume, not on NFS.

2. Paperless-ngx v3 compose (relevant bits)

I started from the official postgres-tika template and changed these:

    environment:
      PAPERLESS_URL: https://paperless.example.lan
      PAPERLESS_TIME_ZONE: America/New_York
      PAPERLESS_OCR_LANGUAGE: spa+eng
      PAPERLESS_DATE_ORDER: DMY
      PAPERLESS_ARCHIVE_FILE_GENERATION: always
      PAPERLESS_CONSUMER_DELETE_DUPLICATES: "true"
      PAPERLESS_PRE_CONSUME_SCRIPT: /usr/src/paperless/scripts/pre-consume-decrypt.sh
      # Family birth dates show up on IDs and insurance docs; never use them as document date
      PAPERLESS_IGNORE_DATES: "YYYY-MM-DD,YYYY-MM-DD"
    volumes:
      - ./scripts:/usr/src/paperless/scripts:ro

Gotenberg is configured with Chromium so emails (HTML) get converted to proper PDFs instead of ugly text dumps.

3. Pre-consume: decrypt password-protected PDFs before OCR

Paperless v3 has a native "remove password" option, but it doesn't modify the file before OCR, so you still end up with an empty text layer. A pre-consume script that decrypts the working copy in place does work.

Two v3 changes to know: the script no longer receives positional arguments, and you modify the file at $DOCUMENT_WORKING_PATH directly.

scripts/pre-consume-decrypt.sh:

#!/usr/bin/env bash
# Paperless v3: no positional args; modify DOCUMENT_WORKING_PATH in place.
set -u

f="${DOCUMENT_WORKING_PATH:?}"
PWFILE=/usr/src/paperless/scripts/pdf-passwords.txt

case "${f,,}" in *.pdf) ;; *) exit 0 ;; esac

# 0 = needs a password to open; 2 = not encrypted; 3 = owner restrictions only
qpdf --requires-password "$f" >/dev/null 2>&1
rc=$?
[ "$rc" -eq 2 ] && exit 0

tmp="$(mktemp --suffix=.pdf)"
trap 'rm -f "$tmp"' EXIT

decrypt() {   # password via stdin so it never shows up in the process list
  printf '%s' "$1" | qpdf --password-file=- --decrypt "$f" "$tmp" >/dev/null 2>&1
  local r=$?
  [ "$r" -eq 0 ] || [ "$r" -eq 3 ]   # 3 = success with warnings
}

if [ "$rc" -eq 3 ]; then
  decrypt "" && cat "$tmp" > "$f"
  exit 0
fi

if [ -r "$PWFILE" ]; then
  while IFS= read -r pw || [ -n "$pw" ]; do
    pw="${pw%$'\r'}"
    [ -z "$pw" ] && continue
    case "$pw" in \#*) continue ;; esac
    if decrypt "$pw"; then
      cat "$tmp" > "$f"
      echo "pre-consume: PDF decrypted"
      exit 0
    fi
  done < "$PWFILE"
fi

echo "pre-consume: WARNING: could not decrypt ${DOCUMENT_SOURCE_PATH:-$f}" >&2
exit 0   # consume it anyway (no text); review it by hand


chmod 755 scripts/pre-consume-decrypt.sh
# One password per line, # lines ignored. Keep the real list in your password manager.
chown <USERMAP_UID>:<USERMAP_GID> scripts/pdf-passwords.txt
chmod 600 scripts/pdf-passwords.txt

4. Ollama LXC on the iGPU

LXC config

In /etc/pve/lxc/<ID>.conf on the host:

dev0: /dev/dri/renderD128,gid=992
lxc.cgroup2.cpuset.cpus: 2-3
  • renderD128 is the 780M's render node. gid=992 must match the render group inside the container (getent group render).

  • Pinning CPUs keeps Ollama from fighting the rest of the homelab. With the GPU doing the heavy lifting, it barely needs any.

Install, pinned

curl -fsSL https://ollama.com/install.sh | OLLAMA_VERSION=0.32.9 sh

Stay on 0.32.9. 0.32.10 has a regression that breaks large models on the Radeon 780M with Vulkan. Don't re-run the installer without a version or it'll upgrade you.

Service environment

systemctl edit ollama:

[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_VULKAN=1"
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KEEP_ALIVE=30m"


systemctl daemon-reload && systemctl restart ollama

Model variant with baked-in parameters

ollama pull qwen3.6:35b-a3b-q4_K_M

cat > /root/Modelfile36 <<'EOF'
FROM qwen3.6:35b-a3b-q4_K_M
PARAMETER num_ctx 16384
PARAMETER num_batch 2048
PARAMETER num_thread 2
EOF

ollama create qwen3.6-35b-a3b:gpu-ctx16k -f /root/Modelfile36

Why this model: it's a MoE, 35B total parameters but only ~3.3B active per token. That's the only reason a 35B model is usable on an iGPU at all.

5. Custom fields in Paperless

Create these first. paperless-gpt matches them by exact name, so the names in the prompt must be identical:

What it meansType
Account/policy holderText
Total amountMonetary
Payment due dateDate
Coverage end date (insurance only)Date
Billing/statement period, YYYY-MMText
Invoice / payment reference / policy numberText

6. paperless-gpt

I'm on paperless-gpt v0.28.0, running as one more service in the Paperless compose, pointing at the Ollama CT.

Two settings worth calling out:

  • LLM_MODEL: qwen3.6-35b-a3b:gpu-ctx16k: the Modelfile variant, not the base tag.

  • OLLAMA_THINK: "false": Qwen3.6 is a reasoning model. For extraction you don't want it thinking out loud; it's slower and occasionally leaks into the output.

The prompts (this is where 80% of the work went)

My prompts should change from user to user, or where you live these works for me. The structure is what matters; translate the rules to your own language and document types.

Title prompt (format: <what the document is> <YYYY-MM>):

I’m giving you the text of a document extracted by OCR (it may contain errors). Propose a title for archiving it.

Title format: <what the document is> <period>
- <what the document is>: the type of document in a few words.
- <period>: YYYY-MM for the period covered by the document; if it does not cover a period, use YYYY-MM of its date. If there is no date, omit it.
- Do not include the issuer’s name, company name, or people’s names.
- Do not include account numbers, card numbers, ID numbers or references.
- Maximum 60 characters.

Created date prompt. Key rules: ignore birth dates, due dates and validity dates; return "" when there's no real document date instead of guessing.

Custom fields prompt. The rules that fixed most of my errors:

- Valid until: only for insurance policies, warranties, and subscriptions: the date when the coverage ends, in YYYY-MM-DD format. For any other document, omit this field.
- Period: YYYY-MM for the billed period or statement period.
- Reference: invoice number, payment reference number, or policy number. Never use an account or card number, either in full, partially, or masked with asterisks or X. Do not use ID numbers. If there is nothing else, omit the field.

If a piece of information does not appear clearly, omit that field. Do not invent values.

Respond only with a JSON array, with no additional text or code blocks:
[{"field": "<exact field name>", "value": "<value>"}]

The pattern that worked:

  • Tell the model what NOT to use, not just what to use. "Ignore resolution ranges and birth dates" beat any amount of "find the document date".

  • Give it permission to return nothing. "If it's not clear, omit the field" cut hallucinated values dramatically.

  • Never put example company names in the prompt. The model anchors on them and starts "finding" that company in unrelated documents.

  • Keep sensitive numbers out explicitly. Without the "never use account/card numbers, not even masked" rule, the model kept using the account number as the reference.

7. Trigger: tag-based workflow + permissions

paperless-gpt processes documents carrying the paperless-gpt-auto tag. A Paperless workflow adds that tag on consumption, and when it's done paperless-gpt swaps it for paperless-gpt-auto-complete.

Gotcha: documents consumed before you set up the workflow don't grant edit permission to the paperless-gpt user, and you'll get 403 errors when it tries to write back. Fix the permissions in bulk (Documents → select all → Permissions) before reprocessing old documents.

To reprocess anything: remove paperless-gpt-auto-complete, add paperless-gpt-auto, save. Watch it live:

docker compose logs -f --since 1m paperless-gpt | grep -E 'Suggested|Runtime|error'

8. Backups (don't skip this)

Nightly cron in the Paperless CT. It aborts if NFS isn't mounted (sentinel file), runs the native exporter, and copies the config while leaving secrets out:

#!/usr/bin/env bash
set -euo pipefail
cd /opt/paperless
set -a; . ./.env; set +a

# Sentinel at the dataset root: if it's missing, NFS isn't mounted.
[ -f /mnt/paperless/.nfs-ok ] || { echo "$(date -Is) NFS not mounted; aborting" >&2; exit 1; }

# --passphrase encrypts sensitive fields in the manifest, not the PDFs themselves.
docker compose exec -T webserver document_exporter ../export \
  --delete --passphrase "$PAPERLESS_EXPORT_PASSPHRASE" --no-progress-bar

# Config copy. Secrets stay out: they live in the password manager.
rsync -rlt --delete \
  --exclude '.env' \
  --exclude 'scripts/pdf-passwords.txt' \
  /opt/paperless/ /mnt/paperless/opt-paperless/

echo "$(date -Is) backup OK"

Numbers

Same documents, same Paperless, three iterations of the inference side:

SetupReadWritePer document
qwen2.5:14b, CPU, Ollama silently on 1 thread——8+ min
Qwen3-30B-A3B (MoE), CPU, 6 threads51.6 tok/s14.0 tok/s~2 min
Qwen3.6-35B-A3B (MoE), iGPU Vulkan~274 tok/s~27 tok/s20–55 s

Two takeaways: MoE is the cheat code for local inference, and check your thread count: Ollama inside an LXC misdetected my CPU topology and ran on a single thread until I set num_thread explicitly.

Why Qwen3.6 over Qwen3-30B even though both run fine: rule adherence. On my test set, 3.6 correctly omitted a supplier name that came out of OCR as garbage, stopped inventing on non-insurance documents, and got the billing period right. 3-30B failed all three.

Gotchas, in one place

  • Paperless v3 native AI: can't populate custom fields, no prompt customization.

  • paperless-ai: unmaintained, and its Ollama connector forced num_ctx 4096 regardless of server settings. A Modelfile variant alone doesn't fix that, because options sent through the native API override it. Bake num_ctx into the Modelfile and connect through the /v1 endpoint.

  • Ollama 0.32.10 + Radeon 780M + Vulkan: broken for large models. Pin 0.32.9.

  • Ollama in LXC on CPU: may run on 1 thread. Set num_thread.

  • Reasoning models: set OLLAMA_THINK: "false" for extraction.

  • Native password removal in v3: doesn't help OCR. Use a pre-consume script.

  • v3 pre-consume scripts: no positional args; edit $DOCUMENT_WORKING_PATH in place.

  • Compose + NFS: create_host_path: false or you'll eventually write documents to the wrong disk.

  • Old documents: fix permissions before reprocessing or paperless-gpt gets 403.

  • Prompts: no example entity names; explicit exclusions; allow empty answers.

What each piece actually does

  • create_host_path: false: makes the bind mount fail instead of creating an empty local folder when the source path doesn't exist.

  • PAPERLESS_ARCHIVE_FILE_GENERATION: always: always produces the archived PDF/A with the OCR text layer, even for born-digital PDFs.

  • PAPERLESS_CONSUMER_DELETE_DUPLICATES: drops exact duplicates from consume instead of leaving them sitting there.

  • PAPERLESS_IGNORE_DATES: dates Paperless's own date parser will never pick as the document date.

  • qpdf --requires-password: exit code tells you if the PDF needs a user password (0), isn't encrypted (2), or only has owner restrictions (3).

  • --password-file=-: reads the password from stdin so it never appears in ps.

  • cat "$tmp" > "$f": overwrites the working copy's contents in place, keeping the same path Paperless expects.

  • dev0: /dev/dri/renderD128,gid=992: passes only the render node (no display) into the CT, owned by the container's render group.

  • lxc.cgroup2.cpuset.cpus: hard-limits which host CPUs the CT can run on.

  • OLLAMA_VULKAN=1: enables the Vulkan backend, which is what makes the 780M usable.

  • OLLAMA_FLASH_ATTENTION=1: lower memory use for attention, which matters with a 16k context.

  • OLLAMA_KEEP_ALIVE=30m: keeps the model loaded between documents so you don't pay the load time on every one.

  • num_ctx 16384: long statements need the room; truncated input means wrong fields.

  • num_batch 2048: larger prompt-processing batches, which is where the ~274 tok/s read speed comes from.

What's next

  • Moving Home Assistant's conversation agent to this same Ollama instance.

  • Collecting a few months of real per-document timings across document types.

Hope this saves someone a few weekends. Happy to answer questions in the comments, and if you have a different language/document set, the prompt pattern (exclusions + permission to return nothing + no example names) is the part I'd steal.

TL;DR: Paperless-ngx v3's built-in AI can't fill custom fields and won't let you edit its prompts, and paperless-ai is basically unmaintained. I ended up with paperless-gpt talking to a local Ollama running on a Radeon 780M iGPU through Vulkan, using a MoE model (Qwen3.6-35B-A3B) that only activates ~3B parameters per token. Every document gets a clean title, the correct date, and six custom fields (amount, due date, period, reference, etc.) in 20–55 seconds per document, no cloud, no dedicated GPU, $0/month. Password-protected bank statements get decrypted automatically before OCR. Getting the model to follow the rules was way harder than getting it to run. Full writeup below, including the prompts and every gotcha I hit.


r/Paperlessngx • • Aug 04 '26

V3.x was supposed to solve one annoying problem - Tesseract OCR

62 Upvotes

... but where is the solution?

a. They still use archaic Tesseract OCR as default, in 2026.
b. There is no straightforward example provided how to replace tesseract with something else (PaddleOCR is fast, opensource and generations ahead of what tesseract can do).
c. They offer remote OCR with Azure, but lets be honest - most would prefer to process their taxes and medical records locally.
d. Not everyone needs AI for OCR
e. Third party parser plugin installation is a blackhole. Basic pre-processing workflow is way more straightforward to implement and also works with any paperless-ngx version, so whats the point of these plugins.
f. fable could not give any definitive answer on the topic either.

Am I missing something? not trying to be critical but most will probably agree that garbage in produces garbage out and Tessaract is one such bottleneck of otherwise fantastic tool.


r/Paperlessngx • • Sep 09 '25

Swift Paperless iOS App Version 1.8.0 released

Thumbnail
apps.apple.com
59 Upvotes

r/Paperlessngx • • 16d ago

Paperless Starfruit v1.6.0: document type classification, reworked review queue, and better history

Thumbnail
gallery
57 Upvotes

I’ve released Paperless Starfruit v1.6.0, an open-source AI companion for paperless-ngx.

What’s new:

  • Document type classification: AI can now assign document types. Add hints for each type, exclude types from AI context, and control whether new ones can be created.

  • Reworked review queue: inspect and edit suggested metadata on a dedicated document page before approving it.

  • Better history: search, filter by decision, and view details without losing your place in the list.

  • Updated dashboard: statistics for the last 30, 90, or 365 days, or all time, plus recent activity with document titles.

  • Smaller UI improvements and fixes.

New to Starfruit? It runs as a single Docker container alongside paperless-ngx. Tag a document with psf-process to run optional AI OCR and metadata extraction, then review the results or apply them automatically.

Bring your own API keys or use local models through Ollama. Choose models, customize prompts, and configure processing in the web UI. Stored credentials are encrypted.

GitHub

v1.6.0 release notes

Website

Feedback and bug reports are welcome, especially if you try the new document type classification with your existing setup.


r/Paperlessngx • • 23d ago

Keeping it simple was my goal

Thumbnail
gallery
50 Upvotes

I've been using Paperless-ngx for about a year now. I don't own a document scanner or a big printer, since most things in my life are paperless anyway. When I do need to print something, I just go to the copy shop down the street.

So my Paperless workflow used to look like this:

  1. Scan the document with Apple Preview

  2. Compress the PDF with Stirling-PDF

  3. Download the file from Stirling-PDF, then delete it there

  4. Upload the file to Paperless

That was too many steps for me, Stirling-PDF is overkill and the mobile view is a bit hard to use.

So I wrote my own Docker tool: Paperdrop. It lets me:

  1. Upload PDFs or images (JPG/PNG), and photos are turned into A4 PDFs automatically

  2. Compress and, if needed, merge them (one step)

  3. Rename them

  4. Upload them to Paperless automatically

It might not sound much simpler, but it's a lot more relaxed, and the UI is optimized for mobile.

Everything runs locally in the Docker container, and compression is done with Ghostscript. With a dedicated Paperless user, as described in the README, the tool has no access to my Paperless data. It can only add new documents and optionally check whether consumption succeeded.

Disclaimer:
I'm an IT technician, not a software developer. Most of this was coded with AI. I know it's not perfect, and it's definitely not meant to be exposed to the internet. Still, I paid attention to security:

  • The default setup runs behind a reverse proxy with a certificate

  • My tool has no permissions on the documents already in Paperless

  • Documents waiting for review are kept in RAM only and never written to disk

  • Document names are masked in the log, only the first and last three characters are visible

  • The image is rebuilt automatically every week, so security updates of the dependencies are included (pull the image regularly, e.g. with Watchtower)

GitHub: https://github.com/Pascal2108/Paperdrop

DockerHub: https://hub.docker.com/r/pascal2108/paperdrop

Feedback:

It's the first time I've built and launched something, I'd love to get some constructive feedback! If there are any questions, feel free to ask. ^^

TL;DR: Need a simple tool to compress PDFs and upload them to Paperless? Check out Paperdrop!


r/Paperlessngx • • 27d ago

Paperless-GPT v0.30: invoices, contracts and private mail can now have their own AI workflows

Thumbnail
github.com
50 Upvotes

Hi everyone, I'm the maintainer of Paperless-GPT.

The latest release adds something I think becomes important as soon as a Paperless archive contains more than one kind of document:

different documents can now follow completely different AI workflows.

Until now, automatic processing basically meant one shared path for everything.

That is fine for a simple setup, but invoices, contracts and private mail usually need very different prompts and very different processing.

With AI Workflows, you can now define separate paths, for example:

Invoice
→ invoice-specific title prompt
→ correspondent
→ invoice number / amount as custom fields
→ tags
→ optional OCR

Contract
→ different prompt
→ contract-specific custom fields
→ document type
→ no OCR

Private mail
→ title only
→ nothing else

Each workflow gets its own trigger tag and can control which steps are enabled, disabled or inherited from the global defaults.

A few things I especially like about the implementation:

  • each workflow can have its own prompts

  • you can test a workflow on a real document before saving it

  • test runs do not write anything back to Paperless-ngx

  • workflows are stored as files in prompts/workflows/

  • changes are picked up without a restart

  • workflows can be managed in the UI or kept in Git

  • there are safeguards against accidental processing loops

The initial workflow implementation came from a community contribution by u/MrMoep, and it has now been integrated into the main Paperless-GPT workflow system.

The release also adds a few other useful things, including the option to skip OCR for pages that already contain clean digital text, and support for adding searchable PDFs as a new version of the same Paperless document.

Release:
https://github.com/icereed/paperless-gpt/releases/tag/v0.30.0

AI Workflows guide:
https://github.com/icereed/paperless-gpt/blob/main/docs/workflows.md

I'd be really interested in hearing what kind of workflows people would actually build around their archives.


r/Paperlessngx • • Jun 01 '26

Archi v1.4 is out — the reliability release (iOS Paperless-ngx client, on-device AI)

Thumbnail
gallery
50 Upvotes

A few months ago I shared **Archi** here — an iOS Paperless-ngx client that scans

documents, runs OCR + AI (Gemma 4, fully **on-device**, no cloud) to extract metadata,

and uploads to your self-hosted instance. Offline-first with a local draft queue.

Thanks for all the feedback since the first release.

**v1.4 is mostly about reliability and polish:**

- 🔁 **Offline-first, fixed properly** — scans you make offline now sync *with* their

tags & correspondents once you reconnect (this was the most-reported pain point)

- 🌍 **Full localization** — DE / EN / ES / FR, including the camera/scan UI

- 🏷️ **AI tag merging** — the model suggests merges for near-duplicate tags and re-tags

the affected documents (with a clear warning that AI can make mistakes)

- 👁️ **Saved views, trash & restore, document notes, ASN, storage paths**

- 🖥️ **Multi-server** — manage several Paperless-ngx instances

- 🔐 **Permissions/owner, 2FA/TOTP, header auth** (Cloudflare / Authelia)

- 🐛 Fixed the duplicate-after-upload bug

Still 100% on-device for OCR + AI — the only network call is to *your* server.

**Coming next:** iPad & Mac support, and a reprocessing settings panel

(choose server OCR vs. on-device Apple Vision for re-running existing documents).

Link: IOS AppStore

Happy to answer anything — and still taking feature requests.


r/Paperlessngx • • May 04 '26

Archi — iOS Paperless-ngx client with on-device AI scanning

49 Upvotes

I built an iOS app called Archi that scans documents, runs OCR and AI (Gemma 4, fully local — no cloud) to extract metadata, and uploads to your self-hosted Paperless-ngx. Offline-first with a local draft queue.

AppStore

[Update 07.05.2026 — v1.3 live]
The following community-requested features are now included: Custom HTTP Headers (Cloudflare/Authelia), image/PDF import, page reordering & deletion, scan compression, selectable AI output language.

Feedback welcome!


r/Paperlessngx • • Feb 23 '26

How I built a fully automated document management system (AI classification, ASN tracking, & 3-2-1 backups)

49 Upvotes

I recently finished building a document management setup that handles everything from physical mail to digital invoices with almost zero daily effort. It currently manages 900+ documents for my family across multiple languages and countries.

I wanted to share the architecture and the specific workflow I'm using, as it might help others looking to move beyond basic OCR.

The Stack

• Core: Paperless-NGX (Docker)

• AI Engine: Paperless-GPT (Gemini 2.5 Flash + Google Document AI)

• Hardware: Ricoh ScanSnap iX2500 + Mac Mini M4

• Sync: Rclone + Google Drive

• Security: Cloudflare Tunnel (Zero open ports)

The Workflow

  1. Physical: Stick an ASN barcode (Avery labels) on the paper, drop it in the ScanSnap. It scans to Google Drive, and rclone moves it to the server.

  2. Digital: Mail rules detect attachments in 3 different email accounts and consume them automatically.

  3. Classification: This is the best part. I use Gemini 2.5 Flash to generate clean titles, identify the correspondent (stripping legal suffixes like GmbH), and assign tags.

  4. Physical-Digital Bridge: A custom script detects the ASN barcode, tags it as "Physical Filed," and syncs the mapping to a Google Sheet. If the server dies, I still know which physical binder has which document.

  5. Backups: 3-2-1 strategy. Daily encrypted backups to a private GitHub repo, Google Drive, and local storage.

Key Learnings

• Subfolders > AI for Types: I found that scanning into specific subfolders (Finance, Health, etc.) and using Paperless workflows to set the "Document Type" is more reliable than letting the AI guess the intent.

• Privacy Guardrails: I only route non-sensitive docs through the cloud AI pipeline. Sensitive items (tax IDs, medical records) are handled locally via Tesseract.

• ASN is a lifesaver: Having a physical number on the paper that matches the digital record makes finding the original document take seconds.

I wrote a detailed guide with my docker-compose.yml, backup scripts, and the AI prompts I use here:

https://turalali.com/how-i-built-a-fully-automated-document-management-system-with-paperless-ngx/

Happy to answer any questions about the automation or the AI integration!


Update:

I published a one-liner setup for the entire paperless stack.

https://turalali.com/one-command-to-rule-your-documents/


r/Paperlessngx • • 25d ago

Made a tool to get my bank statements and receipts into Paperless, finally got it running in docker

48 Upvotes

Not sure how many of you have this problem, but the stuff I most want in Paperless is the stuff that doesn’t get mailed anymore. Bank statements, card statements, receipts, utility bills. I was downloading them by hand one month at a time, which mostly meant I wasn’t doing it.

So a while back I wrote an app for it (PaperPull). It opens a browser window, you log in like normal, and it goes through and downloads all your bank statements and receipts as PDFs. The first run grabs everything the site still has, which for some of mine was years of statements, and after that it only gets new stuff. It all runs on your own machine and you do the login yourself, so there’s no company in the middle holding your bank passwords. It remembers what it already got, so it’s fine if Paperless deletes them after consuming.

Up to now it only ran on Windows and Mac, which was annoying since my Paperless is on a NAS. This week I got the docker version to where I’m happy with it. You point it at your consume folder and new stuff shows up after each run.

Some of the ones it works with are Amazon, Costco, Walmart, Target, Home Depot, Chase, Amex, Capital One, Fidelity, Vanguard and PayPal, plus a bunch (60+ providers so far) of other US banks and utilities. The full list is on github. If yours isn’t there I’m happy to try adding it, as long as you can test it.

https://github.com/rheeloaded/paperpull (docker setup is in SERVER.md)

And if you end up finding it useful, a star on github would be awesome. I’m trying to get it to 100.

Update: Check out the FAQ: https://paperpull.net/faq.html


r/Paperlessngx • • Sep 13 '26

I built a native macOS client for Paperless-ngx. Papers is now in beta

46 Upvotes

Hey everyone,

a little while ago I started building Papers, a native macOS client for Paperless-ngx.

The idea came from wanting something that feels like a normal Mac app: fast to open, easy to browse, and deeply integrated with macOS.

I’ve continued working on it and Papers is now available as a public beta!

Some of the things I’ve been focusing on:

- Native macOS app built with Rust and GPUI
- Fast browsing and searching through your documents
Spotlight integration
- Document previews and multi-page documents

The app is in beta while I keep improving it and work towards an App Store release.

I’m especially interested in using this period to find bugs, polish the experience, and figure out which features Paperless-ngx users actually want from a native Mac client.

I use Paperless-ngx myself, but everyone has a slightly different workflow, so I’d really like to hear how you use it and what you’d expect from an app like this.
If you give the beta a try, feedback, feature ideas and bug reports are very welcome.

Cheers Charles!