Install
This page shows you how to run the service with the right container flags, with Compose, and from source, and how to run the CLI. It ends with what the machine needs.
The image
Section titled “The image”The image is ghcr.io/vmosoti/rusticdoc:0.1.0, for amd64 and arm64. While the repository is private, so is the image: sign in with docker login ghcr.io and a GitHub token that can read packages before you pull it. You can also build it from a checkout of the repository:
docker build -t rusticdoc .The build pins its base images by digest. The result is one Rust binary on Debian 13 with LibreOffice, its Python bridge, qpdf and pdfcpu. It has no Node, Java or Chromium. It runs as user 1001 and listens on port 9027.
Run it hardened
Section titled “Run it hardened”docker run -d --name rusticdoc \ --read-only --cap-drop ALL --security-opt no-new-privileges \ --tmpfs /tmp --tmpfs /home/rusticdoc:uid=1001,gid=1001 \ -p 127.0.0.1:9027:9027 ghcr.io/vmosoti/rusticdoc:0.1.0| Flag | Why |
|---|---|
--read-only |
A compromised engine cannot change the image’s files |
--cap-drop ALL |
The service binds an unprivileged port and needs no Linux capabilities |
--security-opt no-new-privileges |
Nothing inside can gain privileges through a setuid binary |
--tmpfs /tmp |
Scratch space for conversions. With --read-only and no /tmp, a conversion answers 500 |
--tmpfs /home/rusticdoc:uid=1001,gid=1001 |
A writable home directory for the engines. A DOCX conversion also worked without it, but Compose and the Kubernetes manifest both provide one, so that is the setup to copy |
-p 127.0.0.1:9027:9027 |
Publishes the port on this machine only |
Do not add --init. The service runs as PID 1 so that it can close its /proc/1/environ to the user LibreOffice runs as. With --init, PID 1 is docker-init, and its environment, which holds your keys, is readable from inside the container.
With no credentials, the service answers anyone and warns at every start. Set RUSTICDOC_API_KEYS before you publish the port more widely. Docker has more, including limits on memory and processes.
Compose
Section titled “Compose”deploy/compose.yaml sets the same flags and adds limits on memory, processes and temporary files. It reads its settings from your environment. From the root of a checkout:
docker compose -f deploy/compose.yaml up -d --buildTo use the published image instead of building one:
RUSTICDOC_IMAGE=ghcr.io/vmosoti/rusticdoc:0.1.0 \ docker compose -f deploy/compose.yaml up -d --no-buildDocker lists the variables it reads.
Build from source
Section titled “Build from source”You need the Rust toolchain that rust-toolchain.toml pins, which is 1.99.1. rustup installs it for you.
cargo build --release -p rusticdoc./target/release/rusticdoc serve --bind 127.0.0.1:9027The binary looks for its engines at fixed paths, not on your PATH. Set RUSTICDOC_LIBREOFFICE_BIN, RUSTICDOC_QPDF_BIN and RUSTICDOC_PDFCPU_BIN if yours are elsewhere. LibreOffice also needs its Python UNO bridge, which is the python3-uno package on Debian. The configuration reference lists the paths it tries.
rusticdoc engines shows what it found. An engine that is missing fails only the steps that need it, with 503. On a Mac with no engines installed, text templates rendered and a convert step answered 503. The render memory limit relies on Linux and has not been shown to hold on macOS, so do not serve untrusted templates from a Mac.
The CLI
Section titled “The CLI”Download it
Section titled “Download it”Each release attaches the CLI for Linux and macOS to its GitHub release, with a SHA256SUMS file:
| System | Archive |
|---|---|
| Linux, x86-64 | rusticdoc-0.1.0-x86_64-unknown-linux-gnu.tar.gz |
| Linux, ARM64 | rusticdoc-0.1.0-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | rusticdoc-0.1.0-aarch64-apple-darwin.tar.gz |
| macOS, Intel | rusticdoc-0.1.0-x86_64-apple-darwin.tar.gz |
While the repository is private, download with the GitHub CLI, which uses your sign-in. This fetches the Apple silicon build, checks it and puts it on your PATH:
gh release download v0.1.0 --repo vmosoti/rusticdoc \ --pattern 'rusticdoc-0.1.0-aarch64-apple-darwin.tar.gz' --pattern SHA256SUMSshasum -a 256 --check --ignore-missing SHA256SUMStar -xzf rusticdoc-0.1.0-aarch64-apple-darwin.tar.gzsudo mv rusticdoc-0.1.0-aarch64-apple-darwin/rusticdoc /usr/local/bin/rusticdoc --versionOn Linux, sha256sum --check --ignore-missing SHA256SUMS does the check.
- macOS marks a downloaded binary as quarantined, and the builds are not notarised. If it refuses to open, run
xattr -d com.apple.quarantine /usr/local/bin/rusticdoc. - Linux builds need glibc 2.35 or newer, as on Ubuntu 22.04 and Debian 12, and fontconfig for HTML layout to find fonts.
- Windows has no build: the CLI’s process handling is Unix-only. Run the Linux build under WSL, or use the image as below.
The downloaded CLI renders, validates and runs the PDF operations its own engine covers. convert steps need LibreOffice installed, and some PDF operations need qpdf or pdfcpu, as build from source describes. rusticdoc engines shows what it found.
From the image
Section titled “From the image”The service and the CLI are the same binary, so the image has the CLI in it:
docker run --rm --entrypoint rusticdoc ghcr.io/vmosoti/rusticdoc:0.1.0 enginesTo work on files in your current directory, mount it. The container’s user has to be able to write there, so on Linux pass your own user ID. Group 0 owns the home directory, so keep that group:
docker run --rm -v "$PWD:/work" -w /work --user "$(id -u):0" \ --entrypoint rusticdoc ghcr.io/vmosoti/rusticdoc:0.1.0 \ render invoice.docx --data invoice.json --steps convert -o invoice.pdfFrom a source build, cargo build --release -p rusticdoc gives you the same commands in target/release/rusticdoc.
The CLI reads --data from a file.
The CLI renders templates in its own process, with no separate worker and no memory limit, because it assumes the template is yours. serve isolates them. Run untrusted templates through the service. The CLI reference lists the commands.
System requirements
Section titled “System requirements”| What is known | |
|---|---|
| Container runtime | Docker. CI builds and smoke-tests the image on ubuntu-latest. The examples in these docs ran on an arm64 build |
| Disk | About 300 MB to download, unpacking to about 810 MB, measured on the arm64 build. Docker Desktop keeps both copies and shows about 1.1 GB |
| Memory at rest | 150 to 270 MiB in docker stats, measured after a first conversion with two LibreOffice workers. This grows with what you ask of it |
| Memory under load | Compose limits the container to 6 GiB, which leaves room for four render workers at 1 GiB each, the service and two LibreOffice workers. Sizing has the arithmetic and a failure seen at less |
| CPU | The Kubernetes manifest requests one CPU per pod. No minimum has been measured |
| Operating system for the service | Linux, for the memory limit on render workers. The image is Linux |
| Rust, from source | 1.99.1, pinned in rust-toolchain.toml |
There are no load test figures yet. Size for your own documents and measure them.