Skip to content
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, then read back the text, TSV or a searchable PDF.

On Runtime, OCR runs next to the documents, not on your servers. Scans arrive from customers, email and crawlers. Every sandbox is a Firecracker microVM with its own kernel. The image decoders that read the scans therefore work on a machine that holds 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 and $0.03125 an hour idle (pricing). Ubuntu 24.04 shipped Tesseract 5.3.4 and OCRmyPDF 15.2.0 on 25 October 2026.

Versions and packages

What Ubuntu 24.04 Upstream on 25 October 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.13.0 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 of output. For long documents, write the text to a file and read it with files.readText.
  • -l eng+deu combines languages for mixed documents.
  • The tsv output gives every word with its box and confidence. An agent needs that to find a total or a date by its position.
  • The commands are arrays, so a file name cannot inject shell syntax.

Output formats

From Tesseract's command-line documentation, read 25 October 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. It also writes a plain-text sidecar if you ask for one.

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, which is 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 for each 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 without credit counts toward its hours; an account without credit 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, an account without credit four.
  • Disk. Rendered PDF pages take room. The default 4 GiB disk had about 2.5 GiB free on 24 September 2026. For big PDFs, ask for diskMiB: 6144 or more, up to 10 GiB without credit.
  • Time. A command's default timeout is 60 seconds. Long PDFs need timeoutMs. Set the sandbox's 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 October 2026.

Related.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card