Convert DOCX to PDF With Docker and LibreOffice
A practical way to add document conversion without a paid library: keep LibreOffice in a container, call it from a small Node.js service, and be very deliberate about temporary files.
- Published
- 26 Oct 2025
- Reading time
- 6 minutes
- Author
- Aaryan Jha
Why LibreOffice
DOCX is not a page description. It is a collection of document rules that must be laid out before a PDF can exist. A custom parser can extract text, but matching Word’s tables, pagination, images and fonts is a different problem.
LibreOffice already solves that rendering problem and can run without a graphical interface. Docker then solves the awkward deployment question by packaging the exact binary, fonts and permissions beside the service that invokes it.
The conversion command
The useful part of the interface is deliberately boring. Store the uploaded file under a server-generated name, create a controlled output directory, and invoke LibreOffice in headless mode.
soffice --headless --convert-to pdf --outdir /app/output /app/uploads/input.docx- --headless starts LibreOffice without a desktop session
- --convert-to pdf chooses the target format
- --outdir makes the output location explicit instead of relying on process defaults
The output-directory trap
The first version omitted --outdir. LibreOffice then wrote beside the input file, which produced permission failures in the container and made output discovery depend on where the upload happened to land.
Treat file paths as state
The service should own every path it touches. It should know the expected PDF name before conversion begins, verify that the file exists afterwards, and remove both input and output when the response finishes or an error occurs.
Container setup
The image needs Node.js, LibreOffice, certificates and fonts. The fonts are not decoration: missing fonts can change line wrapping, pagination and the visual result even when conversion technically succeeds.
Create the upload and output directories during the image build, assign them to the application user, and run the process without root privileges. That gives the converter exactly the write access it needs and no more.
An Alpine base can keep the image reasonably small, but test the documents your users actually upload. Font compatibility and native-package availability matter more than winning a container-size contest.
HTTP and safety
A straightforward endpoint accepts one multipart DOCX and returns application/pdf. Before invoking LibreOffice, enforce a body-size limit, inspect the actual file type, ignore the client filename for storage, and reject formats the service does not promise to handle.
Do not concatenate a user-controlled path into a shell command. Prefer a process API with an argument array, generated filenames and a dedicated working directory. A timeout should terminate conversions that never finish, and cleanup belongs in a finally path rather than only after success.
When to add a queue
In my testing, a cold conversion took roughly three seconds and a warm conversion around half a second. The service handled about eight to ten simultaneous conversions before process contention became the concern.
Those numbers depend heavily on document size and available CPU and memory. If traffic can spike, acknowledge the upload quickly, place a job on a queue and let a bounded worker pool perform conversions. For modest internal traffic, a synchronous endpoint is simpler and can be perfectly adequate.