How to run Tesseract OCR in a cloud sandbox
Install tesseract-ocr and a language pack in a microVM, run tesseract or ocrmypdf on the scan, and read back text, TSV or a searchable PDF.
On Runtime, OCR runs next to the documents and nowhere near your servers. Scans arrive from customers, email and crawlers; every sandbox is a Firecracker microVM with its own kernel, so the image decoders that read them work on a machine holding only that batch. OCR keeps CPUs busy, and Runtime bills what it uses: a 2 vCPU, 4 GiB sandbox costs $0.08 an hour at full load, $0.03125 an hour idle (pricing). Ubuntu 24.04 shipped Tesseract 5.3.4 and OCRmyPDF 15.2.0 on 25 September 2026.
Versions and packages
| What | Ubuntu 24.04 | Upstream on 25 September 2026 |
|---|---|---|
| Tesseract engine | tesseract-ocr 5.3.4 |
5.5.3 on GitHub |
| Language data | tesseract-ocr-eng, -deu, -fra, ... (1:4.1.0-2) |
tessdata repositories on GitHub |
| Scanned PDF to searchable PDF | ocrmypdf 15.2.0 |
17.12.1 on PyPI |
| Python wrapper | none | pytesseract 0.3.13 on PyPI |
| In-browser or Node, no binary | none | tesseract.js 7.0.0 on npm |
Language packs are named tesseract-ocr- plus Tesseract's three-letter code:
tesseract-ocr-deu for German, tesseract-ocr-fra for French.
Read the text in an image
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ timeoutSeconds: 1800 });await sbx.exec( "sudo apt-get update -qq && sudo apt-get install -y -qq tesseract-ocr tesseract-ocr-eng tesseract-ocr-deu", { check: true, timeoutMs: 600_000 },);await sbx.network.off(); // the scans stay on this machineawait sbx.files.upload("./invoice.png", "/workspace/invoice.png");const text = await sbx.exec(["tesseract", "invoice.png", "-", "-l", "eng+deu", "--psm", "3"], { check: true, timeoutMs: 300_000,});console.log(text.stdout);await sbx.exec(["tesseract", "invoice.png", "invoice", "-l", "eng", "tsv"], { check: true });const tsv = await sbx.files.readText("/workspace/invoice.tsv");const words = tsv .split("\n") .slice(1) .map((line) => line.split("\t")) .filter((cols) => cols.length === 12 && cols[11]!.trim() && Number(cols[10]) > 80);console.log(words.length, "words above 80% confidence");Pythonimport csvimport iofrom withruntime import Sandboxwith Sandbox.create(timeout_seconds=1800) as sbx: sbx.exec("sudo apt-get update -qq && sudo apt-get install -y -qq " "tesseract-ocr tesseract-ocr-eng tesseract-ocr-deu", check=True, timeout_ms=600_000) sbx.network.off() # the scans stay on this machine sbx.files.upload("invoice.png", "/workspace/invoice.png") print(sbx.exec(["tesseract", "invoice.png", "-", "-l", "eng+deu", "--psm", "3"], check=True, timeout_ms=300_000).stdout) sbx.exec(["tesseract", "invoice.png", "invoice", "-l", "eng", "tsv"], check=True) rows = csv.DictReader(io.StringIO(sbx.files.read_text("/workspace/invoice.tsv")), delimiter="\t", quoting=csv.QUOTE_NONE) words = [r for r in rows if r["text"] and r["text"].strip() and float(r["conf"]) > 80] print(len(words), "words above 80% confidence")-as the output base sends the text to stdout; a result keeps up to 64 KiB, so write long documents to a file and read them withfiles.readText.-l eng+deucombines languages for mixed documents.- The
tsvoutput gives every word with its box and confidence, which is what an agent needs to find a total or a date by position. - The commands are arrays, so a file name cannot inject shell syntax.
Output formats
From Tesseract's command-line documentation, read 25 September 2026:
| Output config | File written | Use |
|---|---|---|
| (none) | out.txt |
Plain text |
pdf |
out.pdf |
The image with an invisible, searchable text layer |
hocr |
out.hocr |
HTML with word boxes |
tsv |
out.tsv |
One row per word: position, confidence, text |
--psm sets page segmentation: 3 is automatic, 6 assumes one
uniform block of text.
tesseract --list-langs shows the installed languages.
Make a scanned PDF searchable
Tesseract reads images, not PDFs. OCRmyPDF does the whole job: it renders the pages, runs Tesseract, and writes a PDF with a text layer, plus a plain-text sidecar if you ask.
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ image: "ocr", diskMiB: 6144, timeoutSeconds: 3600 });await sbx.files.upload("./scan.pdf", "/workspace/scan.pdf");await sbx.exec( "ocrmypdf -l eng --deskew --rotate-pages --skip-text --sidecar scan.txt scan.pdf searchable.pdf", { check: true, timeoutMs: 1_800_000 },);await sbx.files.download("/workspace/searchable.pdf", "./searchable.pdf");await sbx.files.download("/workspace/scan.txt", "./scan.txt");The flags are from OCRmyPDF's cookbook: --deskew straightens tilted
pages, --rotate-pages fixes pages scanned sideways, and --skip-text leaves
alone pages that already have text. --force-ocr does the opposite.
For OCRmyPDF 17 instead of Ubuntu's 15, its installation guide says to
install the Ubuntu package for the dependencies, then uv tool install ocrmypdf. uv is already in every sandbox, and it installs to
/workspace/.local/bin, first on PATH.
Start every sandbox with OCR installed
The ocr image the PDF sample uses, as a custom image; add a
tesseract-ocr-<lang> package per language you read:
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "ocr", recipe: { apt: ["tesseract-ocr", "tesseract-ocr-eng", "tesseract-ocr-fra", "ocrmypdf"], pip: ["pytesseract==0.3.13"], },});Pythonfrom withruntime import RuntimeRuntime().images.build(name="ocr", recipe={ "apt": ["tesseract-ocr", "tesseract-ocr-eng", "tesseract-ocr-fra", "ocrmypdf"], "pip": ["pytesseract==0.3.13"],})Building is free and uses no trial hours; the trial stores three images free.
Throughput and limits
- CPU. OCR is CPU-bound. Split a large archive across sandboxes, one batch each: a paid account runs 100 at once to start, the free trial eight.
- Disk. Rendered PDF pages take room. The default 4 GiB disk had about
2.5 GiB free on 24 September 2026; ask for
diskMiB: 6144or more for big PDFs, up to 10 GiB on the trial. - Time. A command's default timeout is 60 seconds. Long PDFs want
timeoutMs, and the sandbox atimeoutSecondslonger than the job.
To feed the text to a model that answers questions about it, see a data analysis agent. To turn Office files into PDFs first, see LibreOffice in a sandbox; to clean up images before OCR, see ImageMagick in a sandbox.
Sources
- Tesseract command-line usage, https://tesseract-ocr.github.io/tessdoc/Command-Line-Usage.html, read 25 September 2026
- Tesseract releases, https://github.com/tesseract-ocr/tesseract/releases/latest, read 25 September 2026
- OCRmyPDF cookbook and installation, https://ocrmypdf.readthedocs.io/en/latest/cookbook.html and https://ocrmypdf.readthedocs.io/en/latest/installation.html, read 25 September 2026
- Ubuntu 24.04 tesseract-ocr, tesseract-ocr-eng and ocrmypdf packages, https://packages.ubuntu.com/noble/, read 25 September 2026
- PyPI ocrmypdf and pytesseract, npm tesseract.js, read 25 September 2026
Facts on this page were checked on 25 September 2026.