LibreOffice in Docker for Document Processing
Running LibreOffice in headless mode inside isolated Docker containers allows microservice architectures to perform robust, automated document conversions (such as DOCX to PDF) at scale. This guide outlines how to build a lightweight LibreOffice container, execute conversions programmatically via a microservice API, isolate user profiles for concurrency, and apply security and resource limits to ensure system stability.
1. Building a Lightweight Docker Image
To minimize image size while ensuring all necessary rendering libraries and fonts are present, use a lightweight base image like Alpine Linux or Debian Slim.
Create a Dockerfile:
FROM debian:bullseye-slim
# Install LibreOffice headless and essential fonts
RUN apt-get update && apt-get install -y --no-install-recommends \
libreoffice-writer \
libreoffice-calc \
libreoffice-impress \
fonts-liberation \
fonts-dejavu \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Create a non-root user for security
RUN useradd -m -u 1000 appuser
WORKDIR /app
USER appuserInstalling font packages (fonts-liberation,
fonts-dejavu) is critical to prevent text displacement and
visual artifacts during document conversions.
2. Setting Up an HTTP Microservice Wrapper
LibreOffice operates via CLI. To use it in a microservice architecture, wrap the CLI commands in a lightweight web server (such as Python’s FastAPI) that accepts file uploads and returns the converted output.
Example app.py:
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import FileResponse
import subprocess
import tempfile
import os
import shutil
app = FastAPI()
@app.post("/convert")
async def convert_document(file: UploadFile = File(...)):
with tempfile.TemporaryDirectory() as temp_dir:
input_path = os.path.join(temp_dir, file.filename)
with open(input_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
# Isolated user profile directory for thread-safety
user_profile_dir = os.path.join(temp_dir, "profile")
cmd = [
"libreoffice",
"--headless",
"--invisible",
"--nodefault",
"--nofirststartwizard",
"--nolockcheck",
"--nologo",
"--norestore",
f"-env:UserInstallation=file://{user_profile_dir}",
"--convert-to", "pdf",
"--outdir", temp_dir,
input_path
]
try:
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30)
if result.returncode != 0:
raise HTTPException(status_code=500, detail="Conversion failed.")
except subprocess.TimeoutExpired:
raise HTTPException(status_code=408, detail="Conversion timed out.")
base_name = os.path.splitext(file.filename)[0]
output_path = os.path.join(temp_dir, f"{base_name}.pdf")
if not os.path.exists(output_path):
raise HTTPException(status_code=500, detail="Output file not generated.")
return FileResponse(output_path, media_type="application/pdf", filename=f"{base_name}.pdf")3. Key Conversion Flags for Concurrency
By default, LibreOffice limits operations to a single instance tied to a shared user profile. To allow multiple containers or threads to convert documents concurrently without locking conflicts, include the following flags:
--headless: Runs without a graphical user interface.--invisible&--nologo: Suppresses UI startup screens and logos.--nodefault&--norestore: Prevents creating default blank documents and suppresses crash recovery dialogs.-env:UserInstallation=file:///path/to/unique/dir: Forces LibreOffice to use an ephemeral configuration directory, preventing process collisions during concurrent jobs.
4. Resource and Security Hardening
LibreOffice parses complex, untrusted file formats. Strict Docker isolation mitigates the risk of exploits, memory leaks, and CPU exhaustion.
Run the container with resource limits and restricted privileges:
docker run -d \
--name libreoffice-converter \
--p 8000:8000 \
--memory="1g" \
--cpus="1.5" \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=512m \
--tmpfs /app:rw,noexec,nosuid,size=512m \
--cap-drop=ALL \
--security-opt=no-new-privileges \
libreoffice-converter-image--read-only: Ensures the container root filesystem is immutable.--tmpfs: Provides isolated, in-memory writable directories for temporary file processing that automatically clear on completion.--cap-drop=ALL: Removes all default Linux capabilities from the container.--memoryand--cpus: Prevents malformed or large documents from crashing the host machine via memory exhaustion.
5. Lifecycle Management and Scaling
Because LibreOffice can accumulate unreleased memory over prolonged execution periods, implement the following lifecycle practices:
- Ephemeral Workers: Run conversions via queue
consumers (such as Celery or RabbitMQ workers) that recycle or restart
worker processes after a fixed number of tasks (e.g.,
--max-tasks-per-child=50). - Enforce Timeouts: Always wrap the CLI call with a strict timeout (e.g., 30 to 60 seconds) to terminate hanging processes caused by corrupt files.
- Horizontal Auto-Scaling: Deploy the containers behind an orchestrator like Kubernetes or Docker Swarm, scaling pods based on CPU utilization or message queue length.