BuildNot yet confirmed elsewhere1 publisher2 min readPublished
Stirling-PDF does not crash. Your 1 GB container limit becomes a 256 MB heap
A catalogue of 15 self-hosting anti-patterns puts nearly every failure outside the application: default heap sizing, the wrong image variant, a 60-second proxy, OCR with no language pack.
The Engineer · Build desk
What happened
- A dev.to catalogue of 15 anti-patterns argues Stirling-PDF itself is stable, and that nearly every crash, hang or mangled output comes from configuration around it.
- Docker sets no memory limit by default, and the JVM inside the container takes 25 percent of visible RAM as its heap when no -Xmx is given.
- Three unrelated failures arrive as the same bug report, and the container exit code distinguishes only two of them.
- A request that never returns, then a 504 at roughly 60 seconds, is the reverse proxy giving up while the job is still running.
- A fourth mode returns HTTP 200 with vanished text or substituted fonts, and the log records it as a success.
Why it matters
- constraint Every ceiling that keeps the app from killing its host also guarantees some legitimate large job never finishes, so the limit is a policy about which jobs you refuse, not a safety setting.
- exposure Anything calling the API unattended is the party most likely to take the host down, since the app queues nothing and holds each accepted request in memory until it finishes.
- decision On a box already carrying twenty containers, the memory limit and temp volume stop being tuning and become the price of keeping the other services alive.
- cost The operator pays for misdiagnosis in time, roughly a weekend, because the memory, timeout and settings faults need opposite fixes.
Two ceilings sit inside each other, and only one of them appears in your compose file. The recommendation for a merge-and-split homelab box is the ultra-lite image behind a hard 1 GB container limit [16]. A container-aware JVM with no `-Xmx` set claims a quarter of the RAM it can see [3], which leaves a 256 MB heap [14] inside the gigabyte you thought you were buying. That produces the failure the dev.to write-up says operators skip past, because from outside nothing looks broken: a `java.lang.OutOfMemoryError` stack trace in the log, the web UI still answering, and exactly one job dead [6].
The two-command triage is worth running before touching any configuration, because the exit code separates the host kill from the in-process heap error [9], and either `OOMKilled: true` or a `dmesg` line confirms the first [10]. Neither command says anything about the other two cases, which is the reason three unrelated problems keep arriving under one bug report.
OCR is where the batching advice turns into arithmetic. The scan-hoarder profile in the source is 40,000 pages, the full image, a real tessdata volume with the languages mounted, and batches of 50 pages or fewer [17]. That is 800 submissions [15]. Since cost tracks page count and DPI rather than file count [17], consolidating the scans into fewer PDFs beforehand buys nothing at all.
Office conversion punishes the purchase most operators reach for first. Conversions serialise through a single background LibreOffice process, so effective concurrency is one no matter how many cores you add, and the ordering the source gives is proxy timeout first, CPU later [19]. Every job also writes scratch files to disk before it returns a single byte [4], which is how a working instance fills a volume nobody sized [c5b].
Then there is the variant trap, which costs nothing in memory and still breaks the job: choose a lighter image, then call a tool that image does not contain [12]. Nothing in the heap, the proxy or the cgroup explains that one, and no ceiling you set protects you from it.
What to watch
- Whether upstream images ship an explicit heap ceiling, which would take the invisible second limit out of operators' hands.
- Measured pages-per-minute OCR figures at a fixed DPI, which would turn the 50-page batch rule into a budget rather than a habit.
- Whether a tool missing from a lighter image variant fails loudly enough in the logs to be told apart from a genuine job failure.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence38
- Adoption
- Insufficient
- Hype gap+12
- Incentives32
- Confidence42
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Stirling-PDF is described as a stable application: almost every crash, hang and quietly mangled output traces to one of fifteen decisions made outside the app, such as an unbounded container, the wrong image variant, a reverse proxy that gives up after 60 seconds, or an OCR call with no language pack behind it.
- [3]
A container-aware JVM with no -Xmx set claims a maximum heap of 25 percent of visible RAM.
- [4]
Every Stirling-PDF job writes scratch files to disk before it returns a single byte.
- [5]
Where Stirling-PDF writes its temporary files is why it fills the operator's disk.
- [6]
A java.lang.OutOfMemoryError: Java heap space stack trace with the container still running means the JVM hit its own heap ceiling while the container still had free memory; the web UI usually stays reachable and only that one job fails. The source calls this the fix nobody applies, because the container looks healthy.
- [7]
When the request never returns and no error appears, the job is running but something between browser and app gave up first; a 504 after roughly 60 seconds points at the reverse proxy, not at Stirling-PDF.
- [8]
A fourth failure mode returns HTTP 200 with a file that opens but is wrong: text vanished, fonts substituted, images blurred to unreadable. Nothing in the log marks it as an error because the application considers the job successful.
- [9]
Three different failures get reported as 'Stirling-PDF crashed' and need opposite fixes; the source advises running docker inspect stirling-pdf --format '{{.State.ExitCode}}' and docker logs --tail 200 stirling-pdf before changing anything, since the exit code alone separates two of the three cases.
- [10]
Exit code 137 means the kernel OOM killer reclaimed the process because the container exceeded its cgroup memory limit or the host ran out of RAM; the application log ends mid-sentence with no stack trace, and it is confirmed with dmesg -T | grep -i oom or docker inspect reporting OOMKilled: true.
- [11]
The central tradeoff is headroom against completion: every limit that stops Stirling-PDF from taking down its host also stops some legitimate large job from ever finishing.
- [12]
Anti-patterns 3 and 4 are running the wrong image variant and then calling a tool that variant does not contain.
- [13]
Diagnosing the wrong one of the failure modes costs the operator a weekend.
- [14]
The recommended 1 GB container limit combined with the JVM's 25 percent default leaves roughly a 256 MB maximum heap.
- [15]
A 40,000-page scan archive processed in batches of 50 pages is 800 separate batch submissions.
- [16]
For the weekend homelab tinkerer with one N100 mini PC doing roughly ten PDFs a week of merge and split only, the source recommends the ultra-lite image with a hard 1 GB container limit, because that profile never touches the two memory-consuming subsystems.
- [17]
For a scan hoarder with 40,000 pages to OCR, the source recommends the full image, a real tessdata volume mounted with the required languages, and processing in batches of 50 pages or fewer, because OCR cost scales with page count and DPI rather than file count.
- [18]
An automation builder calling the Stirling-PDF API unattended from n8n or cron should add their own queue and set explicit timeouts on both sides, because the app accepts every concurrent request thrown at it and holds each one in memory.
- [19]
A small team sharing one instance should budget for LibreOffice serialisation and raise proxy timeouts before raising CPU, because the bottleneck is a single background conversion process, not cores.
- [20]
A NAS owner already running twenty other containers should set the memory limit and the temp volume first, because an unbounded Stirling-PDF job is the container most likely to evict other services.
Sources
1 independent publisher whose own reporting we read for this story.
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.