On Runtime the converter image is built once for free, and each conversion pays only for the seconds it runs. A 2 vCPU, 4 GiB sandbox with both CPUs busy costs $0.08 an hour. 5,000 thirty-second conversions come to $1.77 at the rates published on 26 October 2026, as worked out below. The uploaded file is opened in a Firecracker microVM with its own kernel and no network, so a hostile document cannot reach anything of yours.
Why convert in a sandbox
Users upload DOCX, ODT and RTF files, and agents produce Markdown that has to become a Word file or a web page. The converters that do this well, LibreOffice and pandoc, are large programs that parse complex formats written by strangers. Running them on an application server puts that parsing next to your database credentials and your other customers' files.
A sandbox per job, or per batch, moves that work somewhere else: a machine with no network card, no credentials and a time limit that stops it.
Build the converter image
Ubuntu 24.04 packages both tools. libreoffice-writer-nogui is LibreOffice's
word processor without a graphical interface (version 24.2.7 in noble), and
pandoc is 3.1.3 there (Launchpad, read 26 October 2026):
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build( { name: "convert", recipe: { apt: ["libreoffice-writer-nogui", "pandoc"] } }, { onLog: (line) => console.log(line.text) },);Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build( name="convert", recipe={"apt": ["libreoffice-writer-nogui", "pandoc"]}, on_log=lambda line: print(line["text"]),)The build runs in its own Firecracker machine and costs nothing. The stored image is billed on its size at $0.08 per decimal GB per 30-day month, and an account without credit keeps its first three images free (custom images).
Convert one document
--headless runs LibreOffice with no user interface, and --convert-to pdf
writes the result into --outdir (LibreOffice's help, read 26 October 2026).
TypeScriptimport { readFile, writeFile } from "node:fs/promises";import { Runtime } from "withruntime";const runtime = new Runtime();await using sbx = await runtime.sandboxes.create({ image: "convert", network: { internet: false }, timeoutSeconds: 600, onLeaseEnd: "stop",});await sbx.files.write("/workspace/in/contract.docx", await readFile("contract.docx"));const run = await sbx.exec( [ "soffice", "--headless", "--convert-to", "pdf", "--outdir", "/workspace/out", "/workspace/in/contract.docx", ], { timeoutMs: 180_000 },);if (run.exitCode !== 0 || run.timedOut) throw new Error(run.stderr);await writeFile("contract.pdf", await sbx.files.read("/workspace/out/contract.pdf"));Pythonfrom withruntime import Runtimeruntime = Runtime()with runtime.sandboxes.create( image="convert", network={"internet": False}, timeout_seconds=600, on_lease_end="stop",) as sbx: with open("contract.docx", "rb") as source: sbx.files.write("/workspace/in/contract.docx", source.read()) run = sbx.exec( ["soffice", "--headless", "--convert-to", "pdf", "--outdir", "/workspace/out", "/workspace/in/contract.docx"], timeout_ms=180_000, ) if run.exit_code != 0 or run.timed_out: raise RuntimeError(run.stderr) with open("contract.pdf", "wb") as target: target.write(sbx.files.read("/workspace/out/contract.pdf"))The command is an array, so a file name with spaces or quotes reaches LibreOffice as one argument and never passes through a shell. Treat the uploaded name as data: store it under a name you choose, as above.
Convert a folder at once
Starting LibreOffice once for many files is faster than starting it once per file. Upload the folder in one call, convert every file with a glob, and download the results in one call:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ image: "convert", network: { internet: false } });await sbx.files.upload("./incoming", "/workspace/in");await sbx.exec("soffice --headless --convert-to pdf --outdir /workspace/out /workspace/in/*.docx", { timeoutMs: 1_800_000,});console.log(await sbx.files.glob("out/*.pdf"));await sbx.files.download("/workspace/out", "./converted");Directories travel as one compressed archive each way, and uploaded files keep their permissions.
Markdown from an agent to DOCX or HTML
pandoc converts between markup formats. -f and -t name the input and output
formats, and -o names the output file. Its PDF output goes through LaTeX by
default, which needs a LaTeX engine installed. For PDF, it is simpler to make
DOCX with pandoc and pass that file to LibreOffice:
Terminalpandoc -f markdown -t docx -o report.docx report.mdsoffice --headless --convert-to pdf report.docxWhat a conversion service needs
| Need | How Runtime covers it |
|---|---|
| Isolation from hostile files | A Firecracker microVM with its own kernel per sandbox |
| A document that calls out to the internet | network: { internet: false }, enforced outside the guest |
| LibreOffice and pandoc ready at start | A custom image from a recipe, a Dockerfile or any container image |
| A conversion that hangs | timeoutMs on the command; timeoutSeconds ends the sandbox |
| Big files | Chunked, SHA-256 checked uploads with no limit beyond the disk |
| Bursts of uploads | Creates wait for room rather than failing, for up to two minutes |
| Pinning a converter version | image: "convert@3" starts from exactly that build |
What it costs
Take 5,000 documents a month, each converted in its own 2 vCPU, 4 GiB sandbox that runs for 30 seconds with 15 CPU-seconds of work:
- CPU
- 5,000 × 15 s / 3,600 × $0.025
- $0.52
- Memory
- 5,000 × 30 s / 3,600 × 4 GiB × $0.0075
- $1.25
- Total
- $1.77
Converting in batches of 50 per sandbox cuts the memory line further, because LibreOffice starts once per batch. Runtime bills $0.025 per vCPU-hour of measured CPU and $0.0075 per GiB-hour of memory, with no plan fee (pricing). A new account gets 100 free hours of a 1 GB machine in its first 30 days without a card.
Sources
- LibreOffice command line parameters:
--headless,--convert-toand--outdir, read 26 October 2026. - Pandoc user's guide: formats,
-o, and PDF through LaTeX, read 26 October 2026. - Launchpad package pages for libreoffice-writer-nogui and pandoc in Ubuntu 24.04, read 26 October 2026.
Related: PDF processing, run untrusted LLM code safely, turn off a sandbox's internet, Docker vs a virtual machine.
Facts on this page were checked on 26 October 2026.