Parameterisable Docker build for LiteParse — the local, open-source document parser by LlamaIndex.
A single Dockerfile produces multiple image flavours via build arguments, ranging from a minimal PDF-only container to a full multi-format parser with pre-baked OCR models, a REST API, and an optional high-accuracy OCR sidecar.
Contributing? See CONTRIBUTING.md | Security issue? See SECURITY.md | Code of Conduct: Contributor Covenant v2.1
- Flavours
- Quick start
- Usage without volume mounts
- Build arguments
- Makefile targets
- docker-compose profiles
- Multi-arch builds
- REST API server
- API documentation
- EasyOCR sidecar
- Testing
- CI/CD
- Architecture decisions
- Credits
| Tag | Pre-baked OCR | LibreOffice | ImageMagick | Approx. size |
|---|---|---|---|---|
liteparse:base |
— (CDN on first use) | — | — | ~350 MB |
liteparse:ocr |
✓ configurable langs | — | — | +~20 MB/lang |
liteparse:full |
✓ configurable langs | ✓ | ✓ | ~2 GB |
liteparse:api |
✓ configurable langs | ✓ | ✓ | ~2 GB |
liteparse-ocr-sidecar |
EasyOCR (Python) | — | — | ~3 GB |
base — PDF parsing only. Tesseract.js language models are downloaded from jsDelivr CDN on first use. Requires internet at parse time if OCR is needed.
ocr — Same as base, but Tesseract.js .traineddata files are baked into the image at build time. Works fully offline after pulling. Languages are chosen at build time via TESSERACT_LANGS.
full — Adds LibreOffice (for .docx, .xlsx, .pptx, .odt, …) and ImageMagick (for .jpg, .png, .gif, .tiff, …) on top of the ocr flavour. All formats that LiteParse supports work out of the box.
api — Identical image to full, but started with a Fastify HTTP server that wraps the LiteParse Node.js API. Exposes POST /parse, POST /batch-parse, and GET /health on port 3000.
liteparse-ocr-sidecar — Standalone EasyOCR Python server. Implements LiteParse's external OCR server protocol. Use it alongside any of the above images for higher accuracy OCR compared to bundled Tesseract.js.
# Build the base image
make base
# Parse a PDF
docker run --rm -v "$PWD:/data" liteparse:base parse /data/report.pdf
# Parse and get JSON output
docker run --rm -v "$PWD:/data" liteparse:base parse /data/report.pdf --format json
# Screenshot page 1
docker run --rm -v "$PWD:/data" liteparse:base screenshot /data/report.pdf --target-pages "1" -o /data/screenshotsLiteParse supports reading from stdin via -, and writes parsed output to stdout. This means you can pipe files directly into the container and capture the results — no -v volume mount needed.
# Redirect a file into the container via stdin
docker run --rm -i liteparse:base parse --no-ocr - < report.pdf
# Same thing using cat + pipe
cat report.pdf | docker run --rm -i liteparse:base parse --no-ocr -Note: The
-iflag (--interactive) is required to keep stdin open.
docker run --rm -i liteparse:base parse --format json --no-ocr - < report.pdf > report.json# Parse and extract text items with jq
docker run --rm -i liteparse:base parse --format json --no-ocr - < report.pdf 2>/dev/null \
| jq '.pages[].textItems[].text'
# Parse a remote PDF without saving it locally
curl -sL https://example.com/report.pdf \
| docker run --rm -i liteparse:base parse --no-ocr -Tip: LiteParse writes processing logs to stderr. Add
2>/dev/nullto suppress them when piping stdout into other tools.
Stdin piping works for single-file parsing with text or JSON output. You still need -v mounts for:
batch-parse— reads from and writes to directoriesscreenshot— writes image files to an output directory- Processing multiple files in a loop (though you can pipe each one individually)
To use lit as if it were installed locally:
# Add to your ~/.bashrc or ~/.zshrc
lit() {
if [ -t 0 ] && [ -n "$1" ] && [ -f "$1" ]; then
# File argument provided — pipe it via stdin automatically
local file="$1"; shift
docker run --rm -i liteparse:base parse "$@" - < "$file"
elif [ ! -t 0 ]; then
# Data is being piped in
docker run --rm -i liteparse:base "$@"
else
# No file, no pipe — pass through (e.g. --help, --version)
docker run --rm liteparse:base "$@"
fi
}Then use it like a local command:
lit report.pdf --format json --no-ocr
lit --version
cat scan.pdf | lit parse --format json -All arguments have defaults and can be combined freely.
| Argument | Default | Description |
|---|---|---|
NODE_VERSION |
22 |
Node.js major version |
LITEPARSE_VERSION |
latest |
@llamaindex/liteparse npm version tag |
TESSERACT_LANGS |
"" |
Space- or comma-separated ISO 639-3 language codes to pre-bake. E.g. "eng fra deu jpn chi_sim" |
INCLUDE_LIBREOFFICE |
false |
Install LibreOffice for Office-document conversion |
INCLUDE_IMAGEMAGICK |
false |
Install ImageMagick + Ghostscript for image-file conversion |
# base
docker build -t liteparse:base .
# ocr – English + French pre-baked
docker build \
--build-arg TESSERACT_LANGS="eng fra" \
-t liteparse:ocr .
# full – all formats, English OCR pre-baked
docker build \
--build-arg TESSERACT_LANGS="eng" \
--build-arg INCLUDE_LIBREOFFICE=true \
--build-arg INCLUDE_IMAGEMAGICK=true \
-t liteparse:full .
# Pin a specific liteparse version on Node 20
docker build \
--build-arg NODE_VERSION=20 \
--build-arg LITEPARSE_VERSION=1.3.1 \
-t liteparse:1.3.1-base .The image entrypoint is lit. Pass subcommands and flags directly:
# Parse
docker run --rm -v "$PWD:/data" liteparse:full parse /data/invoice.docx --format json
# Batch parse a directory
docker run --rm -v "$PWD:/data" liteparse:full batch-parse /data/input /data/output
# Disable OCR (faster, text-native PDFs only)
docker run --rm -v "$PWD:/data" liteparse:base parse /data/report.pdf --no-ocr
# Use a specific OCR language
docker run --rm -v "$PWD:/data" liteparse:ocr parse /data/doc.pdf --ocr-language fra
# Point to an external OCR server
docker run --rm -v "$PWD:/data" \
liteparse:full parse /data/scan.pdf \
--ocr-server-url http://localhost:8080make base Build base image
make ocr LANGS="eng fra jpn" Build ocr image (specify languages)
make full LANGS="eng" Build full image
make api LANGS="eng" Build api image (alias of full + retag)
make ocr-sidecar Build the EasyOCR sidecar image
make all Build base + ocr + full + api
make run-base FILE=report.pdf docker run liteparse:base parse /data/report.pdf
make run-ocr FILE=scan.pdf LANG=fra
make run-full FILE=invoice.docx
make run-api docker compose --profile api up
make run-ocr-sidecar docker compose --profile ocr-sidecar up
make multi-arch REGISTRY=ghcr.io/yourorg/ buildx push amd64+arm64
make clean Remove local liteparse images
make clean-volumes Remove EasyOCR model volume
make help List all targets
Overridable variables: NODE_VERSION, VERSION, LANGS, REGISTRY, DOCUMENTS_DIR, API_PORT, OCR_PORT.
# Build and run a specific flavour
docker compose --profile base build
docker compose --profile ocr build
docker compose --profile full build
docker compose --profile api build
docker compose --profile api up # starts REST API on :3000
docker compose --profile ocr-sidecar up # starts EasyOCR on :8080Environment variables (set in shell or .env):
| Variable | Default | Description |
|---|---|---|
TESSERACT_LANGS |
eng |
Languages for ocr/full/api images |
LITEPARSE_VERSION |
latest |
npm version tag |
NODE_VERSION |
22 |
Node.js version |
REGISTRY |
(empty) | Image registry prefix |
DOCUMENTS_DIR |
$PWD |
Host directory mounted at /data |
API_PORT |
3000 |
Host port for the REST API |
OCR_SIDECAR_PORT |
8080 |
Host port for the EasyOCR sidecar |
OCR_SERVER_URL |
(empty) | OCR sidecar URL forwarded to liteparse |
OCR_LANGS |
en |
Languages loaded by EasyOCR sidecar |
CUDA_VISIBLE_DEVICES |
(empty) | GPU index for EasyOCR; CPU if unset |
Images are published for linux/amd64 and linux/arm64 (Apple Silicon, ARM servers) using Docker Buildx.
# One-time setup: create a multi-platform builder
docker buildx create --name liteparse-builder --use --bootstrap
# Build and push all flavours
REGISTRY=ghcr.io/yourorg LANGS="eng fra" make multi-arch
# Build a single target
REGISTRY=ghcr.io/yourorg docker buildx bake full --push
# Verify manifests
docker buildx imagetools inspect ghcr.io/yourorg/liteparse:fullThe docker-bake.hcl file defines all targets. Variables (REGISTRY, NODE_VERSION, LITEPARSE_VERSION, TESSERACT_LANGS) can be overridden via environment or --set.
Note: On Apple Silicon, add
--platform linux/amd64when building single-arch images locally if you need the amd64 variant for deployment.
The api flavour starts a Fastify HTTP server bundled inside the full image. For the full endpoint reference with request/response schemas and client examples, see api-server/README.md. The OpenAPI 3.0 spec is also served at GET /openapi.yaml.
Liveness probe. Returns {"status": "ok"}.
Parse a single document.
Request: multipart/form-data with a file field.
Query parameters:
| Parameter | Default | Description |
|---|---|---|
format |
text |
text or json |
ocrLanguage |
eng |
ISO 639-3 OCR language |
noOcr |
false |
Set true to skip OCR |
targetPages |
(all) | Page range, e.g. 1-5,10 |
dpi |
150 |
Rendering resolution |
ocrServerUrl |
(env) | Override OCR sidecar URL |
Example:
curl -F "file=@report.pdf" "http://localhost:3000/parse?format=json&ocrLanguage=eng"Parse all documents inside a ZIP archive. Returns a ZIP of results.
# Create a zip of documents
zip documents.zip *.pdf *.docx
# Batch parse
curl -F "file=@documents.zip" "http://localhost:3000/batch-parse?format=json" \
-o results.zip# With docker compose
docker compose --profile api up
# Standalone (requires liteparse:full image built)
docker run --rm \
--entrypoint node \
-p 3000:3000 \
-v "$PWD:/data" \
-e OCR_SERVER_URL=http://host.docker.internal:8080 \
liteparse:full /app-src/server.jsFull API reference with request/response examples, error codes, and client code snippets:
api-server/README.md— complete endpoint reference with curl, Python, and JavaScript examplesapi-server/openapi.yaml— OpenAPI 3.0 specification (importable into Swagger UI, Postman, Redoc, etc.)
The spec is also served by the running API at GET /openapi.yaml:
# Fetch the spec from a running container
curl http://localhost:3000/openapi.yaml
# Import into Swagger Editor for interactive exploration
# Open https://editor.swagger.io and paste the URL or YAML contentA Python FastAPI server that provides higher-accuracy OCR compared to bundled Tesseract.js. Supports GPU acceleration and a wide range of languages.
LiteParse's external OCR protocol: POST /ocr with a multipart/form-data image field. Returns a JSON array:
[
{ "text": "Invoice #1042", "bbox": [12, 34, 210, 58], "confidence": 0.9871 }
]# Start the sidecar
docker compose --profile ocr-sidecar up
# Test it
curl -F "image=@page.png" http://localhost:8080/ocr | jq .
# Use it with liteparse
docker run --rm -v "$PWD:/data" liteparse:full \
parse /data/scan.pdf --ocr-server-url http://host.docker.internal:8080# Enable GPU (set CUDA_VISIBLE_DEVICES in .env or shell)
CUDA_VISIBLE_DEVICES=0 docker compose --profile ocr-sidecar upEasyOCR model files are cached in the liteparse_easyocr-models Docker volume and reused across container restarts. Remove the volume with make clean-volumes to force a re-download.
EasyOCR supports 80+ languages. Set OCR_LANGS to a comma-separated list of EasyOCR language codes:
OCR_LANGS=en,fr,de,ja docker compose --profile ocr-sidecar upThe test suite validates each flavour with progressive checks. The same scripts run locally and in CI.
# Test a single flavour (builds it first)
make test-base
make test-ocr
make test-full
make test-api
# Test all flavours
make test
# Or run tests against an already-built image
bash tests/test-flavour.sh base| Check | base | ocr | full | api |
|---|---|---|---|---|
| Smoke | ||||
| Image exists | ✓ | ✓ | ✓ | ✓ |
lit --version |
✓ | ✓ | ✓ | ✓ |
lit --help |
✓ | ✓ | ✓ | ✓ |
| PDF parsing | ||||
| Parse → text (content match) | ✓ | ✓ | ✓ | ✓ |
| Parse → JSON (pages, items, bbox) | ✓ | ✓ | ✓ | ✓ |
| Multi-page PDF (3 pages) | ✓ | ✓ | ✓ | ✓ |
--target-pages page selection |
✓ | ✓ | ✓ | ✓ |
| OCR | ||||
.traineddata files pre-baked |
— | ✓ | ✓ | ✓ |
eng.traineddata present |
— | ✓ | ✓ | ✓ |
TESSDATA_PREFIX set |
— | ✓ | ✓ | ✓ |
| Multi-format | ||||
| LibreOffice installed | — | — | ✓ | ✓ |
| ImageMagick installed | — | — | ✓ | ✓ |
| Parse DOCX → text (content match) | — | — | ✓ | ✓ |
| Parse PNG → exits cleanly | — | — | ✓ | ✓ |
| Parse PNG → valid JSON | — | — | ✓ | ✓ |
| API server | ||||
/health responds {"status":"ok"} |
— | — | — | ✓ |
GET /openapi.yaml (spec + content-type) |
— | — | — | ✓ |
POST /parse text format |
— | — | — | ✓ |
POST /parse JSON + bbox fields |
— | — | — | ✓ |
POST /parse multipage (3 pages) |
— | — | — | ✓ |
POST /parse targetPages filter |
— | — | — | ✓ |
POST /parse DOCX file |
— | — | — | ✓ |
POST /parse PNG file |
— | — | — | ✓ |
POST /parse no file → 400 + error body |
— | — | — | ✓ |
POST /batch-parse → ZIP response |
— | — | — | ✓ |
POST /batch-parse JSON format |
— | — | — | ✓ |
POST /batch-parse no file → 400 |
— | — | — | ✓ |
| Unknown route → 404 | — | — | — | ✓ |
| Security | ||||
| Runs as non-root user | ✓ | ✓ | ✓ | ✓ |
| HOME writable | ✓ | ✓ | ✓ | ✓ |
| TMPDIR writable | ✓ | ✓ | ✓ | ✓ |
| NODE_PATH set | ✓ | ✓ | ✓ | ✓ |
Fixtures are generated by tests/generate-fixtures.sh (wraps tests/generate-fixtures.js). Requires Node.js on the host. Generated files are gitignored — they are created on-the-fly before tests run.
| Fixture | Format | Purpose |
|---|---|---|
hello.pdf |
Single page, 3 text lines, includes special characters | |
multipage.pdf |
3 pages, used for page-count and --target-pages tests |
|
hello.docx |
DOCX | 3 paragraphs, tests LibreOffice conversion (full/api) |
hello.png |
PNG | 200x60 synthetic image, tests ImageMagick pipeline (full/api) |
batch.zip |
ZIP | Contains hello.pdf + multipage.pdf, tests POST /batch-parse |
First run installs pdf-lib and docx into tests/node_modules/ (also gitignored).
Images are hosted on GitHub Container Registry (GHCR) — free and unlimited for public repositories, with no pull rate limits. Optionally, images can also be published to Docker Hub (see Docker Hub publishing below).
| Trigger | Builds | Tests | Publishes to GHCR |
|---|---|---|---|
| Pull request | ✓ | ✓ | — |
Push to main |
✓ | ✓ | ✓ (multi-arch) |
| Manual dispatch | ✓ | ✓ | optional |
| Called by upstream-watch | ✓ | ✓ | ✓ |
The build matrix runs all four flavours (base, ocr, full, api) in parallel. The OCR sidecar is built and smoke-tested in a separate job.
Multi-arch images (linux/amd64 + linux/arm64) are pushed only after all tests pass. Build layers are cached via GitHub Actions cache (type=gha).
Runs daily at 06:17 UTC. Queries registry.npmjs.org for the latest @llamaindex/liteparse version and compares against .liteparse-version in the repo.
When a new version is detected:
npm registry ──→ upstream-watch ──→ opens PR ──→ CI runs ──→ merge ──→ publish
(new version) (daily cron) (version bump) (build+test) (manual/ (multi-arch
all pass auto) to GHCR)
- Creates a
deps/liteparse-<version>branch with the updated.liteparse-version - Opens a PR targeting
main, which triggers CI - CI builds and tests all flavours with the new version
- If all tests pass, the PR can be merged (auto-merge enabled if the repo allows it)
- Merging to
maintriggers the publish job — only tested images reach GHCR
This guarantees that no untested version is ever published.
The file .liteparse-version at the repo root tracks the currently-pinned LiteParse version. CI reads this file to determine which version to build. The upstream-watch workflow updates it via PR.
For version 1.3.1:
ghcr.io/<owner>/liteparse:base
ghcr.io/<owner>/liteparse:1.3.1-base
ghcr.io/<owner>/liteparse:ocr
ghcr.io/<owner>/liteparse:1.3.1-ocr
ghcr.io/<owner>/liteparse:full
ghcr.io/<owner>/liteparse:1.3.1-full
ghcr.io/<owner>/liteparse:latest (points to full)
ghcr.io/<owner>/liteparse:1.3.1 (points to full)
ghcr.io/<owner>/liteparse:api
ghcr.io/<owner>/liteparse:1.3.1-api
ghcr.io/<owner>/liteparse-ocr-sidecar:latest
By default, images are pushed only to GHCR. To also publish to Docker Hub, configure these in your repo settings (Settings > Secrets and variables > Actions):
Variables (Settings > Variables > New repository variable):
| Variable | Value | Required |
|---|---|---|
DOCKERHUB_ENABLED |
true |
yes |
DOCKERHUB_IMAGE |
youruser/liteparse |
no (defaults to <owner>/liteparse) |
Secrets (Settings > Secrets > New repository secret):
| Secret | Value | Required |
|---|---|---|
DOCKERHUB_USERNAME |
Your Docker Hub username | yes |
DOCKERHUB_TOKEN |
Docker Hub access token (create one here) | yes |
Once configured, the next push to main will publish to both registries with identical tags:
ghcr.io/rafaelgom3s/liteparse:1.3.1-full # GHCR (always)
youruser/liteparse:1.3.1-full # Docker Hub (opt-in)
To disable, delete or set DOCKERHUB_ENABLED to any value other than true.
Rather than maintaining separate Dockerfiles per flavour, a single Dockerfile uses ARG-guarded if [ ... ] shell conditionals inside RUN layers. This keeps the build logic in one place and makes it trivial to compose arbitrary combinations of features without combinatorial explosion.
All optional apt-get install calls share a single RUN apt-get update layer, avoiding the well-known cache-invalidation anti-pattern of splitting update and install across layers.
A dedicated tessdata-downloader stage (based on debian:bookworm-slim) downloads and decompresses Tesseract.js .traineddata files from the jsDelivr CDN using curl. The final image then COPY --from=tessdata-downloaders the results into /tessdata.
When TESSERACT_LANGS="" (the base flavour), the downloader stage produces an empty directory and the COPY is a no-op — the base image pays zero cost for this stage.
This approach keeps build tools (curl) out of the final image and isolates all network I/O at build time, so runtime containers can operate fully offline.
LiteParse reads process.env.TESSDATA_PREFIX and passes it to Tesseract.js as both langPath and cachePath with gzip: false, meaning it expects plain .traineddata files in that directory. Setting ENV TESSDATA_PREFIX=/tessdata in the image means:
- ocr/full/api: models are in
/tessdata→ zero CDN calls at parse time. - base:
/tessdatais empty → Tesseract.js falls back to CDN download on first use.
LibreOffice depends on glibc. Building it on Alpine's musl is impractical. The node:22-bookworm-slim base image also satisfies sharp's pre-built native binaries (which target linux-x64-glibc), avoiding any native compilation during the build.
Instead of the full libreoffice metapackage (which pulls in X11 client libraries, Java, and desktop themes), only the writer, calc, impress, draw, base-core, and nogui packages are installed. This avoids several hundred megabytes of unnecessary dependencies.
Debian's default ImageMagick policy.xml blocks PDF operations. A targeted sed replacement relaxes the PDF coder policy to read|write so that LiteParse can use ImageMagick in its conversion pipeline.
The Fastify REST server (api-server/) is included in every image (its footprint is ~15 MB of node_modules). The default ENTRYPOINT ["lit"] is overridden to node /app-src/server.js in the docker-compose api profile and in the Makefile run-api target.
NODE_PATH=/usr/local/lib/node_modules lets the api-server require('@llamaindex/liteparse') without re-installing the package, since it is already globally installed by the main npm install -g step.
docker-bake.hcl declares targets for linux/amd64 and linux/arm64. A single make multi-arch command builds and pushes all four flavours for both architectures using docker buildx bake. The EasyOCR sidecar is also included as a bake target.
LiteParse is created and maintained by LlamaIndex (run-llama).
- Documentation: https://developers.llamaindex.ai/liteparse/
- GitHub: https://github.com/run-llama/liteparse
- npm package: @llamaindex/liteparse
This repository contains only Docker packaging. All document parsing functionality is provided by LiteParse under its original license.
Third-party components used in the sidecar and tooling:
| Component | Purpose | License |
|---|---|---|
| EasyOCR | High-accuracy OCR engine | Apache 2.0 |
| Tesseract.js | Bundled OCR (via liteparse) | Apache 2.0 |
| LibreOffice | Office-document conversion | MPL 2.0 |
| ImageMagick | Image-file conversion | ImageMagick License |
| Fastify | REST API framework | MIT |
| FastAPI | OCR sidecar framework | MIT |