If you're building a RAG pipeline, a document indexer, or any product that ingests PDFs at scale, you've probably hit the same wall I did. Hosted OCR APIs charge pennies per page that compound into thousands per million. CPU parsers are too slow for production volume. A permanent GPU pod is wasteful when traffic comes in bursts.
MinerU 2.5 produces Markdown and structured JSON from complex PDFs, with published results in the MinerU2.5-Pro-2605-1.2B model card. Its open-source license is based on Apache 2.0 with additional terms. The 1.2B model fits on a 24 GB GPU, and RunPod Serverless scales to zero when nothing is calling.
Real numbers from my open-source mineru-runpod template, measured on a 24 GB RTX 4090 in May 2026: ~$0.001 per page for warm parses, plus a ~$0.03 fixed tax per cold start. The all-in per-page cost depends on how much work you do before the worker scales back to zero. Here's the deploy, the response shape, and the workload patterns this template is the right fit for.
What does it actually cost to run MinerU on RunPod Serverless?
About $0.001 per page on an RTX 4090 once the worker is warm. Each scale-from-zero adds a ~$0.03 fixed tax: roughly 110 seconds of GPU billing for vLLM engine init plus model load. Per-page math depends entirely on amortization. Sparse traffic with one short request per cold start lands closer to $0.005–$0.01 per page.
Real workload-shape math using ADA_24 (RTX 4090, ~$1.10/hr Flex):
| Workload shape | Per-page cost |
|---|---|
| 1,000 pages amortized across one cold start | ~$0.001 |
| 100 pages amortized across one cold start | ~$0.0013 |
| 10 pages then idle out | ~$0.004 |
| One short doc per scale-from-zero (worst case) | ~$0.007 |
Compared to alternatives:
| Tool / setup | Per-page cost | Notes |
|---|---|---|
| Hosted OCR APIs (typical) | $0.001 – $0.01 | vendor lock-in, rate limits, documents leave your stack |
| Permanent GPU pod (24 h on A5000) | $0.001 – $0.003 | 24 h of bills whether you use it or not |
| mineru-runpod, amortized | ~$0.001 – $0.004 | scales to zero; cold-start tax is real |
| Marker / Nougat on CPU | $0 cash, $$$ time | ~30 s/page sequential (Marker docs) |
The trick is RunPod's per-second billing. No worker running, no bill. The catch is every scale-from-zero pays a real fixed cost.
How do I deploy MinerU to RunPod Serverless in ten minutes?
Fork the repo, point RunPod's GitHub auto-build at your fork, create a Serverless Endpoint with ADA_24 (RTX 4090) and FlashBoot enabled, send a request via the included Python client. Total wall-clock from RunPod sign-up to first parse: roughly ten minutes, dominated by the image build (~5–10 min) plus the first cold start (~110 s).
1. Get a RunPod account
Sign up here. At the measured rates below, $5 is roughly 160 full fresh-host startups or 5,000 warm pages before storage and other charges.
2. Fork the repo
gh repo fork sergeyshmakov/mineru-runpod --clone
cd mineru-runpodThe repo stays small: Dockerfile, handler.py, a worker/ package, a Python client (mineru_client), three GitHub Actions workflows, and Hub metadata under .runpod/. The wrapper code is MIT-licensed; the bundled MinerU runtime and model retain their own license terms.
3. Wire RunPod's GitHub auto-build
In the RunPod dashboard:
- Serverless → Templates → New → Import Git Repository
- Point at your fork. Branch
main, Dockerfile pathDockerfile. - RunPod clones, builds the image, stores it in its own registry, and gives you a
template_id. The build runs ~5–10 minutes. Watch the log if you want.
4. Create the endpoint
Dashboard path:
- Serverless → Endpoints → New
- Template: the one you just created
- GPU pool:
ADA_24(RTX 4090, 24 GB) - Workers min:
0, max:3 - Idle timeout:
10seconds - FlashBoot: on
- Save, grab the endpoint id
Or as code (reproducible across redeploys):
pip install -e .[deploy]
python deploy.py --template-id <tid>deploy.py covers endpoint identity, scaling, GPU, disk, and timeout settings. Configure environment variables, secrets, storage, regions, and networking in RunPod.
5. Parse your first PDF
from mineru_client import MineruClient
client = MineruClient(
endpoint_id="<your-endpoint-id>",
api_key="<your-runpod-api-key>",
)
result = client.parse_document(
file_url="https://example.com/report.pdf",
end_page=4, # smoke test on first 5 pages
)
client.save_tarball(result, "./out/doc")
# → ./out/doc/<basename>.md
# → ./out/doc/<basename>_content_list.json
# → ./out/doc/<basename>_middle.json
# → ./out/doc/images/*.pngFirst parse pays a cold start. Subsequent parses on the same warm worker run at ~1–6 s/page on the 4090, content density dependent. After 10 s of idle, the worker scales to zero.
What does the MinerU response actually contain?
Three structured outputs plus extracted images. <basename>.md is Markdown with LaTeX equations, HTML tables, and image references. <basename>_content_list.json is a flat list of typed entries (text, list, equation, table, image, code) usually tagged with page_idx. <basename>_middle.json carries the intermediate layout. Pick the output mode with transport: tarball_b64, inline, or s3.
For a document indexer or RAG pipeline, content_list.json is the file you'll spend the most time with. Text blocks can carry text_level, but headings still need document-specific validation. Store page_idx for citation back to the source; the API reference lists the common block fields.
The Markdown is for human-readable display. middle.json has bounding boxes per span when you need page coordinates for hover-to-source UI.
Transport options on the request: tarball_b64 (default) for outputs under ~20 MB, inline if you want the markdown directly in the JSON response, s3 for anything that would exceed RunPod's response cap. See the R2 bridge post for the s3 setup.
When does mineru-runpod fit your workload, and when doesn't it?
Good fit: batch ingest jobs, bursty traffic (50 docs in a minute, then quiet), background pipelines, OCR-API replacement. Poor fit: interactive single-document apps (cold starts make users think it's broken), sparse traffic (one job per cold start dominates the bill), strict latency SLOs without provisioning workers_min ≥ 1.
I run this template for a document indexer. Here is the fit I have seen in production:
Good fit:
- Batch ingest. Drop 500 PDFs into a queue. One cold start amortizes across the whole batch at ~$0.001 per page.
- Bursty traffic. A user uploads 50 documents in a minute. One cold start, 49 warm parses.
- Background pipelines. Nightly cron processes yesterday's intake. Cold start cost is rounding error against a multi-hour batch.
- OCR-API replacement. Comparable per-page cost without sending documents through a separate parsing SaaS; RunPod and any configured bucket remain infrastructure vendors.
Poor fit:
- Interactive single-document parsing. Your user uploads one PDF and waits two minutes for the cold start. They'll think it's broken.
- Sparse traffic (one job every 20–60 min). Almost every request is a cold start. The ~$0.03 cold-start tax dominates. Rent a permanent low-tier GPU pod and skip serverless instead.
- Strict latency SLOs. Cold-start latency is partly outside your control. Provisioning
workers_min ≥ 1eliminates cold starts but you pay for the warm worker around the clock.
The repo's defaults (workers_min=0, idle_timeout=10s) are tuned for batch-with-bursts. The dashboard's scaling settings are where you tune for other patterns.
What's the real cold-start cost on RunPod Serverless?
Roughly 110 seconds before MinerU starts parsing your first request after a scale-from-zero. The composition: ~3 s fitness checks, ~20 s vLLM engine config, ~20 s model load, ~25 s torch.compile, ~5 s CUDA graph capture, ~5 s of actual parse. Billed at ~$1.10/hr on the 4090 default, that's roughly $0.03 per cold start.
The per-phase breakdown is documented in the troubleshooting guide if you want to see where the time goes. The boot-time warmup in this template loads MinerU's model and JIT-compiles vLLM kernels before the worker accepts requests. When RunPod's FlashBoot snapshot is available on a subsequent scale-from-zero, the wall-clock drops to ~7–8 seconds because the snapshot captured a warm process. When the snapshot isn't available (new host, image rebuild), warmup re-runs and you pay the full ~110 s again.
The FlashBoot mechanism investigation covers when the fast path applies, with measured numbers across multiple consecutive cold starts.
What should I watch out for before going to production?
Three production gotchas the marketing won't mention. The 20 MB response cap silently drops large outputs (symptom: NoneType after a successful parse — covered by the R2 bridge). execution_timeout defaults to 900 s and won't cover full books. Inline base64 input is constrained by RunPod's whole-request limits. None of these crash the worker; they manifest as confusing client-side errors.
- 20 MB response cap. RunPod's
/runsyncgateway drops responses over ~20 MB. Multi-page parses with embedded images hit this around 50–80 pages. Worker logsdone; client getsNoneType. Fix:transport: "s3"+ Cloudflare R2, walked through in the R2 bridge post. - Long-job timeout. Repo defaults
execution_timeout=900s(good for ~150–300 pages on 4090). A 5,000-page book is 80–500 minutes depending on content density. Bumpexecution_timeoutfor long jobs; the endpoint upper limit is 24 hours. - Inline payload cap on the way in. RunPod caps the whole request at 10 MB on
/runand 20 MB on/runsync; base64 leaves roughly 7.5 MB and 15 MB for raw file bytes. For larger files, pass a short-lived signedfile_urlfrom your storage. - Cold-start economics. "Pennies per page" depends on amortization. Track average pages per cold start in your logs. If it's under 30, bump
idle_timeoutor runworkers_min=1.
Where to next
The repo ships with:
- Typed Python client (
MineruClient) deploy.py/destroy.pyfor endpoint lifecycle automation- Reference adapter pattern for wrapping MinerU output into domain models
- Unit tests and CI on every PR
- Commitlint + semantic-release for automated CHANGELOG / GitHub Releases
For the deeper context that didn't fit:
- How RunPod FlashBoot actually works — four-request investigation into the cold-start mechanism and the per-host snapshot caveat.
- The R2 bridge for the 20 MB response cap — fix for
NoneTypeon multi-page outputs. - Choosing a GPU — when 24 GB is enough, when to opt up to 48 GB.
If this saved you time, the easiest way to say thanks is signing up for RunPod through this link. Star the repo on GitHub for updates.
FAQ
How does mineru-runpod compare to hosted PDF APIs?
Per-page cost is in the same ballpark ($0.001–$0.004) when amortizing cold starts across reasonable batches. The differences are control and lock-in. You deploy the endpoint in your RunPod account, pick the GPU and concurrency, and choose the MinerU version. The trade-off is operating a serverless template and evaluating RunPod plus your storage provider instead of consuming one managed parsing API.
Can MinerU 2.5 handle non-English PDFs?
Yes. The vlm-auto-engine default backend is tagged for English and Chinese in the current model card. For other scripts, MinerU 3.4's pipeline backend uses script-family OCR models such as east_slavic, cyrillic, arabic, devanagari, and korean. The Pro VLM also handled Cyrillic in my test even though lang is ignored on the VLM path; that result does not establish support for every script. Switch backends per request with the backend field.
What's the difference between vlm-auto-engine, pipeline, and hybrid-auto-engine?
vlm-auto-engine uses MinerU's 1.2B VLM via vLLM. It measured ~1–6 s/page warm on the tested English, Chinese, and Cyrillic documents. pipeline uses OCR plus dedicated layout, formula, and table models; it is slower in these measurements (~3–5 s/page) but more memory-predictable and exposes script-family language choices. hybrid-auto-engine combines the two paths. Test quality and peak VRAM on representative documents before changing GPU size.
Does the per-page cost include the cold-start tax?
No. The ~$0.001 per page is warm-worker math. Each fresh-host start adds roughly $0.03 on the 4090 default. Your effective per-page cost is ((0.001 × pages) + (0.03 × fresh_starts)) / pages. For 100 pages across one fresh start, that's $0.0013 per page. For 10 pages, it's $0.004.
Can I use mineru-runpod with my own MinerU model?
Yes, but changing the cached download alone is not enough. A fork must configure MinerU to select the model, bake the matching files into HF_HOME, and validate the model against the pinned MinerU/vLLM/CUDA stack before redeploying. Follow the upstream model-selection path for the MinerU version you pin.
What GPU does the template default to?
ADA_24 (RTX 4090, 24 GB). Switched from AMPERE_24 (A5000) on 2026-05-26 after measuring per-page cost. The 4090 is 2–4× faster per page than the A5000 and cheaper per page despite the higher hourly rate. See Choosing a GPU for the full math and when to opt up to 48 GB.
How do I keep my RunPod endpoint warm to avoid cold starts?
Set workers_min=1 on the endpoint. You pay for the always-on worker around the clock (~$0.000306/s on the 4090 default, so ~$26/day or ~$800/month). Worth it if your traffic is steady enough that the warm worker stays busy, or if your latency SLO can't tolerate the cold-start window. For bursty traffic, workers_min=0 with FlashBoot enabled is usually cheaper.
Disclosure: RunPod links in this post use a referral code that credits me at no cost to you. The post would read the same without it.