Applications Azure

Paperless-ngx with Tika and Gotenberg on Ubuntu 24.04 on Azure User Guide

| Product: Paperless-ngx with Tika and Gotenberg on Ubuntu 24.04 LTS on Azure

Overview

Paperless-ngx is the popular open source document management system: a self-hosted, searchable digital archive for your paperless office. Drop a document into the consume directory and Paperless indexes it, tags it and archives a searchable copy.

Stock Paperless-ngx handles PDFs and images through its OCR pipeline, but it cannot read Office documents on its own. This image adds the two companion services that give it full Office document support, already installed, wired together and proven working:

  • Apache Tika extracts the text and metadata from Office files.
  • Gotenberg converts them to PDF so Paperless has a readable preview and thumbnail.

The result is that a Word, Excel, PowerPoint, OpenDocument or RTF file dropped into the consume directory arrives in Paperless as a fully searchable document with a rendered preview, exactly like a scanned PDF. Nothing to configure: PAPERLESS_TIKA_ENABLED, PAPERLESS_TIKA_ENDPOINT and PAPERLESS_TIKA_GOTENBERG_ENDPOINT are already set and verified at build time.

This image runs everything bare metal, with no containers. The granian application server serves the bundled Angular frontend on loopback, a Celery worker and Celery beat scheduler run the background pipeline, and a consumer process watches the input folder. PostgreSQL provides the database and Redis the task broker.

Apache Tika and Gotenberg are internal services. Both bind to the loopback interface only and are reachable exclusively by the Paperless workers on the same VM. They have no authentication of their own, so they are deliberately never exposed: nginx on port 80, serving the Paperless web interface, is the only public listener on the machine.

Paperless administrator and PostgreSQL credentials are generated on the first boot of every deployed VM, so two VMs launched from this image never share passwords. A fresh Django secret key is generated at the same time. Credentials are written to /root/paperless-ngx-gotenberg-tika-credentials.txt with mode 0600 so only root can read them. No documents and no shared credentials ship in the image.

The Paperless application code, media archive, data directory, consume folder and search index live under /opt/paperless on a dedicated Azure data disk, separate from the operating system disk. The PostgreSQL data directory sits on its own data disk at /var/lib/postgresql. Both are captured into the image, re-provisioned with every VM, and can be resized independently.

What is included:

  • Paperless-ngx 3.0.5 installed from the official release tarball into /opt/paperless
  • Apache Tika Server 3.3.2 on OpenJDK 17, bound to loopback 127.0.0.1:9998
  • Gotenberg 8.34.0 with LibreOffice 24.2, bound to loopback 127.0.0.1:3000
  • Office ingestion pre-wired and verified: DOC, DOCX, XLS, XLSX, PPT, PPTX, PPSX, ODT, ODS, ODP, ODG and RTF
  • granian application server on loopback :8000, nginx reverse proxy on :80
  • PostgreSQL on a dedicated 20 GiB data disk at /var/lib/postgresql
  • Application, media archive, consume folder and search index on a dedicated 60 GiB data disk at /opt/paperless
  • Redis task broker and the full tesseract OCR pipeline for scans and images
  • Per-VM administrator, database and secret-key credentials generated at first boot, in a root-only file
  • Nine systemd units enabled and active
  • 24/7 cloudimg support

Prerequisites

  • An Azure subscription with permission to create virtual machines.
  • An SSH key pair for the azureuser account.
  • A network security group allowing inbound TCP 22 (SSH) and TCP 80 (HTTP) from your address.
  • Standard_B2ms (2 vCPU, 8 GiB) is the recommended size and the minimum we support. This image runs Paperless, a Java service and a LibreOffice conversion engine together. On a 4 GiB VM they contend for memory during conversion; on Standard_B2ms the whole stack idles at roughly 1 GiB and leaves over 6 GiB free.

Step 1: Deploy from the Azure Marketplace

In the Azure portal choose Create a resource, search for Paperless-ngx with Tika and Gotenberg by cloudimg, and select Create. Pick your resource group and region, choose the Standard_B2ms size, select SSH public key for the authentication type with the username azureuser, and allow inbound ports 22 and 80. Review and create.

Step 2: Deploy from the Azure CLI

az group create --name paperless-rg --location eastus

az vm create \
  --resource-group paperless-rg \
  --name paperless-vm \
  --image cloudimg:paperless-ngx-gotenberg-tika:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group paperless-rg --name paperless-vm --port 80 --priority 1001

# Get the public IP address
az vm list-ip-addresses --resource-group paperless-rg --name paperless-vm \
  --query "[0].virtualMachine.network.publicIpAddresses[0].ipAddress" -o tsv

Step 3: Connect and Retrieve Initial Credentials

First boot generates the per-VM credentials. Allow about a minute after the VM reports running, then connect:

ssh azureuser@<vm-ip>

Read the credentials file. It is mode 0600 and owned by root, so sudo is required:

sudo cat /root/paperless-ngx-gotenberg-tika-credentials.txt

You will see the per-VM administrator password, the PostgreSQL password and the URL for this VM:

paperless.url=http://<vm-ip>/
paperless.admin.user=admin
paperless.admin.pass=<PAPERLESS_ADMIN_PASSWORD>
paperless.db.name=paperless
paperless.db.user=paperless
paperless.db.pass=<generated per VM>

These values are unique to this VM. Store them in your password manager and treat the file as sensitive.

Step 4: Confirm the Services Are Running

Nine systemd units make up this image. All nine should report active:

systemctl is-active postgresql redis-server tika gotenberg nginx \
  paperless-webserver paperless-task-queue paperless-scheduler paperless-consumer

All nine services active

Each line returns active. In order these are the database, the task broker, the Apache Tika text extraction service, the Gotenberg conversion service, the web front end, the Paperless application server, the Celery worker, the Celery scheduler and the directory consumer.

Step 5: Check the Office Ingestion Services

Apache Tika and Gotenberg are what make Office documents work. Confirm both are healthy:

curl -s http://127.0.0.1:9998/version; echo
curl -s http://127.0.0.1:3000/health; echo

Tika and Gotenberg health

Tika reports Apache Tika 3.3.2. Gotenberg returns a JSON health document reporting "status":"up" and, nested under details, the LibreOffice engine also up. LibreOffice is the engine Gotenberg uses to turn Office files into PDF.

Step 6: The Internal Service Model

Both companion services are bound to the loopback interface and are not reachable from outside the VM. Confirm it:

ss -ltn | grep -E ':(80|9998|3000)[[:space:]]'

Loopback binding

Port 80 listens on 0.0.0.0 because nginx serves the Paperless web interface to your users. Ports 3000 and 9998 listen only on 127.0.0.1, so Gotenberg and Tika can be reached by the Paperless workers on this VM and by nothing else.

This matters. Apache Tika Server and Gotenberg both ship without any authentication of their own, and an exposed document conversion endpoint is a well known server side request forgery and denial of service risk. Keeping them on loopback is the security model of this image. Do not publish ports 9998 or 3000 in your network security group, and do not change the bind address in /etc/systemd/system/tika.service or /etc/systemd/system/gotenberg.service.

Step 7: First Login to the Paperless Web Interface

Browse to http://<vm-ip>/. You will be redirected to the sign-in page.

Paperless sign-in page

Sign in as admin with the password from Step 3. You arrive at the dashboard.

Paperless dashboard

Change the administrator password once you are in, from the user menu in the top right.

Step 8: Import Your First Office Document

This is what the image is for. Copy any Word or Excel file into the consume directory and Paperless will pick it up automatically. From your workstation:

scp quarterly-report.docx azureuser@<vm-ip>:/tmp/
sudo install -o paperless -g paperless -m 0644 /tmp/quarterly-report.docx /opt/paperless/consume/

No Office file to hand? Create a real .docx on the VM and ingest that instead — this is a genuine Word document, not a renamed text file, so it exercises the same Tika and Gotenberg path your own documents will take:

python3 - <<'DOCX'
import zipfile
t = "Quarterly report for the cloudimg paperless archive. Revenue is up and filing is automatic."
ct = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
      '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
      '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
      '<Default Extension="xml" ContentType="application/xml"/>'
      '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
      '</Types>')
rels = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
        '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>'
        '</Relationships>')
doc = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
       '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
       f'<w:body><w:p><w:r><w:t>{t}</w:t></w:r></w:p></w:body></w:document>')
with zipfile.ZipFile("/tmp/quarterly-report.docx", "w", zipfile.ZIP_DEFLATED) as z:
    z.writestr("[Content_Types].xml", ct); z.writestr("_rels/.rels", rels)
    z.writestr("word/document.xml", doc)
print("created /tmp/quarterly-report.docx")
DOCX
sudo install -o paperless -g paperless -m 0644 /tmp/quarterly-report.docx /opt/paperless/consume/
echo "queued for ingestion"

The consumer notices the file within a few seconds. Behind the scenes Paperless sends it to Apache Tika for the text and metadata, and to Gotenberg for a PDF rendition, then indexes the result. A first conversion takes a little longer because LibreOffice starts up; later ones are quicker.

Watch it happen:

sudo journalctl -u paperless-task-queue -n 20 --no-pager

Refresh the Documents view in the browser and your Office files are listed with rendered previews:

Office documents ingested

Open a document and select the Content tab. The text on the left is what Apache Tika extracted from the original Office file; the page on the right is the PDF that Gotenberg rendered from it. Because the text is indexed, the document is now full-text searchable.

Extracted text and rendered PDF

You can confirm the same from the command line:

sudo -u paperless env -C /opt/paperless/src \
  PAPERLESS_CONFIGURATION_PATH=/opt/paperless/paperless.conf \
  /opt/paperless/.venv/bin/python manage.py shell -c "
from documents.models import Document as D
from pathlib import Path
for d in D.objects.all().order_by('id'):
    ap = Path(d.archive_path) if d.archive_path else None
    print(f'id={d.id}  {d.original_filename}  text={len(d.content or \"\")} chars  archive={ap.name if ap else \"-\"}')
"

Ingested Office documents

Each row shows the original Office filename, how many characters of text Tika extracted, and the PDF that Gotenberg produced.

Step 9: Supported Formats

Through Tika and Gotenberg this image ingests:

Format Extensions
Word .doc, .docx
Excel .xls, .xlsx
PowerPoint .ppt, .pptx, .ppsx
OpenDocument .odt, .ods, .odp, .odg
Rich text .rtf

Paperless's own pipeline continues to handle PDFs, and scans and photographs are OCRed with tesseract as normal.

Email files (.eml) are not ingested by this image. Paperless renders emails to PDF using a headless browser engine, and no redistributable browser build is available for Ubuntu 24.04, so that route is not included. Email files placed in the consume directory are ignored rather than producing a failed task. Everything in the table above is fully supported.

Step 10: The REST API

Paperless exposes a REST API that uses the same credentials.

# The sign-in page is served without authentication
curl -s -o /dev/null -w "login page: %{http_code}\n" http://127.0.0.1/accounts/login/

# Without credentials the API is rejected
curl -s -o /dev/null -w "unauthenticated: %{http_code}\n" http://127.0.0.1/api/documents/

# With the admin credentials it returns 200
curl -s -o /dev/null -w "authenticated: %{http_code}\n" \
  -u "admin:<PAPERLESS_ADMIN_PASSWORD>" http://127.0.0.1/api/documents/

The login page returns 200, the unauthenticated API call returns 403, and the authenticated call returns 200.

Step 11: Services and Operations

# Status of a single component
systemctl status tika --no-pager | head -5

# Restart the whole application tier
sudo systemctl restart paperless-webserver paperless-task-queue paperless-scheduler paperless-consumer

# Logs
sudo journalctl -u paperless-consumer -n 30 --no-pager
sudo journalctl -u gotenberg -n 30 --no-pager

Tuning for a larger VM: the image is sized for Standard_B2ms, so the Celery worker runs a single task worker and Apache Tika's heap is capped at 1 GiB. If you move to a larger size, raise PAPERLESS_TASK_WORKERS in /opt/paperless/paperless.conf and -Xmx in /etc/default/tika, then restart the affected services.

Step 12: The Dedicated Data Disks

df -h /opt/paperless /var/lib/postgresql

/opt/paperless holds the application, the media archive, the consume folder and the search index. /var/lib/postgresql holds the database. Both are separate Azure managed disks captured into the image, so they are re-provisioned on every VM and can be grown independently in the portal without touching the OS disk.

Step 13: Enable HTTPS with Let's Encrypt

The image serves plain HTTP so it works immediately on a bare IP address. For production, put a DNS name in front of it and add a certificate:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d paperless.your-domain.com

Certbot edits the nginx site in place and installs a renewal timer. Afterwards, add your hostname to PAPERLESS_CSRF_TRUSTED_ORIGINS in /opt/paperless/paperless.conf and restart paperless-webserver.

Step 14: Backups and Maintenance

A complete backup is the PostgreSQL database plus the media archive:

sudo -u postgres pg_dump paperless | gzip > /var/backups/paperless-$(date +%F).sql.gz
sudo tar czf /var/backups/paperless-media-$(date +%F).tar.gz -C /opt/paperless media

Paperless also ships a document exporter that writes documents and metadata together in a portable form; see the upstream documentation for document_exporter.

Operating system security updates are applied by unattended-upgrades, which is enabled in this image.

Scaling Beyond a Single VM

This image is a single-VM appliance: Paperless, PostgreSQL, Redis, Tika and Gotenberg all run together, which suits teams and departmental archives well. To grow beyond one VM, move PostgreSQL to Azure Database for PostgreSQL and Redis to Azure Cache for Redis by editing the connection settings in /opt/paperless/paperless.conf. Because Tika and Gotenberg are plain HTTP services, they can also be moved onto their own hosts and pointed at with PAPERLESS_TIKA_ENDPOINT and PAPERLESS_TIKA_GOTENBERG_ENDPOINT — keep them on a private network, as neither has authentication.

Support

This image is maintained by cloudimg with 24/7 support. Contact support@cloudimg.co.uk with the Azure region, the VM size and the output of systemctl is-active for the affected service.