What you will set up
To merge two PDFs or shrink a scan, many people upload contracts, ID cards and invoices to free PDF websites, and those sites get a copy of every file. Stirling-PDF does the same work in your browser, on your own server: more than 50 tools, among them merge, split, compress, OCR to make scanned pages searchable, and conversion from Word to PDF.
Here it runs from its official image, in Podman under a user of its own called stirling, behind Caddy from the Podman guide.
Stirling-PDF is developed by Stirling PDF, Inc., a company in San Francisco, United States, with contributions from its community on GitHub. It is open core: most of the code is open source under the MIT licence, while the directories app/proprietary, app/saas, engine and parts of the web interface are under the proprietary Stirling PDF User License. The official images include those parts, and the login and user management used below come from them. Without a paid licence, that licence allows "internal trial, evaluation, or minimal use" within the limits the software sets, which here is five user accounts, and not use in client-facing or commercial contexts. If you will use it in a business, read its terms first.
Out of the box, Stirling-PDF asks the admin at the first login whether to turn on analytics, sent to PostHog's EU servers (eu.i.posthog.com) and to a Scarf tracking pixel, and when an admin opens it, their browser checks supabase.stirling.com, a Stirling service, for updates, sending the version, the edition and whether login is on. This guide switches both off before the first start.
Every step below was run on a fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13:
- Without logging in, the tools answered
401from the internet, and the defaultadminaccount with passwordstirlingnever existed, because the guide creates your own admin instead. - On 2 vCPU, merging a 35 MB file with a scan took 3 seconds, compressing it to 9 MB took 13 seconds, OCR of a scanned one-page Swedish lease took 4 seconds, and of five pages 9 seconds, with å, ä and ö right. A Word file became a PDF in under a second, or 10 seconds when LibreOffice first had to start.
- A 50 MB PDF went through Caddy from outside, and a 120 MB one was refused.
- Over a capture of its network traffic through three restarts, a login and OCR in the browser, Stirling-PDF opened no connections to the internet, and the browser contacted only the server.
- After each operation, the container's temporary directory was empty again, and no record of the file names was kept.
- An update with
podman auto-update, a backup and a restore all worked, and everything started again by itself after a reboot of the server.
Stirling-PDF used about 1 GB of memory when idle, about 1.4 GB during OCR, and 1.7 GB at most while compressing a 35 MB file.
Before you start
You need:
- a server set up as in the Podman guide, with Caddy running;
- about 2.5 GB of free disk space for the image;
- an A record and an AAAA record for
pdf.example.compointing at your server.
The examples use pdf.example.com for Stirling-PDF and anna for your admin account. Replace them throughout.
1. Create the user
As root:
useradd -m -s /bin/bash stirling
loginctl enable-linger stirling
machinectl shell stirling@
Everything up to step 5 runs as stirling.
2. Add Swedish to OCR
The image reads text in English, German, French, Portuguese and Chinese. For Swedish, download Tesseract's Swedish language data into a directory that the container will read:
mkdir -p ~/tessdata
curl -fL -o ~/tessdata/swe.traineddata https://github.com/tesseract-ocr/tessdata_fast/raw/main/swe.traineddata
The file is about 4 MB. Other languages work the same way, with their three-letter code, such as nor, dan or fin. On our test lease, OCR in English turned "Hyresgäst: Åsa Öberg" into "Hyresgast: Asa Oberg", and with Swedish it kept every letter.
3. Choose your admin account
When Stirling-PDF starts for the first time, it creates an admin account named admin with the password stirling, and the API accepts that password straight away, before anyone has changed it. Whoever finds your server before you do could log in. Have it create your own account instead. Create ~/stirling.env, with a long password of your own:
cat > ~/stirling.env <<'EOF'
SECURITY_INITIALLOGIN_USERNAME=anna
SECURITY_INITIALLOGIN_PASSWORD=a-long-password-of-your-own
EOF
chmod 600 ~/stirling.env
Stirling-PDF only reads these lines when the account does not exist yet. Keep the file: if the database is ever lost, it creates your account again from it, instead of admin.
4. Describe the container
mkdir -p ~/.config/containers/systemd
Create ~/.config/containers/systemd/stirling-pdf.container:
[Unit]
Description=Stirling-PDF
[Container]
ContainerName=stirling-pdf
Image=docker.io/stirlingtools/stirling-pdf:latest
Volume=stirling-configs:/configs
# Extra OCR languages, copied into the container at every start
Volume=%h/tessdata:/usr/share/tessdata:ro
# Your admin account, created at the first start
EnvironmentFile=%h/stirling.env
Environment=SECURITY_ENABLELOGIN=true
# No analytics, no update check, no record of your files
Environment=SYSTEM_ENABLEANALYTICS=false
Environment=SYSTEM_SHOWUPDATE=false
Environment=PREMIUM_ENTERPRISEFEATURES_AUDIT_ENABLED=false
Environment=SYSTEM_FILEUPLOADLIMIT=100MB
# Only Caddy, on this server, can reach Stirling-PDF: the port is not open to the internet.
PublishPort=127.0.0.1:8105:8080
AutoUpdate=registry
[Service]
Restart=always
# The first start downloads about 2.3 GB.
TimeoutStartSec=900
[Install]
WantedBy=default.target
What the lines do:
- The
latestimage includes Tesseract and OCRmyPDF for OCR, and LibreOffice for Word, Excel and PowerPoint files. It is 2.34 GB.latest-fat(2.51 GB) adds more fonts and Stirling's AI engine, andlatest-ultra-lite(580 MB) leaves out OCR and LibreOffice, the two things most people install it for. stirling-configsholds the settings and the database with the user accounts. Your PDFs are never stored there.SECURITY_ENABLELOGIN=truerequires a login for the web interface and for the API. It is the default in version 3, and the line makes sure it stays on.SYSTEM_ENABLEANALYTICS=falseswitches off PostHog and Scarf for the whole server, so the analytics question never appears.SYSTEM_SHOWUPDATE=falsestops browsers from checkingsupabase.stirling.comfor updates; the timer in step 9 keeps the image up to date instead.PREMIUM_ENTERPRISEFEATURES_AUDIT_ENABLED=falseswitches off the audit log. It is on by default, without a licence, and keeps a record of every operation for 90 days, with the user, their IP address, and the name and size of every file.SYSTEM_FILEUPLOADLIMIT=100MBrefuses larger uploads in Stirling-PDF itself. Caddy has the same limit in step 5.
Start it:
systemctl --user daemon-reload
systemctl --user start stirling-pdf
systemctl --user enable --now podman-auto-update.timer
The first start downloads the image, then Stirling-PDF needs about 35 seconds before it answers. Check that it is up:
curl -s http://127.0.0.1:8105/api/v1/info/status
It answers {"version":"3.0.2","status":"UP"}.
5. Put Caddy in front
Go back to root with exit, switch to machinectl shell caddy@, and add this block at the end of ~/Caddyfile:
pdf.example.com {
request_body {
max_size 100MB
}
reverse_proxy 127.0.0.1:8105
}
Restart Caddy with systemctl --user restart caddy.
Caddy has no upload limit of its own, so max_size sets one that matches step 4. To allow larger files, raise both.
6. Check that strangers are kept out
From your own computer, try a tool without logging in:
curl -s -o /dev/null -w '%{http_code}\n' -F fileInput=@any.pdf https://pdf.example.com/api/v1/misc/compress-pdf
It answers 401. Logging in with admin and stirling fails too. Without an account, a visitor sees the login page, the API description and the version number, and nothing else.
Open https://pdf.example.com and log in with the account from step 3. Stirling-PDF logs each wrong password with the visitor's real address, which Caddy passes on.
After more than five wrong passwords in a row, an account is locked for two hours, even for the right password. Anyone who knows your username can do that, so choose one that is not easy to guess. Restarting the container lifts the lock.
Without a licence, the server takes five accounts in all. The Invite button at the bottom left opens Users, which shows how many are in use, and Invite people adds more.
7. Use the tools
The tools are listed on the right. Choose one, add files with Add Files, check the settings, and run it with the button at the bottom of the panel. When it is done, Download saves the result.
To OCR a scanned Swedish document:
- Choose OCR / Cleanup scans and add the scan.
- Under Languages, tick Swedish, and untick English if the document is only in Swedish.
- Choose Process OCR and Review, then Download.
The result looks the same as the scan, but you can search it and copy its text. In our test, the paragraph sign § came out as the digit 8, so check numbers and signs in important documents.
To turn a Word file into a PDF, choose Convert, add the file, check that Convert to: is Document (PDF), and choose Convert Files.
These are the times we measured on 2 vCPU, from the server through Caddy:
| Operation | File | Time |
|---|---|---|
| Merge | 35 MB of photos and a 0.5 MB scan | 3 s |
| Split | the merged 35 MB file into three parts | 4 s |
| Compress | 35 MB of photos, to 8.9 MB | 13 s |
| OCR in Swedish | one scanned page | 4 s |
| OCR in Swedish | five scanned pages | 9 s |
| Word to PDF | one page | 0.5 s, or 10 s when LibreOffice starts first |
LibreOffice starts at the first conversion, and stops again after two minutes without one.
8. What stays where
- On the server: each upload is written to a temporary directory in the container while it is processed, and deleted afterwards. In our tests the directory was empty again after every operation, and the container itself is replaced at every restart. The names of Word and other Office files you convert appear in the log,
journalctl --user -u stirling-pdf, but never their contents. - In your browser: the PDF Library on the left keeps your files, the originals and the results, in your browser's own storage on your computer, not on the server. On a shared computer, delete them there when you are done.
9. Updates
The podman-auto-update.timer from step 4 checks once a day for a new latest image, and restarts Stirling-PDF on it. To see whether one is waiting, as stirling:
podman auto-update --dry-run
pending in the UPDATED column means an update is waiting; podman auto-update installs it now. In our test, an update from 3.0.0 to 3.0.2 took a few seconds, and the account and settings were kept. To free the disk space of old images afterwards:
podman image prune -f
10. Back up
Your PDFs are never stored on the server, so there is little to back up: the settings, the user accounts, and the key that encrypts stored credentials. As stirling:
mkdir -p ~/backup
systemctl --user stop stirling-pdf
podman volume export stirling-configs --output ~/backup/stirling-configs.tar
systemctl --user start stirling-pdf
chmod 600 ~/backup/stirling-configs.tar
Copy ~/backup to another machine, and keep it private. To restore it:
systemctl --user stop stirling-pdf
podman volume rm stirling-configs
podman volume create stirling-configs
podman volume import stirling-configs ~/backup/stirling-configs.tar
systemctl --user start stirling-pdf
Troubleshooting
Swedish is not in the Languages list, or OCR turns å, ä and ö into a, a and o. The file from step 2 is missing, or Swedish was not ticked. Check that ~/tessdata/swe.traineddata exists, and restart Stirling-PDF with systemctl --user restart stirling-pdf, as the language files are copied in at start.
A large upload fails, or curl gets 413. The file is larger than Caddy's max_size from step 5. Raise it there, and SYSTEM_FILEUPLOADLIMIT in step 4, then restart both.
The right password gives "Account is locked due to too many failed attempts". Someone, perhaps you, typed a wrong password more than five times. Restart Stirling-PDF to lift the lock at once, or wait two hours.
The first conversion of a Word file takes about 10 seconds. LibreOffice starts on demand, and stops after two minutes without a conversion. The next ones take under a second.
The analytics question appears at login. The SYSTEM_ENABLEANALYTICS=false line is missing from the container file. Add it, run systemctl --user daemon-reload and restart Stirling-PDF.