Skip to content

Utilities stack

Three small, stateless-ish web tools that I'd otherwise reach for a random website for. They run together in one compose stack on the apps server, because none of them needs much and they share the same lifecycle.

Tool What it does Port
IT-Tools Developer helpers in the browser: JWT/base64/hash/UUID, cron and chmod calculators, subnet calculator, QR codes, text diff, ... 8071
BentoPDF PDF toolkit (merge, split, compress, rotate, sign, convert). Everything runs client-side in your browser, so the files never leave your machine. 8072
ConvertX Converts files between ~1000 formats (FFmpeg, LibreOffice, ImageMagick, Pandoc, ...). Has accounts and keeps converted files for a while. 3002

Why self-host them: documents like tax PDFs, IDs or work files shouldn't be uploaded to "free online PDF merger" sites. These do the same thing on your own hardware.

Compose

compose.yaml
services:

  ### --- IT Tools --- ### https://github.com/CorentinTh/it-tools
  it-tools:
    image: corentinth/it-tools:2024.10.22-7ca5933
    container_name: it-tools
    restart: unless-stopped
    ports:
      - "8071:80"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s

  ### --- Bento PDF --- ### https://github.com/alam00000/bentopdf
  bentopdf:
    image: bentopdf/bentopdf-simple:1.15.4
    container_name: bentopdf
    restart: unless-stopped
    # bentopdf(-simple) listens on 8080 inside the container
    ports:
      - "8072:8080"
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/"]
      interval: 60s
      timeout: 5s
      retries: 3
      start_period: 20s

  ### --- ConvertX --- ### https://github.com/C4illin/ConvertX
  convertx:
    image: ghcr.io/c4illin/convertx:v0.19.0
    container_name: convertx
    restart: unless-stopped
    ports:
      - "3002:3000"
    #environment:
      #- JWT_SECRET=${JWT_SECRET} # when unset it will use the value from randomUUID()
    volumes:
      - /mnt/appdata/convertx/data:/app/data
    # ConvertX ships a real /healthcheck route - better than a raw TCP check
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

No .env is needed as written. If you set JWT_SECRET for ConvertX (recommended if you want logins to survive a container recreate), add it as ${JWT_SECRET} and create the value with openssl rand -hex 32.

Notes

  • Pinned tags. Every image is pinned and Renovate proposes updates. ConvertX is pre-1.0, where a 0.x minor bump can be a breaking release, so its updates wait for review instead of automerging.
  • BentoPDF listens on 8080, not 3000 as some older examples show. Mapping 8072:3000 gives you a container that is "up" but serves nothing, which is why it has a real HTTP healthcheck now.
  • Healthchecks use what's in each image (curl in IT-Tools and ConvertX, wget in BentoPDF's Alpine/nginx image). Check with docker exec <container> which curl wget before copying a healthcheck to another image.
  • State: only ConvertX writes anything (/app/data: its SQLite DB and the converted files, auto-deleted after 24 h by default). IT-Tools and BentoPDF are static web apps.
  • Access: all three are LAN-only tools. If you ever publish them, put them behind SSO first; ConvertX in particular runs real converters on uploaded files.

Sources & further reading