---
id: 2024-10-09-install-stirling-pdf-on-ubuntu-24-04
slug: install-stirling-pdf-on-ubuntu-24-04
title: "Ubuntu 24.04: install Stirling PDF and run it in production"
excerpt: "Install Stirling PDF on Ubuntu 24.04 LTS with Docker Compose: OCR setup, reverse proxy with TLS, hardening and operational monitoring."
date: "2024-10-09T06:21:24+02:00"
updated: "2026-09-06T10:25:00+02:00"
author:
  name: "Sebastian Palencsár"
  handle: "spalencsar"
category: "server-environments"
tags: ["ubuntu", "docker", "stirling-pdf", "pdf", "ocr", "server-environments", "nginx", "self-hosted"]
reading_time: 18
toc: true
---

PDF documents are among the most sensitive data holdings in companies and public authorities: employment contracts, invoices, tax records and internal reports must never, for data-protection and GDPR reasons, be sent unchecked to external cloud services. Web-based tools such as Smallpdf or Adobe Cloud do solve everyday tasks like merging or redacting pages, but in a business environment they regularly violate compliance rules.

**Stirling PDF** resolves this dilemma as a fully open-source, locally hosted complete solution. The application runs as a lightweight [Docker container](/en/server-environments/install-and-use-docker-on-ubuntu-26-04){.badge-link-text}, processes all documents locally in the RAM of your own server and leaves no persistent file remnants on external platforms after processing is finished.

Stirling PDF is deployed on a server with **Ubuntu 24.04 LTS (Noble Numbat)**: with an integrated OCR engine for German and English text, a hardened Nginx reverse proxy with TLS termination, and reproducible update and backup routines for production.

<blockquote class="infobox infobox--info">
💡 **Note:** A working online demo of the software is provided by the project at [pdf.ipx64.xyz](https://pdf.ipx64.xyz){.badge-link-text}. For your own infrastructure we use the official container distribution.
</blockquote>

## Architecture and components

At its core Stirling PDF is based on a Java application (Spring Boot) and under the hood orchestrates specialized Linux command-line tools. Instead of reinventing the wheel, the container wraps proven open-source libraries:

* **Apache PDFBox and OpenPDF:** Handle low-level PDF operations (split pages, merge, rotate, edit metadata).
* **LibreOffice (headless):** Converts office formats (DOCX, ODT, PPTX, XLSX) losslessly into standards-compliant PDF files.
* **Tesseract OCR and OCRmyPDF:** Search scanned documents, recognize text layers and embed searchable OCR text into existing PDFs.
* **Ghostscript and QPDF:** Optimize file sizes, repair damaged PDF structures and decrypt password-protected files.

```markdown
┌─────────────────────────────────────────────────────────────┐
│              STIRLING PDF ARCHITECTURE MODEL                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Web browser (client)                                      │
│        │                                                    │
│        ▼ HTTPS (port 443)                                   │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ Nginx reverse proxy (TLS termination and SSL)       │   │
│   │ client_max_body_size 100M; proxy_read_timeout 300s; │   │
│   └────┬────────────────────────────────────────────────┘   │
│        │ HTTP (127.0.0.1:8080)                              │
│        ▼                                                    │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ Docker container (stirlingtools/stirling-pdf)       │   │
│   │                                                     │   │
│   │  ┌───────────────┐ ┌───────────────┐ ┌───────────┐  │   │
│   │  │  Spring Boot  │ │  LibreOffice  │ │ Tesseract │  │   │
│   │  │  Web / Auth   │ │  Converter    │ │    OCR    │  │   │
│   │  └───────┬───────┘ └───────┬───────┘ └─────┬─────┘  │   │
│   └──────────┼─────────────────┼───────────────┼────────┘   │
│              ▼                 ▼               ▼            │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ Host filesystem / persistent volumes (/opt/...)     │   │
│   │  ./configs       ./customFiles       ./trainingData │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

The split between reverse proxy on the host and the Stirling container on `127.0.0.1` ensures that unencrypted HTTP traffic never reaches the external network unprotected.

## System requirements and resource planning

Before we start the deployment, we check the hardware resources of the target server. PDF operations with LibreOffice and optical character recognition (OCR) are CPU- and memory-intensive.

**Minimum vs. recommended system resources:**

| Resource | Minimum operation (1–2 users) | Recommended for production and OCR |
| --- | --- | --- |
| **CPU** | 2 vCPUs | 4 vCPUs (speeds up parallel OCR jobs) |
| **Memory** | 2 GB RAM | 4 to 8 GB RAM (Tesseract + JVM) |
| **Disk** | 10 GB free space | 25 GB SSD/NVMe (for temp files and Docker images) |
| **Operating system** | Ubuntu 24.04 LTS (x86_64 or ARM64) | Ubuntu 24.04 LTS (x86_64 or ARM64) |

<blockquote class="infobox infobox--warn">
⚠️ **Watch the OOM killer:** When Stirling PDF OCRs a 200-page PDF or renders complex Word documents through LibreOffice, memory use can briefly rise by 1.5 to 2 GB. On systems with only 2 GB RAM an active swap file is mandatory, otherwise the Linux kernel terminates the container with an out-of-memory signal (`SIGKILL`).
</blockquote>

## Step 1: Update the system and install Docker

Ubuntu 24.04 still ships the old `docker.io` package and the outdated Python version of Docker Compose in its default package sources. For stable and secure operation we use the official repository from Docker Inc. with the modern Compose v2 extension (`docker-compose-plugin`).

**Refresh package sources and install base tools:**

```bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg lsb-release
```

**Add the official Docker GPG key and configure the repository:**

```bash
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

**Install Docker Engine and Docker Compose v2:**

```bash
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

**Validate service status and autostart:**

```bash
sudo systemctl enable --now docker
docker compose version
```

```bash
# Expected output:
Docker Compose version v2.29.x (or newer)
```

## Step 2: Create the directory structure and permissions

We place the container configuration in a structured way under `/opt/stirling-pdf`. All persistent data, language models and custom settings remain isolated in dedicated subdirectories:

```bash
sudo mkdir -p /opt/stirling-pdf/{configs,customFiles,trainingData,pipeline}
cd /opt/stirling-pdf
```

**Meaning of the directories:**

* `configs/`: Holds the application configuration (`settings.yml`), databases for user accounts and API keys.
* `customFiles/`: Allows storing your own logos, CSS customizations or watermark templates.
* `trainingData/`: Storage for Tesseract OCR language packs (`.traineddata`), e.g. German (`deu`) and English (`eng`).
* `pipeline/`: Optional folder for automated processing pipelines (e.g. automatic OCR for dropped scans).

## Step 3: Download OCR language files

By default the Stirling PDF base image ships English language data. To process German scans without errors (including umlauts `ä`, `ö`, `ü` and `ß`), we download the optimized Fast Tessdata model directly into our volume:

```bash
cd /opt/stirling-pdf/trainingData
sudo curl -fsSL -O https://github.com/tesseract-ocr/tessdata_fast/raw/main/deu.traineddata
sudo curl -fsSL -O https://github.com/tesseract-ocr/tessdata_fast/raw/main/eng.traineddata
cd /opt/stirling-pdf
```

<blockquote class="infobox infobox--practice">
❗ **Tip on model quality:** For archival requirements with maximum character precision you can alternatively use the `tessdata_best` repository. The `tessdata_fast` model, however, needs only about a quarter of the RAM and, with standard fonts, delivers nearly identical recognition rates at significantly higher throughput.
</blockquote>

## Step 4: Production-ready Docker Compose configuration

Stirling PDF offers two primary image variants:
1. `stirlingtools/stirling-pdf:latest`: Standard image with the full feature set (LibreOffice, OCR, PDFBox).
2. `stirlingtools/stirling-pdf:latest-fat`: Already contains all worldwide Tesseract language files (requires over 4 GB of disk space on pull).

We use the standard image and bind the required language models in a targeted way through the mounted `trainingData` volume.

Create the file `/opt/stirling-pdf/docker-compose.yml`:

```bash
sudo nano /opt/stirling-pdf/docker-compose.yml
```

Insert the following configuration:

```yaml
services:
  stirling-pdf:
    image: stirlingtools/stirling-pdf:latest
    container_name: stirling-pdf
    restart: unless-stopped
    ports:
      # Bind to localhost only — protection via Nginx reverse proxy
      - "127.0.0.1:8080:8080"
    volumes:
      - ./trainingData:/usr/share/tessdata:rw
      - ./configs:/configs:rw
      - ./customFiles:/customFiles:rw
      - ./pipeline:/pipeline:rw
    environment:
      # User interface and localization
      - SYSTEM_DEFAULTLOCALE=en-US
      - UI_APPNAME=AdminDocs PDF Studio
      # Security and authentication options
      - DOCKER_ENABLE_SECURITY=true
      - SECURITY_ENABLE_LOGIN=true
      - SECURITY_CSRF_DISABLED=false
      # Performance and resource settings
      - INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
      - SYSTEM_MAXFILESIZE=100
    deploy:
      resources:
        limits:
          cpus: '3.00'
          memory: 3500M
        reservations:
          memory: 1024M
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
```

<span class="nb-accent">Explanation of the central parameters:</span>

* `127.0.0.1:8080:8080`: Binds the service exclusively to the local loopback adapter. External requests on port 8080 are dropped immediately and must pass the TLS-secured proxy.
* `DOCKER_ENABLE_SECURITY=true` and `SECURITY_ENABLE_LOGIN=true`: Enables Stirling PDF's built-in role and user system.
* `SYSTEM_MAXFILESIZE=100`: Sets the upload limit for input files to 100 megabytes.
* `limits.memory: 3500M`: Protects the host from uncontrolled memory hunger on faulty or manipulated PDF files.

## Step 5: Start the container and create the initial administrator

We start the container in the background:

```bash
cd /opt/stirling-pdf
sudo docker compose up -d
```

**Follow the start process in the logs:**

```bash
sudo docker compose logs -f stirling-pdf
```

On first start with the security option enabled (`DOCKER_ENABLE_SECURITY=true`), Stirling PDF automatically creates a temporary administrator account. Watch the logs for the following string:

```bash
#################################################################
# Generated Admin Username: admin                               #
# Generated Admin Password: <generated-one-time-password>       #
#################################################################
```

Copy this initial password to the clipboard. We use it in the web interface to set a permanent password immediately.

<blockquote class="infobox infobox--info">
💡 **Note on the initial password:** If the password was missed in the logs, it can always be read in `/opt/stirling-pdf/configs/settings.yml` or regenerated by stopping the container and resetting the configuration.
</blockquote>

## Step 6: Hardening with Nginx reverse proxy and Let's Encrypt

For productive use on the intranet or the internet we attach Stirling PDF through an Nginx reverse proxy with a secure TLS certificate.

**Install Nginx and Certbot:**

```bash
sudo apt install -y nginx certbot python3-certbot-nginx
```

**Create the Nginx configuration for the domain:**

Replace `pdf.example.com` with your desired domain name:

```bash
sudo nano /etc/nginx/sites-available/stirling-pdf.conf
```

Insert the following server block:

```nginx
server {
    listen 80;
    listen [::]:80;
    server_name pdf.example.com;

    # Generous limits for large document uploads
    client_max_body_size 100M;
    client_body_timeout 300s;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;

        # Headers to forward client information
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support for progress displays
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts for compute-heavy OCR and conversion jobs
        proxy_connect_timeout 90s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
    }
}
```

<span class="nb-accent">Why client_max_body_size and timeouts are indispensable:</span>

By default Nginx limits requests to 1 megabyte (`client_max_body_size 1M`). Every upload of a scanned PDF or a large handbook would otherwise be rejected with `413 Request Entity Too Large`. The extended timeouts (`proxy_read_timeout 300s`) prevent connection drops (`504 Gateway Timeout`) when Tesseract analyzes hundreds of pages.

**Enable the site and check the configuration:**

```bash
sudo ln -s /etc/nginx/sites-available/stirling-pdf.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```

**Generate a TLS certificate with Let's Encrypt:**

```bash
sudo certbot --nginx -d pdf.example.com
```

Certbot automatically extends the Nginx configuration with TLSv1.2/TLSv1.3 directives and sets up automatic certificate renewal via a systemd timer.

## Step 7: Configure the firewall with UFW

We make sure that only the secure web ports are reachable from outside:

```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
```

```bash
# Check the status:
sudo ufw status verbose
```

Internal port `8080` is isolated thanks to our Compose binding to `127.0.0.1` and does not appear in the external firewall rules at all.

## Step 8: Verification and system check

After setup we check the operating state of all components on the Linux console.

**1. Check port binding:**

```bash
ss -tulpn | grep 8080
```

```bash
# Expected result (must show 127.0.0.1, never 0.0.0.0):
tcp   LISTEN 0      4096   127.0.0.1:8080       0.0.0.0:*    users:(("docker-proxy",pid=...))
```

**2. Check the Docker healthcheck:**

```bash
docker compose -f /opt/stirling-pdf/docker-compose.yml ps
```

```bash
# Expected result (status "healthy"):
NAME           IMAGE                             COMMAND                  SERVICE        STATUS
stirling-pdf   stirlingtools/stirling-pdf:latest "tini -- ./entrypoin…"   stirling-pdf   Up 5 minutes (healthy)
```

**3. Query API status and Tesseract support:**

```bash
curl -s http://127.0.0.1:8080/api/v1/info/status
```

The JSON response must report status `UP` and list the installed OCR languages.

## Maintenance, updates and backup strategy

Professional operation requires reliable maintenance routines for container updates and configuration backups.

### Performing container updates

Because Stirling PDF is actively developed, monthly updates should be planned:

```bash
cd /opt/stirling-pdf
sudo docker compose pull
sudo docker compose down
sudo docker compose up -d
sudo docker image prune -f
```

### Creating a consistent configuration backup

Because the actual PDF files are deleted from temporary storage immediately after processing, for disaster recovery we only need to back up configuration data and user settings:

```bash
#!/bin/bash
# Backup script for Stirling PDF
BACKUP_DIR="/var/backups/stirling-pdf"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"
tar -czf "$BACKUP_DIR/stirling_config_$DATE.tar.gz" -C /opt/stirling-pdf configs customFiles trainingData

# Clean backups older than 30 days
find "$BACKUP_DIR" -type f -name "*.tar.gz" -mtime +30 -delete
```

This script can be stored as a daily cron job under `/etc/cron.daily/backup-stirling-pdf`.

## Typical errors and troubleshooting

In practice, specific error states can appear during PDF processing:

### 1. Error 413: Request Entity Too Large

* **Symptom:** When uploading a PDF larger than 1 MB, the browser aborts with HTTP 413.
* **Cause:** The Nginx directive `client_max_body_size` is missing or set too low.
* **Fix:** Set `client_max_body_size 100M;` in `/etc/nginx/sites-available/stirling-pdf.conf` and run `sudo systemctl reload nginx`.

### 2. Tesseract does not recognize German umlauts

* **Symptom:** OCR texts contain hieroglyphs or spaces instead of `ä`, `ö`, `ü`.
* **Cause:** The language file `deu.traineddata` was not found in the mounted directory or has wrong file permissions.
* **Diagnosis and fix:**
  ```bash
  ls -lh /opt/stirling-pdf/trainingData/
  sudo chmod 644 /opt/stirling-pdf/trainingData/*.traineddata
  sudo docker compose restart stirling-pdf
  ```

### 3. Container is terminated unexpectedly during OCR processing

* **Symptom:** `docker compose ps` shows `Exited (137)`.
* **Cause:** Exit code 137 means `SIGKILL` by the Linux out-of-memory (OOM) killer.
* **Diagnosis:** Check `dmesg -T | grep -i oom`.
* **Fix:** Raise the memory limit in `docker-compose.yml` (`memory: 4096M`) or set up a 4 GB swap file on the host:
  ```bash
  sudo fallocate -l 4G /swapfile
  sudo chmod 600 /swapfile
  sudo mkswap /swapfile
  sudo swapon /swapfile
  echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
  ```

## Command Reference (Cheatsheet)

The most important operational commands for management and fault diagnosis at a glance:

| Task / command | Function / explanation |
| --- | --- |
| `docker compose -f /opt/stirling-pdf/docker-compose.yml up -d` | Starts the Stirling PDF stack in the background |
| `docker compose -f /opt/stirling-pdf/docker-compose.yml logs -f` | Shows real-time logs of the application and the web server |
| `docker compose -f /opt/stirling-pdf/docker-compose.yml restart` | Restarts the container (e.g. after a configuration change) |
| `docker stats stirling-pdf` | Monitors live CPU and memory use of the container |
| `nginx -t && systemctl reload nginx` | Validates the Nginx reverse-proxy configuration and reloads rules |
| `certbot renew --dry-run` | Simulates automatic renewal of the TLS certificate |
| `ss -tulpn \| grep 8080` | Checks whether port 8080 is listening exclusively on `127.0.0.1` |

## Further Resources

The following documentation provides deeper information on the technologies and components used:

| Resource / documentation | Description |
| --- | --- |
| [Official Stirling PDF documentation](https://docs.stirlingpdf.com){.badge-link-text} | Detailed documentation of all parameters, API endpoints and pipeline functions |
| [Stirling-Tools GitHub repository](https://github.com/Stirling-Tools/Stirling-PDF){.badge-link-text} | Official source code, issue tracker and release notes |
| [Docker Engine documentation for Ubuntu](https://docs.docker.com/engine/install/ubuntu/){.badge-link-text} | Best practices for installing and maintaining Docker on Ubuntu Linux |
| [Tesseract OCR project](https://github.com/tesseract-ocr/tesseract){.badge-link-text} | Background on the text-recognition engine and language models |
| [AdminDocs: Docker and Docker Compose guide](/en/server-environments/how-to-install-docker-and-docker-compose-on-almalinux){.badge-link-text} | Fundamental container orchestration and hardening on Linux |
| [Portainer CE web GUI](/en/linux-beginners/install-portainer-on-ubuntu-24-04-lts){.badge-link-text} | Graphical management of Docker containers and stacks on Ubuntu 24.04 LTS |

## Conclusion

With the setup built here, Ubuntu 24.04 LTS has a full, GDPR-compliant document hub ready. Through strict encapsulation in the Docker container, decoupling via a hardened Nginx reverse proxy and binding to the local loopback adapter, the security architecture stays transparent and low-maintenance.

<blockquote class="infobox infobox--info">
💡 **Practical tip for administrators:** In Stirling PDF under the admin settings, set user quotas and session timeouts. When employees edit confidential documents in the browser, temporary browser caches should be invalidated automatically after the session ends. Also enable the option for automatic metadata cleanup so that hidden author and printer information is reliably removed before PDF documents are passed on.
</blockquote>

That completely removes the need for unsecured third-party web services on the company network, while data sovereignty remains 100 percent on your own server infrastructure.
