How to Self-Host Stirling-PDF on Proxmox LXC 2026
Home Lab

How to Self-Host Stirling-PDF on Proxmox LXC 2026

Ricardo Gil
October 12, 2026
12 min read
#proxmox #lxc #self-hosting #pdf-tools #stirling-pdf
πŸ›’

Products in This Post

Affiliate links

As an Amazon Associate I earn from qualifying purchases at no extra cost to you.

PDF tools are everywhere online β€” but they all come with the same catch: your documents leave your machine. Adobe Acrobat phones home. Smallpdf processes files on their servers. ILovePDF uploads everything to the cloud. If you're handling contracts, financial statements, legal documents, or anything else you'd rather keep private, that's a dealbreaker.

Stirling-PDF fixes this. It's an open-source, self-hosted PDF manipulation suite with over 40 operations β€” merge, split, compress, convert, OCR, watermark, rotate, sign, redact, and more β€” all running locally on hardware you control. Per the official Stirling-PDF project documentation, every operation is performed server-side with no data persistence: files are processed in memory and discarded immediately. Nothing leaves your network.

In this guide I'll walk through setting up Stirling-PDF inside a Proxmox LXC container using Docker Compose β€” the cleanest deployment approach for a homelab environment. By the end you'll have a fully functional, self-hosted PDF toolkit accessible over HTTPS from anywhere on your network (or beyond, if you wire it up to Traefik).

What Is Stirling-PDF?

Stirling-PDF (maintained by Stirling-Tools on GitHub) is a Spring Boot application that wraps several battle-tested open-source PDF libraries into a clean, modern web UI. Under the hood it uses LibreOffice for document-to-PDF conversion, Ghostscript for compression and manipulation operations, and Tesseract for OCR. The project packages all of this into a single Docker image, which makes deployment straightforward even inside a resource-constrained LXC container.

The feature set is genuinely comprehensive. Beyond the basics, Stirling-PDF includes: PDF-to-Word/Excel/HTML conversion, image-to-PDF with multi-page support, extract and reorder pages, compare two PDFs side-by-side, add page numbers and watermarks, digitally sign PDFs, and redact sensitive text. The optional OCR mode (powered by Tesseract) makes scanned documents fully searchable β€” a natural pairing if you're also running Paperless-ngx for document archiving.

Resource requirements, per the official project documentation: a minimum of 2 GB RAM for standard PDF operations. Enabling OCR and the advanced HTML-to-PDF conversion mode (which brings in Calibre and additional dependencies) pushes that to 4 GB. CPU requirements are modest β€” the workload is I/O-bound rather than compute-bound, so even a low-power N100 or older Ryzen handles it without issue.

Why Self-Host a PDF Tool?

The privacy argument is the most obvious one, but the practical benefits compound quickly. A self-hosted Stirling-PDF instance comes with no upload limits, no monthly subscription, no "Premium plan required" paywalls blocking specific features. You get every feature the project offers β€” including OCR, PDF-to-Word conversion, and digital signing β€” for free, running on hardware you already own for other workloads.

For businesses, the compliance dimension matters significantly. HIPAA, SOC 2, GDPR, and similar frameworks all get complicated the moment you start piping sensitive documents through third-party SaaS services. A self-hosted solution keeps your documents on your own infrastructure, processed by software you've vetted, with no third party anywhere in the chain. That's a much simpler story to tell in an audit.

There's also a reliability argument worth making. Cloud PDF services have rate limits, occasional outages, and unpredictable policy changes. A self-hosted instance has none of those constraints β€” it's available whenever your homelab is running. Pair it with a quality UPS like the CyberPower CP1500PFCLCD (~$220) and your uptime is essentially only limited by your ISP and hardware.

Prerequisites

Before you begin, make sure you have the following in place:

  • A running Proxmox VE 8.x instance. If you're setting up your homelab from scratch, the complete LXC self-hosting guide covers the full Proxmox setup, networking, and storage configuration.
  • A Debian 12 LXC template downloaded in Proxmox (we'll pull it in the next step if needed)
  • At least 2 GB of RAM to allocate to the container β€” bump to 4 GB if you want OCR support
  • 10 GB of storage for the container root (Stirling-PDF's Docker image is approximately 1.5 GB)
  • Optional: a domain with DNS configured and a working Traefik setup if you want HTTPS access

Host hardware matters here. Any modern mini PC with 16 GB of RAM can comfortably run Stirling-PDF alongside a full homelab stack. The Beelink SER5 (Ryzen 5 5500U, 16GB/480GB, ~$299) is a solid foundation β€” enough RAM to run Stirling-PDF, Vaultwarden, Navidrome, and several other LXC containers simultaneously without hitting memory pressure. If you're planning a larger self-hosted stack, the MINISFORUM UM890 Pro (Ryzen 9 8945HS, 32GB/1TB, ~$429) gives you significantly more headroom.

One critical Proxmox setting: the LXC container needs the nesting=1 feature flag to run Docker inside it. This is set during container creation and can't be changed on a running container β€” make sure you include it from the start.

Creating the LXC Container

Start by making sure your Proxmox node has the Debian 12 template available. Run the following on the Proxmox host shell (accessible via the Proxmox web UI under your node β†’ Shell, or via SSH):

bash
pveam update
pveam download local debian-12-standard_12.7-1_amd64.tar.zst

Now create the container. I'm using CT ID 200 here β€” substitute whatever ID fits your numbering scheme. The --features keyctl=1,nesting=1 flag is what makes Docker work inside the LXC:

bash
pct create 200 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --hostname stirling-pdf \
  --cores 2 \
  --memory 2048 \
  --swap 512 \
  --storage local-lvm \
  --rootfs local-lvm:10 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --unprivileged 1 \
  --features keyctl=1,nesting=1

pct start 200
pct enter 200

You're now inside the container. Run the initial system update and install the tools you'll need for the Docker install:

bash
apt update && apt upgrade -y
apt install -y curl ca-certificates gnupg lsb-release

Assign a static IP to this container β€” either via a DHCP reservation on your router (using the MAC address visible in the Proxmox UI under the container's Network tab), or by editing /etc/network/interfaces directly in the container. Write down the assigned IP; you'll use it to access the web UI and to configure Traefik later.

If you want to enable OCR support in Stirling-PDF, allocate 4 GB of RAM instead of 2 GB. You can set this during container creation by changing --memory 2048 to --memory 4096, or you can adjust it later via the Proxmox UI under the container's Resources tab. A memory change requires a container restart to take effect.

Installing Docker Inside the LXC

Stirling-PDF's recommended deployment method is Docker Compose, and running Docker inside a Proxmox LXC works cleanly as long as the nesting feature flag is set. The official Docker convenience script handles the full install:

bash
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker

Verify the Docker installation before proceeding:

bash
docker --version
docker run --rm hello-world

If hello-world runs successfully and prints the confirmation message, Docker is working correctly inside the LXC. If you get permission or AppArmor errors, double-check that nesting=1 is set β€” verify with pct config 200 on the Proxmox host. The output should include features: keyctl=1,nesting=1.

Install Docker Compose as a plugin (the modern approach, integrated with the Docker CLI):

bash
apt install -y docker-compose-plugin
docker compose version

You should see a version string like Docker Compose version v2.x.x. With both Docker and Docker Compose installed, you're ready to deploy Stirling-PDF.

Deploying Stirling-PDF with Docker Compose

Create a working directory for the service and navigate into it:

bash
mkdir -p /opt/stirling-pdf && cd /opt/stirling-pdf

Create the docker-compose.yml file. This is the core configuration that defines how Stirling-PDF runs:

yaml
version: '3.8'
services:
  stirling-pdf:
    image: frooodle/s-pdf:latest
    container_name: stirling-pdf
    ports:
      - "8080:8080"
    volumes:
      - ./configs:/configs
      - ./extraConfigs:/extraConfigs
      - ./customFiles:/customFiles
      - ./logs:/logs
    environment:
      - DOCKER_ENABLE_SECURITY=false
      - INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
      - LANGS=en_GB
    restart: unless-stopped

A few notes on the environment variables. DOCKER_ENABLE_SECURITY controls whether Stirling-PDF's built-in authentication layer is active β€” set it to true if you're exposing this instance to the public internet or to an untrusted network. INSTALL_BOOK_AND_ADVANCED_HTML_OPS enables Calibre-based ebook-to-PDF conversion and advanced HTML rendering; it increases the Docker image size substantially and isn't needed for standard PDF operations, so leave it false unless you specifically need those features. LANGS controls which Tesseract OCR language packs are installed β€” add additional languages with a comma-separated list per the official project documentation (for example, en_GB,es_ES,fr_FR).

Pull the image and start the container:

bash
docker compose pull
docker compose up -d

The initial image pull takes a few minutes β€” the base image is approximately 1.5 GB. Once it's running, open a browser and navigate to http://<your-lxc-ip>:8080. The Stirling-PDF dashboard should appear, showing all available PDF operations organized by category. If the UI doesn't load after a minute, check the container logs for startup errors:

bash
docker compose logs -f stirling-pdf

The application typically takes 30–60 seconds to fully initialize on first startup as it loads the Spring Boot context and configures the PDF processing libraries.

Configuring Stirling-PDF

Out of the box, Stirling-PDF works without any additional configuration. The volumes defined in the compose file create a configs directory in /opt/stirling-pdf/ on first startup, containing a settings.yml file with all available options. Here are the key settings worth reviewing for a homelab deployment:

yaml
security:
  enableLogin: false           # Set true to require authentication
  initialLogin:
    username: "admin"
    password: "your-password"  # Required if enableLogin is true
system:
  defaultLocale: "en-US"
  googlevisibility: false      # Removes Google indexing hints from the UI
  customHTMLFiles: false
ui:
  appName: "PDF Tools"         # Customize the dashboard title
  homeDescription: "Self-hosted PDF processing β€” nothing leaves your network"
  appNameNavbar: "PDF Tools"

The authentication decision depends on your network setup. On a trusted internal network β€” behind Tailscale, Headscale, or a properly firewalled VLAN β€” leaving login disabled is reasonable. The moment you expose the instance to the public internet via a reverse proxy, enable authentication and set a strong password before going live.

After editing settings.yml, apply the changes with a container restart:

bash
cd /opt/stirling-pdf && docker compose restart stirling-pdf

Putting Stirling-PDF Behind Traefik

If you already have Traefik running as your reverse proxy, connecting Stirling-PDF takes about two minutes. Update docker-compose.yml to remove the direct port binding and add Traefik routing labels:

yaml
version: '3.8'
services:
  stirling-pdf:
    image: frooodle/s-pdf:latest
    container_name: stirling-pdf
    volumes:
      - ./configs:/configs
      - ./extraConfigs:/extraConfigs
      - ./customFiles:/customFiles
      - ./logs:/logs
    environment:
      - DOCKER_ENABLE_SECURITY=false
      - INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
      - LANGS=en_GB
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.stirling-pdf.rule=Host(`pdf.yourdomain.com`)"
      - "traefik.http.routers.stirling-pdf.entrypoints=websecure"
      - "traefik.http.routers.stirling-pdf.tls=true"
      - "traefik.http.routers.stirling-pdf.tls.certresolver=letsencrypt"
      - "traefik.http.services.stirling-pdf.loadbalancer.server.port=8080"
    networks:
      - traefik
    restart: unless-stopped

networks:
  traefik:
    external: true

Replace pdf.yourdomain.com with your actual subdomain and make sure your DNS points it to your Proxmox host's public IP (or Cloudflare Tunnel endpoint). Once deployed, Traefik handles TLS termination automatically via Let's Encrypt β€” your instance will be accessible at https://pdf.yourdomain.com with a valid certificate.

If Traefik runs on a different LXC or host, the containers need to share a Docker network. The simplest approach is to add an attachable: true flag to the Traefik network definition on the Traefik side, then reference it as external: true on the Stirling-PDF side as shown above. The complete self-hosting guide covers cross-container networking and the full Traefik configuration pattern in detail.

Keeping Stirling-PDF Updated

The Stirling-PDF project ships updates frequently β€” new operations, UI improvements, and bug fixes appear regularly. Updating the container is a two-command operation:

bash
cd /opt/stirling-pdf
docker compose pull
docker compose up -d

Docker Compose handles this gracefully: it pulls the new image, stops the running container, and starts a replacement with the same volumes attached. The ./configs directory is preserved because it's mounted as a volume rather than baked into the image β€” your settings.yml customizations survive updates.

For automated updates, Watchtower is a common choice in homelab environments β€” it's a container that monitors your other containers and pulls new image versions on a configurable schedule. That said, for a tool processing sensitive documents, reviewing the changelog before each update is a reasonable precaution. Breaking changes in how PDFs are processed are rare, but they happen.

To prune old Docker images after updating (they accumulate and consume disk space):

bash
docker image prune -f

Resource Usage and Scaling

Per the official project documentation, standard PDF operations β€” merging documents, splitting pages, compressing files, rotating pages β€” run comfortably within the 2 GB RAM allocation. The memory ceiling gets approached when processing large batches of high-resolution scans through OCR, since Tesseract is memory-intensive at scale.

If the container hits its memory limit under heavy OCR load, increase the allocation on the Proxmox host and restart the container:

bash
# On the Proxmox host (not inside the LXC):
pct set 200 --memory 4096
pct reboot 200

CPU usage is similarly modest for most operations β€” a 2-core allocation is sufficient. The exception is bulk OCR jobs on multi-page scans, where Tesseract will saturate whatever cores are available. For a shared homelab host running multiple LXC containers, 2 cores is a reasonable allocation; you can raise it temporarily during heavy jobs using pct set 200 --cores 4 and drop it back afterward.

Disk usage is dominated by the Docker image (~1.5 GB) plus whatever temporary files are in flight during processing. Stirling-PDF doesn't persist processed files β€” they're downloaded by the browser and then cleared β€” so the rootfs size stays stable over time. The 10 GB allocation defined during container creation is comfortable headroom for the image, the OS, and runtime overhead.

Use Cases Worth Highlighting

A few Stirling-PDF workflows that are particularly useful in a homelab or small business context. The OCR + searchable PDF pipeline is the most impactful: scan a physical document, upload it to Stirling-PDF, run OCR, and get back a fully searchable PDF. Feed that into Paperless-ngx and you have a complete document management pipeline with zero data leaving your network.

The PDF compression operation is surprisingly effective for reducing email attachment sizes. Per the project's feature documentation, Ghostscript-based compression can reduce PDF file size by 60–80% for typical scanned documents, depending on original resolution and content type. For office environments where large PDFs get emailed constantly, this alone justifies the setup.

The comparison tool is useful for document review workflows β€” upload two versions of a contract or specification, and Stirling-PDF highlights the differences visually. It's not as polished as a dedicated document comparison tool, but for occasional use cases it's more than adequate, and it keeps the comparison entirely within your own infrastructure.

Want this kind of setup for your business?

I deploy private, self-hosted infrastructure on Proxmox: AI, automation and internal tools on hardware you own, documented and handed off. See the Private AI Stack β†’

Not sure what you need? Book a free discovery call.

Final Thoughts

Stirling-PDF is one of those self-hosted tools that earns its place immediately. The installation is straightforward β€” a Debian LXC, Docker inside it, and a single Docker Compose file β€” and the result is a capable PDF toolkit that handles everything you'd previously have sent to Adobe or Smallpdf. The zero-data-retention design means you can confidently process any document without thinking about where it ends up.

The LXC + Docker Compose pattern used here is the same approach that works for dozens of homelab services. Once you're comfortable with it, deploying a new service is a repeatable 15-minute exercise: create the container, install Docker, write a compose file, and point Traefik at it. If you haven't worked through a few of these yet, the Vaultwarden LXC guide is a good second example of the same pattern applied to a different service.

Start with the 2 GB allocation and the default configuration. OCR and advanced conversion features can be enabled incrementally as you find you need them. The project's GitHub repository is actively maintained and the documentation is solid β€” when something doesn't behave as expected, the issue tracker and discussions tab are genuinely helpful resources.

Ricardo Gil is a full-stack developer (.NET/Angular) with 6+ years of experience. He runs a personal homelab on a Beelink mini PC with Proxmox VE, self-hosting everything from Jellyfin to local LLMs with Ollama. More about Ricardo β†’
πŸ“¬Weekly Newsletter

Get the best home lab & AI content

No spam. One email per week. Unsubscribe anytime.

Share this article