Stirling-PDF is an open source web application for working with PDF files on your own server: merge, split, compress, rotate, convert, OCR, watermark, redact and dozens of other operations, all processed locally so documents never leave your infrastructure. It also exposes every tool through a REST API, which makes it easy to automate document workflows. In this tutorial you will run Stirling-PDF on Ubuntu 24.04 with Docker Compose, add OCR languages, test the API, enable login and publish it over HTTPS behind Nginx.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 2 GB of RAM. OCR and office conversions are memory hungry, so 4 GB is more comfortable for regular use.
- A non-root user with
sudoprivileges. - Docker Engine and the Docker Compose plugin installed from Docker's official repository.
- A domain or subdomain (this guide uses
pdf.your_domain) with a DNSArecord pointing toyour_server_ip. - Ports 22, 80 and 443 open in your firewall.
Step 1 - Creating the directory layout
Stirling-PDF reads its configuration, OCR language data and custom files from mounted directories. Create them under /opt/stirling-pdf:
sudo mkdir -p /opt/stirling-pdf/{trainingData,extraConfigs,customFiles,logs,pipeline}
Each directory has a specific role:
| Host directory | Container path | Purpose |
|---|---|---|
trainingData | /usr/share/tessdata | Tesseract OCR language files |
extraConfigs | /configs | settings.yml and the user database |
customFiles | /customFiles | Custom logos, templates and static files |
logs | /logs | Application logs |
pipeline | /pipeline | Saved automation pipelines |
Step 2 - Adding OCR language data
Stirling-PDF uses Tesseract for OCR. Because the trainingData directory is mounted over the container's language data directory, it must contain every language you want to use, including English.
Download the English and Spanish models from the official tesseract-ocr/tessdata repository. Replace spa with any other three-letter code you need, such as deu, fra or por:
cd /opt/stirling-pdf/trainingData
sudo curl -fLO https://github.com/tesseract-ocr/tessdata/raw/main/eng.traineddata
sudo curl -fLO https://github.com/tesseract-ocr/tessdata/raw/main/spa.traineddata
Check that both files downloaded correctly:
ls -lh /opt/stirling-pdf/trainingData
-rw-r--r-- 1 root root 23M Sep 25 11:02 eng.traineddata
-rw-r--r-- 1 root root 38M Sep 25 11:02 spa.traineddata
A file of a few hundred bytes means the download returned an error page instead of the model; delete it and try again.
Step 3 - Running Stirling-PDF with Docker Compose
Create the Compose file:
sudo nano /opt/stirling-pdf/compose.yaml
Add the following service:
services:
stirling-pdf:
image: stirlingtools/stirling-pdf:latest
container_name: stirling-pdf
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
volumes:
- /opt/stirling-pdf/trainingData:/usr/share/tessdata
- /opt/stirling-pdf/extraConfigs:/configs
- /opt/stirling-pdf/customFiles:/customFiles
- /opt/stirling-pdf/logs:/logs
- /opt/stirling-pdf/pipeline:/pipeline
environment:
- DISABLE_ADDITIONAL_FEATURES=false
- LANGS=en_GB
A few notes on this file:
stirlingtools/stirling-pdfis the current official image. Older guides usefrooodle/s-pdf, which is the previous name of the same project.- The port is bound to
127.0.0.1. Ports published by Docker bypass UFW, so this is what keeps the service private until Nginx and TLS are in place. DISABLE_ADDITIONAL_FEATURES=falsekeeps the login and user management features available, which you will enable in Step 5.
Start the container:
cd /opt/stirling-pdf
sudo docker compose up -d
The first start takes a minute while the Java application initializes. Follow the logs until the startup finishes:
sudo docker compose logs -f stirling-pdf
Press Ctrl+C to stop following the logs once you see that the application has started on port 8080. Then query the status endpoint:
curl -s http://127.0.0.1:8080/api/v1/info/status
{"status":"UP","version":"1.3.2"}
The version number will be different on your server. The settings.yml file has also been generated in /opt/stirling-pdf/extraConfigs.
Step 4 - Testing the REST API
Every tool in the web interface has an API endpoint. The complete, interactive list is available in the Swagger UI at /swagger-ui/index.html once the site is published.
Create two one-page test PDFs with Ghostscript, which renders a tiny PostScript program to PDF. You can also skip this and copy any two PDFs to /tmp/a.pdf and /tmp/b.pdf with scp:
sudo apt install ghostscript
printf '%%!PS\n/Helvetica findfont 24 scalefont setfont 72 720 moveto (First document) show showpage\n' | ps2pdf - /tmp/a.pdf
printf '%%!PS\n/Helvetica findfont 24 scalefont setfont 72 720 moveto (Second document) show showpage\n' | ps2pdf - /tmp/b.pdf
Merge them through the API. Each file is sent as a fileInput form field:
curl -s -X POST http://127.0.0.1:8080/api/v1/general/merge-pdfs \
-F "fileInput=@/tmp/a.pdf" \
-F "fileInput=@/tmp/b.pdf" \
-o /tmp/merged.pdf
Confirm that the result is a PDF:
file /tmp/merged.pdf
/tmp/merged.pdf: PDF document, version 1.7
If the file is reported as JSON or text, open it with cat /tmp/merged.pdf to read the error message returned by the API.
Step 5 - Enabling login
By default anyone who can reach Stirling-PDF can use it. Before exposing it to the internet, enable login and set the initial administrator account. Edit the Compose file:
sudo nano /opt/stirling-pdf/compose.yaml
Extend the environment list:
environment:
- DISABLE_ADDITIONAL_FEATURES=false
- LANGS=en_GB
- SECURITY_ENABLELOGIN=true
- SECURITY_INITIALLOGIN_USERNAME=admin
- SECURITY_INITIALLOGIN_PASSWORD=your_strong_password
Replace your_strong_password with a long, unique password. The initial account is only created on the first start with login enabled, so change its password from your account settings right after the first sign-in.
Recreate the container so it picks up the new variables:
cd /opt/stirling-pdf
sudo docker compose up -d
Check that unauthenticated requests are now redirected to the login page:
curl -sI http://127.0.0.1:8080/ | grep -i -E '^(HTTP|location)'
The response should be a 302 redirect whose Location header points to /login.
With login enabled, API requests must be authenticated too. After signing in, open your account settings in the web interface to view your API key and send it in the X-API-KEY header:
curl -s -X POST http://127.0.0.1:8080/api/v1/general/merge-pdfs \
-H "X-API-KEY: your_api_key" \
-F "fileInput=@/tmp/a.pdf" \
-F "fileInput=@/tmp/b.pdf" \
-o /tmp/merged.pdf
Once you have signed in and changed the password, you can remove the SECURITY_INITIALLOGIN_PASSWORD line from the Compose file so the password is not left in plain text.
Step 6 - Publishing Stirling-PDF with Nginx and HTTPS
Install Nginx and Certbot:
sudo apt update
sudo apt install nginx certbot python3-certbot-nginx
Create a server block:
sudo nano /etc/nginx/sites-available/stirling-pdf
Add the following configuration, replacing pdf.your_domain with your domain. The larger body size and timeouts allow big uploads and slow operations such as OCR on long scanned documents:
server {
listen 80;
listen [::]:80;
server_name pdf.your_domain;
client_max_body_size 200M;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/stirling-pdf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Allow web traffic through UFW if it is active, then request a certificate:
sudo ufw allow 'Nginx Full'
sudo certbot --nginx -d pdf.your_domain
Open https://pdf.your_domain in your browser. You should see the Stirling-PDF login page. Sign in with the administrator account, change the password, and try a tool such as Compress or OCR on a sample file.
To OCR a scanned document from the web interface, open the OCR tool, upload the PDF, select the languages that match the document (only the ones you installed in Step 2 are listed) and run it. The result is a PDF with a searchable text layer.
Step 7 - Upgrading Stirling-PDF
Pull the new image and recreate the container. Your settings, users and language files are kept in the mounted directories:
cd /opt/stirling-pdf
sudo docker compose pull
sudo docker compose up -d
curl -s http://127.0.0.1:8080/api/v1/info/status
Troubleshooting
OCR fails with a missing language error. The language file is not in the mounted directory. Check it from inside the container with sudo docker compose exec stirling-pdf ls /usr/share/tessdata, add the missing .traineddata file as shown in Step 2 and restart with sudo docker compose restart stirling-pdf.
Large uploads fail with 413 Request Entity Too Large. Nginx rejected the file. Raise client_max_body_size in the server block that Certbot updated, then run sudo nginx -t && sudo systemctl reload nginx.
Operations on big files end with 504 Gateway Timeout. The operation took longer than the proxy timeout. Increase proxy_read_timeout, and check free memory with free -h and free space with df -h, because the container writes temporary files while processing.
The container restarts in a loop. Read the reason with sudo docker compose logs --tail 50 stirling-pdf. Out-of-memory errors mean the server needs more RAM for the operations you are running.
Conclusion
Stirling-PDF is now running in Docker on Ubuntu 24.04, protected by a login and published over HTTPS, with OCR in the languages you need and a REST API you can call from scripts. As next steps, customize the application name and allowed features in /opt/stirling-pdf/extraConfigs/settings.yml, create separate accounts for your team from the admin settings, and build multi-step pipelines to automate recurring document tasks.
