The headless LibreOffice approach — and its problems
The most common advice for generating .odt files on a server is to install LibreOffice and run it in headless mode:
# The traditional approach
libreoffice --headless --convert-to odt input.html
This works for converting between formats, but it comes with serious operational overhead:
- Massive dependency — LibreOffice is 500+ MB installed. Your Docker image goes from lean to enormous. Build times increase, deployment is slower, and storage costs rise.
- Process management — LibreOffice runs as a separate process. You need to handle startup time, memory limits, timeouts, zombie processes, and concurrent execution. Each conversion spawns a new process or locks a shared instance.
- Scaling difficulties — Under load, LibreOffice headless becomes a bottleneck. It's CPU-intensive, single-threaded per conversion, and difficult to scale horizontally without significant infrastructure.
- No serverless support — LibreOffice doesn't run in AWS Lambda, Cloudflare Workers, Vercel Edge Functions, or any serverless/edge environment. The binary is too large and the execution model is incompatible.
- No browser support — LibreOffice is a native application. It can't run in a web browser. If you want client-side document generation, it's not an option.
- Security surface — Running a full office suite on your server expands your attack surface. LibreOffice has had CVEs, and keeping it patched adds maintenance burden.
The lightweight alternative
If your goal is to create .odt documents — reports, invoices, letters, permits, form fills — you don't need LibreOffice at all. odf-kit generates valid .odt files directly from JavaScript or TypeScript:
| LibreOffice headless | odf-kit | |
|---|---|---|
| Install size | 500+ MB | ~50 KB (npm package) |
| Runtime dependencies | System packages, fonts, Java (optional) | Zero |
| Startup time | Seconds | Instant (library import) |
| Concurrent generation | Limited by process/memory | Limited only by Node.js event loop |
| Serverless / edge | Not possible | Works anywhere JavaScript runs |
| Browser | Not possible | Full support |
| Docker image impact | +500 MB base, slower builds | No impact |
| Format conversion (e.g., .odt → PDF) | Yes | No — generation only |
| Edit existing complex documents | Yes | Template filling only |
When you still need LibreOffice: If you need to convert .odt to PDF, render complex existing documents, or perform format conversion between office formats, LibreOffice headless is still the right tool. odf-kit is for creating new documents and filling templates — the tasks where LibreOffice is overkill.
Quick start
import { OdtDocument } from "odf-kit"; import { writeFileSync } from "fs"; const doc = new OdtDocument(); doc.setMetadata({ title: "Monthly Report" }); doc.setHeader("Confidential — Page ###"); doc.addHeading("Monthly Report", 1); doc.addParagraph("All systems operational."); doc.addTable([ ["System", "Status", "Uptime"], ["API", "OK", "99.95%"], ["Database","OK", "99.99%"], ], { border: "0.5pt solid #000" }); doc.addList([ "No incidents this month", "Scheduled maintenance completed", ], { type: "numbered" }); const bytes = await doc.save(); writeFileSync("report.odt", bytes);
No LibreOffice process. No binary dependency. The file is generated in-process, in milliseconds, and the output opens correctly in LibreOffice, Google Docs, and Microsoft Word.
Fill templates without LibreOffice
If you have an existing .odt template designed in LibreOffice, you can fill it with data programmatically — without needing LibreOffice installed on the server that runs the code:
import { fillTemplate } from "odf-kit"; import { readFileSync, writeFileSync } from "fs"; const template = readFileSync("template.odt"); const result = fillTemplate(template, { name: "Alice", company: "Acme Corp", items: [ { product: "Widget", qty: 5 }, { product: "Gadget", qty: 3 }, ], }); writeFileSync("output.odt", result);
The template uses {placeholder} syntax with support for loops ({#items}...{/items}), conditionals, and dot notation for nested data. odf-kit automatically heals fragmented placeholders that LibreOffice splits across XML elements during editing.
Serverless and edge deployment
Because odf-kit is a pure JavaScript library with zero native dependencies, it runs in environments where LibreOffice simply cannot:
- AWS Lambda — import and call, no layers or custom runtimes needed
- Cloudflare Workers — runs at the edge, no origin server
- Vercel / Netlify Functions — works in any Node.js serverless function
- Deno Deploy — ESM import works out of the box
- Docker — your image stays lean; no
apt-get install libreoffice
For browser usage — where LibreOffice is impossible — see the browser generation guide.
Lean Docker example
Here's how small your Dockerfile stays:
FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --production COPY . . CMD ["node", "generate.mjs"]
Compare this to a Dockerfile that installs LibreOffice — which requires a Debian or Ubuntu base image, apt-get install libreoffice, font packages, and typically pushes your image past 1 GB. With odf-kit, your Alpine-based image stays under 100 MB.
What odf-kit supports
odf-kit generates valid ODF 1.2+ .odt files with: headings (levels 1–6), paragraphs with full text formatting (bold, italic, underline, strikethrough, superscript, subscript, font size, font family, color, highlight), tables with borders, column widths, cell formatting, row/column spans, numbered and bulleted lists with nesting, images (PNG, JPEG), hyperlinks and internal bookmarks, headers and footers with automatic page numbers, page layout (orientation, margins, paper size), tab stops, page breaks, document metadata, and method chaining.
The template engine supports placeholder replacement, loops over arrays, conditionals on truthy/falsy values, dot notation for nested objects, and automatic placeholder healing.
Related guides
For detailed tutorials, see the Node.js generation guide, the browser generation guide, and the template filling guide. For a comparison with the abandoned simple-odf library, see the simple-odf alternative page. For government compliance use cases, see the ODF government compliance guide.