Paperless-ngx + Tika: Index Office Docs and Emails
You scan a contract, drop it into Paperless-ngx, and thirty seconds later you can search the text inside it. It feels like magic the first time.
Then you drop in the .docx version of that same contract and it lands in your archive as a file with a name and nothing else. No text. No search. Same for the .xlsx of last year's invoices, and same for the .eml export of the thread where the terms were actually agreed.
It is a common complaint, and it is not a bug. Out of the box Paperless-ngx handles PDFs and images, because those go through OCR. Office documents and email need two extra services that Paperless knows how to talk to but does not ship with: Apache Tika and Gotenberg.
One thing to clear up first, because it causes real confusion: this is not the same as Paperless's built-in mail fetching. That feature logs into an IMAP account and pulls messages in for you. What we are fixing here is whether Paperless can read the contents of an email at all once it has one, whether that arrived through mail fetching or as an .eml file you dropped in the consume folder. Without Tika and Gotenberg, both routes give you an unreadable document.
What each piece actually does
They are not interchangeable, and understanding the split makes the config obvious.
Tika extracts. Point it at a .docx, .xlsx, .odt or .eml and it returns the text and metadata inside. Apache has been maintaining it for about twenty years and it recognizes well over a thousand file types. This is what makes the document searchable.
Gotenberg converts. Paperless does not just want text, it wants an archival PDF version of every document so you have something stable to look at years from now. Gotenberg renders the original into a PDF. We covered Gotenberg on its own a while back if you want the deeper tour.
Paperless orchestrates both. You need both running or the Office parser stays off.
Wiring it up
On Elestio your Paperless-ngx service is a Docker Compose stack, so open a terminal on the service and edit /opt/app/docker-compose.yml. Add two services:
gotenberg:
image: docker.io/gotenberg/gotenberg:8.34
restart: always
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
tika:
image: docker.io/apache/tika:3.3.1.0
restart: always
Then three environment variables on the webserver service:
environment:
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
Bring it up:
cd /opt/app && docker-compose up -d
Note the middle variable name. It is PAPERLESS_TIKA_GOTENBERG_ENDPOINT, not PAPERLESS_GOTENBERG_ENDPOINT. Both Gotenberg settings live under the Tika namespace because Paperless treats them as one optional feature. People guess the shorter name, get no error, and spend an hour wondering why nothing changed.
The part everyone misses
Look again at those two Gotenberg flags. They are not decoration.
To convert an email, Gotenberg renders it in headless Chromium. An HTML email is a web page, and web pages fetch things. Tracking pixels. Remote images. Whatever the sender embedded. Render one unprotected on your server and you have quietly told the sender that their email was opened, from your server's IP, on the date you archived it. Do that across an inbox export and you have announced your archiving schedule to every marketer who ever mailed you.
--chromium-disable-javascript=true kills scripts. --chromium-allow-list=file:///tmp/.* restricts Chromium to local files, so remote fetches never happen.
This is the opposite of the usual privacy trade. You self-hosted a document archive to keep your paperwork off other people's servers. Skipping these two flags means the act of archiving becomes the leak.
The Paperless maintainers ship these flags in their reference compose file for exactly this reason. Copy them.
Pin the versions
Use gotenberg:8.34 and tika:3.3.1.0 rather than latest. Those are the versions Paperless-ngx currently tests against, and this is a three-service handshake where a surprise major bump in either dependency breaks ingestion quietly. You find out weeks later when you search for something and it is not there.
Check what your Paperless release pins before you upgrade anything.
Budget for the RAM
Be realistic about this. Tika is a JVM service and Gotenberg carries Chromium and LibreOffice. You are adding two hungry containers to a stack that was previously fine on very little, and the Elestio Paperless-ngx template is specified at 2 GB precisely because it assumes you are not running them.
| Setup | Sensible RAM | Elestio tier |
|---|---|---|
| Plus Tika and Gotenberg, steady use | 4 GB | NC-MEDIUM-2C-4G, $16/mo |
| Bulk importing an inbox or archive | 8 GB | NC-LARGE-4C-8G, $29/mo |
Open source means no license fees here, but the VM is real infrastructure with a real bill. Check current pricing before you resize. If you plan to dump years of email in one go, size up for the import and come back down after, since Elestio bills hourly and a one-day bump costs pennies.
Troubleshooting
Office files still import with no text. Check PAPERLESS_TIKA_ENABLED is actually reaching the container with docker-compose config. A variable in the wrong indentation block silently does nothing.
Everything works except .eml files. That is Gotenberg, not Tika. Email conversion uses the Chromium route while Office files use the LibreOffice route, so email can fail on its own. Check the Gotenberg container logs.
Documents already in your archive are still empty. Enabling Tika does not reprocess history. Re-run the document through the consume folder, or use the bulk edit tools to trigger reprocessing.
Ingestion hangs or the container restarts. Almost always memory. Watch docker stats during an import.
Connection refused in the Paperless logs. The endpoints use Docker service names, so tika and gotenberg have to be in the same Compose project. Do not substitute localhost.
Worth the twenty minutes
This is a small change with a disproportionate payoff. Two containers, three variables, and the half of your document archive that was invisible becomes searchable alongside everything else.
The flags are the part to not skip. An archive that phones home about what you are archiving is worse than no archive.
You can deploy Paperless-ngx on Elestio and add both services in an afternoon.
Thanks for reading ❤️ See you in the next one 👋