Stirling-PDF with Docker: a private PDF toolbox (Linux, Windows, Mac)
Before you start
- 21 min read
Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#
Stirling-PDF is an open-source PDF toolbox that runs in your browser and on your own server, so you can merge, split, compress, sign, redact and OCR PDFs without uploading them to a third-party website.
Logo: the official Stirling-PDF project (github.com/Stirling-Tools/Stirling-PDF)
The project has more than 50 PDF tools, a REST API, and user accounts. You run one Docker container, open port 8080 in a browser, and your files stay on your server.
| Current version | 3.2.2, released October 2026 (GitHub release v3.2.2) |
| Time needed | About 30 minutes, most of it the first image download |
| Memory | 4 GB suggested for the standard image; 6 GB for the fat image; 2 GB for ultra-lite (official docs) |
| Ports | 8080 inside the container (you choose the host port) |
| Licence | MIT for most of the code; some directories have their own licence (open-core) |
| Official docs | docs.stirlingpdf.com and github.com/Stirling-Tools/Stirling-PDF |
At a glance:
- What it does: 50+ PDF tools, a web interface in 40+ languages, a REST API, and folder automation.
- Images:
latest(standard),latest-fat(adds the bundled AI engine, off until you turn it on) andlatest-ultra-lite(core tools, no login, no OCR). - Login: on by default. The first admin is
adminwith passwordstirling, and you must change it at the first sign-in. - Privacy: documents stay on your server. Anonymous usage analytics are asked for, and nothing is sent until someone answers. AI is off in every image until you turn it on.
- Platforms: the Docker commands are the same on all three systems; see Before you start for what is tested.
How it fits together#
Your browser talks to one container. The container saves files in folders on your server. Two outside calls exist, and both are off until you allow them.
Drawn by DIY Lab Hub from the official Stirling-PDF docs (read October 2026). The solid arrow is the path your files take. The dashed red arrows are outside calls: anonymous usage counts to PostHog's EU servers, and prompts to an AI provider if you enable AI.
Before you start#
You need a computer or server that runs Docker. Stirling-PDF's standard image is about 1.2 GB to download (Docker Hub size for 3.2.2) and needs about 4 GB of memory for a small team.
| Requirement | Linux | Windows | Mac |
|---|---|---|---|
| Docker | Docker Engine with the Compose plugin | Docker Desktop | Docker Desktop |
| Memory | 4 GB or more | 4 GB or more | 4 GB or more |
| Disk | 10 GB free | 10 GB free | 10 GB free |
| CPU | 2-4 cores (official sizing for 1-10 users) | 2-4 cores | 2-4 cores |
| Support status | Docker is the recommended way (official) | Commands tested by nobody here; Stirling docs do not name Docker Desktop | Commands tested by nobody here; Stirling docs do not name Docker Desktop |
Check that Docker works. Expected output differs by version; the important part is that both commands print a version:
docker --version docker compose version
docker --version docker compose version
Checklist before you install:
- [ ] Docker and Docker Compose v2 are installed (
docker compose versionprints a version) - [ ] You have at least 10 GB free and 4 GB of memory for the container
- [ ] Port 8080 is free on the host, or you have picked another host port
- [ ] You have chosen a folder for the data,
~/docker_volumes/stirling-pdf(Windows:$HOME\docker_volumes\stirling-pdf) - [ ] For the HTTPS section only: a domain name pointing at your server, and ports 80 and 443 open
Tested on: not tested by DIY Lab Hub. Every command and number in this guide comes from the official Stirling-PDF docs, the GitHub release and repository, Docker Hub, and the Caddy and Traefik docs, read October 2026.
Step 1: Create the data folders#
Stirling-PDF keeps its settings, logs, uploads and OCR languages in folders on your host. Create them once, next to a compose file. Using the same folder for everything makes backups simple.
Linux#
mkdir -p ~/docker_volumes/stirling-pdf/{configs,logs,customFiles,pipeline,storage,tessdata}
cd ~/docker_volumes/stirling-pdf
ls
Windows (PowerShell)#
$base = "$HOME\docker_volumes\stirling-pdf" New-Item -ItemType Directory -Force -Path "$base\configs","$base\logs","$base\customFiles","$base\pipeline","$base\storage","$base\tessdata" | Out-Null Set-Location $base Get-ChildItem
Mac (Terminal)#
Same commands as Linux:
mkdir -p ~/docker_volumes/stirling-pdf/{configs,logs,customFiles,pipeline,storage,tessdata}
cd ~/docker_volumes/stirling-pdf
ls
Check it worked. The folder list shows configs, logs, customFiles, pipeline, storage and tessdata. Each one is needed: the docs say /configs and /storage must always be mounted.
Tip: If your user ID on Linux is not 1000, the container's files will belong to the wrong user. The fix is in Common mistakes below; check with
id -uandid -gnow so you know the numbers.
Step 2: Write the compose file#
This is the docs' own example with three changes: the pinned tag 3.2.2, the data folders under ~/docker_volumes/stirling-pdf, and analytics switched off. The file has no version: line, because Compose ignores it now.
The compose file is the same text on every system. Create it in the folder from Step 1.
Linux#
cat > docker-compose.yml <<'EOF'
services:
stirling-pdf:
image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:3.2.2
container_name: stirling-pdf
ports:
- "8080:8080"
volumes:
- ./configs:/configs
- ./logs:/logs
- ./customFiles:/customFiles
- ./pipeline:/pipeline
- ./storage:/storage
- ./tessdata:/usr/share/tessdata
environment:
SYSTEM_ENABLEANALYTICS: "false"
mem_limit: 4g
restart: unless-stopped
EOF
cat docker-compose.yml
Windows (PowerShell)#
@'
services:
stirling-pdf:
image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:3.2.2
container_name: stirling-pdf
ports:
- "8080:8080"
volumes:
- ./configs:/configs
- ./logs:/logs
- ./customFiles:/customFiles
- ./pipeline:/pipeline
- ./storage:/storage
- ./tessdata:/usr/share/tessdata
environment:
SYSTEM_ENABLEANALYTICS: "false"
mem_limit: 4g
restart: unless-stopped
'@ | Set-Content -Path docker-compose.yml -Encoding utf8NoBOM
Get-Content docker-compose.yml
Mac (Terminal)#
Same commands as Linux:
cat > docker-compose.yml <<'EOF'
services:
stirling-pdf:
image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:3.2.2
container_name: stirling-pdf
ports:
- "8080:8080"
volumes:
- ./configs:/configs
- ./logs:/logs
- ./customFiles:/customFiles
- ./pipeline:/pipeline
- ./storage:/storage
- ./tessdata:/usr/share/tessdata
environment:
SYSTEM_ENABLEANALYTICS: "false"
mem_limit: 4g
restart: unless-stopped
EOF
cat docker-compose.yml
Check it worked. The file prints back with the image line ending in :3.2.2. Docker Hub lists that tag for this release, so the pull will not fail on the tag.
Tip: Keep
:3.2.2in the file. Changing it to:latestor to a tag without a version number is the most common reason a compose file stops working later (see Common mistakes).
Step 3: Start the container and check it answers#
The first start downloads the image and creates the settings files in configs. The first start can take a few minutes.
Linux#
docker compose up -d docker compose ps curl -fsS http://localhost:8080/api/v1/info/status
Windows (PowerShell)#
docker compose up -d docker compose ps Invoke-WebRequest -UseBasicParsing http://localhost:8080/api/v1/info/status | Select-Object -ExpandProperty Content
Mac (Terminal)#
Same commands as Linux:
docker compose up -d docker compose ps curl -fsS http://localhost:8080/api/v1/info/status
Check it worked. docker compose ps shows the stirling-pdf container with state running. The status call returns a short text that contains UP. The official docs do not print the full response; the repo's own healthcheck looks for UP in it. If the container stops, read the logs in Troubleshooting.
Step 4: Sign in for the first time and change the password#
Stirling-PDF creates an admin account on the first start when the user database is empty. The docs say the account is admin with password stirling. You must set a new password at the first sign-in, at least 8 characters long.
Linux#
- Open
http://localhost:8080in a browser. - Sign in with username
adminand passwordstirling. - Set a new password of at least 8 characters and save it in your password manager.
- When asked whether to send anonymous usage data, choose no. The compose file already switches analytics off, so the question should not appear.
Windows (PowerShell)#
Same steps as Linux: open http://localhost:8080 in your browser, sign in as admin with stirling, and set a new password.
Mac (Terminal)#
Same steps as Linux: open http://localhost:8080 in your browser, sign in as admin with stirling, and set a new password.
Check it worked. The page shows the tool list after you sign in with the new password. Sign out and sign in again once to be sure the new password works.
Tip: The default password is public knowledge. Change it before you give the address to anyone, even on your home network. The docs also let you create your own first admin before the first start with
SECURITY_INITIALLOGIN_USERNAMEandSECURITY_INITIALLOGIN_PASSWORD, but those are only used while the database is empty.
Step 5: Add OCR languages (optional)#
The standard image includes English, German, French, Portuguese and Simplified Chinese. For other languages, put the Tesseract language file in the tessdata folder. The compose file mounts that folder at /usr/share/tessdata.
Linux#
- Download the
.traineddatafile for your language from thetesseract-ocr/tessdatarepository on GitHub (the docs name this repository; choosetessdatafor more accuracy ortessdata_fastfor speed). - Move the file into
~/docker_volumes/stirling-pdf/tessdata/. - Restart the container:
docker compose restart stirling-pdf.
Windows (PowerShell)#
- Download the
.traineddatafile from the same GitHub repository. - Move it into
$HOME\docker_volumes\stirling-pdf\tessdata\. - Restart the container:
docker compose restart stirling-pdf.
Mac (Terminal)#
Same steps as Linux, with the file in ~/docker_volumes/stirling-pdf/tessdata/:
docker compose restart stirling-pdf
Check it worked. In the app, open Settings, then Server, then Advanced. The Processing card lists the installed languages. The docs say languages added this way through the app are lost when the container is recreated, so use the folder for the ones you keep.
Put it behind HTTPS (with or without Traefik)#
Stirling-PDF works on your home network over plain HTTP. For a public domain, or for a shared server, put a reverse proxy in front and serve it over HTTPS. Choose one route: Traefik if you already run it, or Caddy if you do not. Local-only users can skip this whole section.
Before either route, set the public address so invite emails and share links point at your domain. The official production checklist asks for this (SYSTEM_BACKENDURL and SYSTEM_FRONTENDURL), and forward the proxy headers as the docs describe.
With Traefik#
Use this if you already run Traefik with the Docker provider on an external network called proxy. The labels below are Traefik's own Docker labels from its reference page. The entrypoint and certificate resolver names are set in your Traefik configuration; change them if yours differ. Replace pdf.example.com with your domain.
Create the network if it is missing:
docker network inspect proxy >/dev/null 2>&1 || docker network create proxy
docker network inspect proxy *> $null; if ($LASTEXITCODE -ne 0) { docker network create proxy }
Replace the stirling-pdf service in docker-compose.yml with this, and keep the external network at the bottom:
services:
stirling-pdf:
image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:3.2.2
container_name: stirling-pdf
volumes:
- ./configs:/configs
- ./logs:/logs
- ./customFiles:/customFiles
- ./pipeline:/pipeline
- ./storage:/storage
- ./tessdata:/usr/share/tessdata
environment:
SYSTEM_ENABLEANALYTICS: "false"
SYSTEM_BACKENDURL: "https://pdf.example.com"
SYSTEM_FRONTENDURL: "https://pdf.example.com"
mem_limit: 4g
restart: unless-stopped
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.stirling-pdf.rule=Host(`pdf.example.com`)"
- "traefik.http.routers.stirling-pdf.entrypoints=websecure"
- "traefik.http.routers.stirling-pdf.tls=true"
- "traefik.http.routers.stirling-pdf.tls.certresolver=letsencrypt"
- "traefik.http.services.stirling-pdf.loadbalancer.server.port=8080"
networks:
proxy:
external: true
Start it with docker compose up -d in the same folder on each system.
Check it worked. docker compose ps shows the container running, and https://pdf.example.com opens the sign-in page with a valid certificate. The port 8080 mapping was removed on purpose, so only Traefik reaches the app.
Without Traefik: Caddy#
Caddy gets a certificate for your domain automatically from Let's Encrypt, as long as the name points at your server and ports 80 and 443 reach it. It runs as a second container in the same compose file, so it can reach Stirling-PDF by its service name. Inside Docker, localhost means the Caddy container itself, so do not write localhost:8080 in the Caddyfile.
Replace the file's services section with the one below. The stirling-pdf service no longer publishes port 8080 to your host; only Caddy publishes 80 and 443.
services:
stirling-pdf:
image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:3.2.2
container_name: stirling-pdf
volumes:
- ./configs:/configs
- ./logs:/logs
- ./customFiles:/customFiles
- ./pipeline:/pipeline
- ./storage:/storage
- ./tessdata:/usr/share/tessdata
environment:
SYSTEM_ENABLEANALYTICS: "false"
SYSTEM_BACKENDURL: "https://pdf.example.com"
SYSTEM_FRONTENDURL: "https://pdf.example.com"
mem_limit: 4g
restart: unless-stopped
caddy:
image: caddy:2.11.7
container_name: caddy
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./caddy/conf:/etc/caddy
- ./caddy/data:/data
- ./caddy/config:/config
restart: unless-stopped
Create the Caddyfile in caddy/conf/. It is a plain text file with one site block, and the domain must be your own:
pdf.example.com {
reverse_proxy stirling-pdf:8080
}
Then start both containers. Caddy's own docs describe this layout, and a reload after edits is docker compose exec -w /etc/caddy caddy caddy reload.
Linux#
mkdir -p caddy/conf caddy/data caddy/config nano caddy/conf/Caddyfile docker compose up -d
Windows (PowerShell)#
New-Item -ItemType Directory -Force -Path caddy\conf, caddy\data, caddy\config | Out-Null notepad caddy\conf\Caddyfile docker compose up -d
Mac (Terminal)#
Same commands as Linux:
mkdir -p caddy/conf caddy/data caddy/config nano caddy/conf/Caddyfile docker compose up -d
Check it worked. Test the certificate from outside your network or from your own machine:
curl -I https://pdf.example.com
Invoke-WebRequest -UseBasicParsing -Method Head https://pdf.example.com | Select-Object StatusCode
The official docs do not show the exact response code for this app. A pass means the TLS handshake works with a certificate for your domain, and you get an HTTP answer from Stirling-PDF rather than a certificate error.
Home only, no public domain: Caddy's local certificate#
If the server is only for your home network, you have no public name for Let's Encrypt to check. Use a name that only your network uses, such as pdf.home.arpa (a name reserved for home networks), and tell Caddy to make its own local certificate:
pdf.home.arpa {
tls internal
reverse_proxy stirling-pdf:8080
}
tls internal makes Caddy act as its own certificate authority. Your browser does not trust that authority yet, so you must install Caddy's root certificate on each machine that will use the app. Copy it out of the Caddy container and add it to the trust store. The commands below are the ones in the Caddy docs.
Linux#
docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt /usr/local/share/ca-certificates/root.crt && sudo update-ca-certificates
Windows (PowerShell)#
Run PowerShell as administrator, then:
docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt "$env:TEMP\root.crt" certutil -addstore -f "ROOT" "$env:TEMP\root.crt"
Mac (Terminal)#
docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt /tmp/root.crt && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/root.crt
Firefox and Chrome can keep their own certificate stores. If the warning stays, import root.crt in the browser's certificate settings (the Caddy docs describe these menus).
Check it worked. Open https://pdf.home.arpa in a browser. The padlock shows and there is no certificate warning.
Security and testing#
- Ports: only ports 80 and 443 (through Caddy or Traefik) should face the internet. Port 8080 should not be open on your router. Keep login on.
- Login:
security.enableLoginis true by default. The docs warn that with login off, anyone who can reach the server can use it. Do not turn it off on a server others can reach. - Lockout: five failed sign-ins lock an account for 120 minutes (official defaults).
- Secrets: the compose file above contains no keys. If you later add an AI key, keep it in a
.envfile next to the compose file and make it readable only by you:chmod 600 .envon Linux and Mac. On Windows, use the file's Security tab to limit access to your user. - What is stored:
configsholds settings, generated keys and the built-in database.storageholds the files users save. Protect both. - Status test:
docker compose psshould showrunning. - Functionality test: sign in, open a PDF, merge two files or compress one, and download the result. This is a real end-to-end check; the docs do not print expected output for it.
- SSL test: with HTTPS in place,
curl -I https://pdf.example.comshould return an HTTP response without a certificate error.
Privacy: what leaves your machine#
Stirling-PDF is built to keep documents local, but several features can send data outside. This list uses the official docs.
| Feature | Default | What is sent | Turn it off |
|---|---|---|---|
| Your PDFs and uploads | Stay on your server (/storage, processed in the container) | Nothing, unless you use a feature that calls an outside service | Not needed |
| Anonymous analytics | Asked of the first admin; nothing sent until answered. This file sets it to off | Anonymous server and usage counts (PostHog EU). Document contents are never sent | SYSTEM_ENABLEANALYTICS: "false" (already set) |
| Browser analytics | Only when analytics are on and a user accepts | Tool usage counts, page views, errors; page paths to a pixel | Turned off by the setting above |
| Stripe checkout | Only when an admin opens a plan purchase | Payment data goes to Stripe, not documents | Do not open the checkout |
| AI features | Off in every image | Prompts, relevant document text and file names to the model provider you set; with Stirling Cloud AI, page text only | Keep AI off unless you need it |
Common mistakes#
Each row comes from a real report on the project's issue tracker or from the official docs' own warnings. The last row is about mistakes AI assistants make with this tool.
| Mistake | Why it happens | Do this instead |
|---|---|---|
Using an old or stale image tag such as :fat or :ultra-lite without a version | The old example compose files used those tags, and the issue reports they no longer resolve (#7542). Tags also change over time | Pin a version that Docker Hub lists, such as 3.2.2 or 3.2.2-fat, and check the list at hub.docker.com/r/stirlingtools/stirling-pdf |
Running latest with automatic updates | latest moves with every release. One user on automatic nightly updates found the default admin login back after an update (#5771) | Pin the version in the compose file and upgrade on purpose, after a backup of configs |
Changing settings in the app without the /configs folder mounted | Without the volume, settings and the admin account live inside the container and are lost when it is recreated | Mount ./configs:/configs (the docs say always mount it) and check the folder has files after the first start |
Leaving the default admin / stirling login in place | The password is printed in the public docs | Change it at the first sign-in (Step 4) before anyone else can reach the port |
| Using a host folder owned by another user, then the container cannot write | The container runs as UID and GID 1000. Other owners need PUID and PGID (#5509 reports startup trouble with changed user settings) | Set PUID and PGID to your numbers from id -u and id -g in the compose file's environment: section, then docker compose up -d |
| Uploads fail or download an HTML page behind nginx | nginx allows only 1 MB per request by default (official FAQ) | Set client_max_body_size 2000M; and the long proxy timeouts from the docs' nginx example |
Setting the OCR language in settings.yml on Windows and expecting it to appear | The language list is read from the tessdata folder. One Windows user reported the Japanese language still did not appear after setting tessdataDir (#7690) | Use the tessdata folder mounted at /usr/share/tessdata (Step 5) and check Settings, then Server, then Advanced |
| Leaving the public address unset behind a proxy | Invite emails, share links and the sign-in redirect use the wrong address. OAuth redirect URL problems are still open (#5661) | Set SYSTEM_FRONTENDURL and SYSTEM_BACKENDURL to your HTTPS address |
AI assistants suggesting the old image name, a version: line, or an invented default login | Older posts and the repo's old README badge still show frooodle/s-pdf and older compose files. The docs now list stirlingtools/stirling-pdf with pinned tags and a login that is set on the first start | Copy the image name and tag from this guide or the Docker page of the docs, and remove any version: line; check the login on the First login section of the docs |
Tip: Run
docker compose configafter every edit. It prints the file as Compose reads it and shows YAML mistakes before you restart.
Tip: Start with the standard image. The fat image is larger and needs 6 GB; the ultra-lite image has no login and no OCR, so it is the wrong choice for a shared server.
Tip: Change one thing at a time when you add HTTPS. If the page fails, you know which change caused it.
Troubleshooting#
| Problem | Solution |
|---|---|
| Container stops or does not start | Read the logs: docker compose logs stirling-pdf --tail 100. The docs say the first error usually names the cause |
| Page does not load on port 8080 | Check docker compose ps says running, and the host port in the compose file is free |
| Settings or the admin password revert after a restart | /configs is not mounted. Add ./configs:/configs and restart |
| Login locked after failed attempts | Wait 120 minutes (the default reset time), or ask another admin to reset the password |
| Uploads fail with 413 or an HTML page appears | The reverse proxy limit is too small. Raise client_max_body_size (nginx) or check the proxy in front |
| OCR language is missing from the list | Put the .traineddata file in tessdata and restart the container |
| Container uses a lot of memory when idle | Set a memory limit (mem_limit: 4g). The docs say the Java heap is sized from that limit |
| Tools time out often | The server is short of CPU or memory; see Performance and resources |
| HTTPS shows a certificate warning | For a home name, install Caddy's root certificate (see the local certificate steps); for a public name, check ports 80 and 443 reach Caddy |
Known bugs#
These were open on the issue tracker on the day this guide was checked, October 2026:
- #8446 "Upload to server" sends an empty body on WebKit browsers. On a Mac, try another browser for uploads if this happens.
- #8330 v3.0.2 JAR hung on one Proxmox container while v3.0.1 worked. This was the JAR, not the Docker image; it is a reason to keep the old version until you test a new one.
- #8572 viewer navigation is slow on large vector PDFs in version 3.2.0.
- #5661 OAuth redirect URL problems.
- #8522 the Ollama AI engine can fail with "Exceeded maximum output retries (6)".
Updates and backups#
Update on purpose. Read the release notes on GitHub first. Then change the tag in docker-compose.yml to the new version, back up configs (see below), and run:
docker compose pull docker compose up -d
docker compose pull docker compose up -d
If you run a different tag family (for example you move from 3.2.2 to 3.2.3), the same two commands apply. The official upgrade docs for V1 servers say to back up before a major upgrade.
Back up the folders. Your data is in configs (settings, keys and the built-in database), storage (files), customFiles and, for HTTPS, caddy/data (certificates). The docs also say the built-in database writes nightly backups to configs/backup/db/; copy those off the server too.
Linux and Mac: backup script#
mkdir -p ~/docker_volumes/stirling-pdf/backups cd ~/docker_volumes/stirling-pdf tar -czf "backups/stirling-pdf-$(date +%F).tar.gz" configs storage customFiles caddy/data 2>/dev/null ls -lh backups
Schedule it every night at 03:00 with cron (crontab -e):
0 3 * * * cd ~/docker_volumes/stirling-pdf && tar -czf "backups/stirling-pdf-$(date +\%F).tar.gz" configs storage customFiles caddy/data 2>/dev/null
Windows (PowerShell): backup script#
$base = "$HOME\docker_volumes\stirling-pdf"
New-Item -ItemType Directory -Force -Path "$base\backups" | Out-Null
$items = @("$base\configs", "$base\storage", "$base\customFiles")
if (Test-Path "$base\caddy\data") { $items += "$base\caddy\data" }
Compress-Archive -Path $items -DestinationPath "$base\backups\stirling-pdf-$(Get-Date -Format yyyy-MM-dd).zip" -Force
Schedule it with Task Scheduler (run the script at 03:00 daily; the Start in folder is your stirling-pdf folder).
Restore test. Once a month, unpack the newest archive into an empty folder, and check that configs holds settings.yml and the database files. The restore itself is then a stop, a copy back, and a start.
Performance and resources#
The docs give sizing by number of users:
| Users | CPU | Memory | Disk |
|---|---|---|---|
| 1-10 | 2-4 cores | 4 GB | 10 GB |
| 10-50 | 4-8 cores | 8-16 GB | 50 GB, SSD recommended |
| 50+ | 8+ cores | 16 GB or more | 100 GB+, SSD |
- Memory limit: the docs say to set a limit on the container, because the Java heap is sized from it (up to 70 percent). Without a limit it sizes from the host's total memory.
- Tuning: if the container stops under heavy load, the docs suggest
JAVA_CUSTOM_OPTS: "-XX:MaxRAMPercentage=50"to leave room for LibreOffice and Tesseract. - Monitor:
docker stats stirling-pdfshows live memory and CPU use. The logs are inlogs/ordocker compose logs.
FAQ#
Does Stirling-PDF need a GPU?
No GPU is mentioned anywhere in the official docs. The sizing is in CPU cores and memory.
Do my files leave the server?
Your PDFs stay on the server, in the container and in the storage folder. Anonymous usage analytics, Stripe checkout and AI calls are the only outside traffic the docs describe, and each one is optional or off by default. The Privacy section lists them.
Can I run it without a login?
Yes, with SECURITY_ENABLELOGIN=false, but anyone who can reach the server can then use it. The docs say so plainly. Keep login on for anything reachable from another computer.
Is it free to use?
The free plan covers up to 5 users. Team and Enterprise add seats and features such as external databases and SAML single sign-on. Most of the code is MIT-licensed; some parts are separately licensed (open-core), so check the LICENSE file in the repository for your use.
Sources#
- Stirling-PDF docs: Docker install, configuration, sign-in and security, production checklist, file storage, performance, process limits, OCR languages, analytics, AI security, plans, upgrading, FAQ - read October 2026
- Stirling-PDF repository and release v3.2.2 (LICENSE and README at the tag) and Docker Hub tag list - read October 2026
- Caddy docs (running with Docker, reverse_proxy, tls, automatic HTTPS) and Traefik Docker provider labels - read October 2026
- GitHub issues #7542, #5771, #5509, #7690, #5661, #8330, #8446, #8572, #8522 - read October 2026
Checked against the official docs on 2026-10-30.