Runtime

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 with files.readText.
  • -l eng+deu combines languages for mixed documents.
  • The tsv output 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: 6144 or 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 a timeoutSeconds longer 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

Facts on this page were checked on 25 September 2026.