Self-hosted service
dendianugerah/reubah avatar
dendianugerah/reubah

Reubah: a self-hosted image and document converter

a web-based tool for processing images and converting documents with a simple interface

702 stars42 forksHTMLMIT

At a glance

What is it?
Reubah is an MIT licensed Go service with a web interface that converts images and documents in a container, and its most useful feature is honesty: the readme publishes a per format compatibility matrix, including the combinations that do not work and the note that PDF to PDF is still bad. The judgement is that a small tool that tells you where it fails is easier to adopt than one that quietly returns a wrong file.
Who is it for?
Reubah fits a team that needs to convert a document or a batch of images on its own infrastructure and does not want to upload them anywhere, because the service runs from one container, keeps nothing on disk and cleans up after itself, and the compatibility matrix tells you in advance which conversions will work.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 88 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A converter that keeps nothing

Reubah is described as a simple web-based tool for processing images and converting documents with a simple interface, and the repository is a Go service with a web front end, since the primary language in the metadata is HTML and the server is written in Go.

The notes at the end of the readme are the part that describes the design rather than the features. There is an isolated processing environment, there is no file storage and the delivery is immediate, there is automatic cleanup, and there is input validation.

That is a coherent position for a converter. A tool that turns whatever a user uploads into another format is, by definition, handling files it does not know anything about, and a service that keeps a copy of them is a service that accumulates liability. Converting in a temporary area and deleting the result means the deployment does not become a document store, and the isolated environment means a malformed file that upsets an image library takes down a request rather than the service.

The feature list is short and marked honestly. The file converter is done, with a note to keep adding formats, and dark mode is done. The API and background removal for images are not done, and they are shown as unchecked boxes rather than as upcoming features.

The project is MIT licensed, the last push was on 2026-07-26, and there are no GitHub releases, so the container image on the registry is the artefact to depend on.

The compatibility matrix is the documentation

Most conversion tools publish a list of the formats they claim to support. Reubah publishes a matrix, which is a different and more useful thing.

The guide at the top explains how to read it, find your source format in the left column, follow the row, a check means the conversion is supported and a dash means it is the same format and no conversion is needed.

The image matrix runs from JPG and JPEG, PNG, WebP, GIF, BMP and HEIC and HEIF to PDF, and the shape of it is informative. Every one of the six raster formats converts to PDF, and every one of them converts to each other, with one exception that stands out: HEIC and HEIF is never a target, so a check mark appears in the HEIC column and a cross in the HEIC row. Converting into HEIC is the one direction the tool does not do.

The document matrix is where the caveat is. PDF as a source converts to DOCX and to DOC, but not to ODT, RTF or TXT, and the label on that row says in plain words that PDF to PDF currently still bad. DOCX, DOC, ODT, RTF and TXT all convert to each other and to PDF, so the weak point is the PDF as an input, which is what you would expect from a document format that stores a page description rather than a text flow.

An additional table covers the image features, and it is the table that tells you what the tool will not do. Background removal is marked as coming soon and is a cross in every row. Optimization is a check only for JPG and JPEG. Batch processing is a check for every format, which is the feature that matters if you have a folder of images rather than one file.

Two deployment routes and one port

There are two ways to run it, and both use the image published to the GitHub container registry.

The compose route is the one the readme shows first, and it is three commands:

bash
git clone https://github.com/dendianugerah/reubah.git
cd reubah
docker-compose up -d

The single container route is the one to copy when you have an orchestrator that expects a command rather than a file:

bash
docker run -d --name reubah -p 8081:8081 -v doc-temp:/tmp -e PORT=8081 --restart unless-stopped ghcr.io/dendianugerah/reubah:latest

The port is 8081 on the host, the environment variable sets the port inside the container, and the named volume mounted at /tmp is the scratch area the service works in. The application is then on http://localhost:8081.

The compose file is short and does the interesting work in three lines. The service runs with the no-new-privileges security option, and it drops all capabilities. For a container that parses files uploaded by strangers, dropping the capabilities means the process cannot do the things a container process normally can, such as change its own network configuration or load a kernel module, and no new privileges means a setuid binary inside the image cannot gain more than it started with.

The restart policy is unless-stopped in both files, so the service comes back after a reboot of the host.

The image runs as a non-root user with a health check

The Dockerfile is a two stage build, and the runtime stage is where the interesting decisions are.

The builder is golang:1.22-alpine with the packages the Go libraries need to compile, which are the C toolchain, the WebP development files, the HEIF libraries, the x265 and libde265 codecs, Node.js and npm for the front end templates, and bash. The go.mod and go.sum are copied before the source so that the dependency download is cached and only invalidated when the dependencies change, which is the standard trick and the difference between a two second rebuild and a two minute one.

The front end is built with npm inside the templates directory, and then the binary is compiled with CGO enabled, which is necessary because the WebP and HEIF libraries are C libraries.

The runtime stage is alpine 3.19 with a smaller set of packages, and the interesting ones are LibreOffice, the Java runtime that LibreOffice needs, the Liberation fonts, and the WebP and HEIF libraries again. Three directories are created for LibreOffice to keep its configuration in, because the service does not run as root and the default locations are not writable by it.

Then the user is created. A group with id 1000 and a user with id 1000 are added, the application directory and those three temporary directories are given to it, and the container switches to that user. The home environment variable is pointed at /tmp so that LibreOffice writes its profile somewhere writable. The port is exposed, the health check runs a curl against the root path every thirty seconds with a three second timeout, and the entry point is the binary itself.

So the final image is a non-root container with no capabilities, and that is the right shape for a service that exists to open files it was given.

Six libraries, one per format family

The go.mod is the clearest statement of how the conversions are implemented, because each dependency corresponds to a format.

chai2010/webp at 1.4.0 is the WebP encoder and decoder, and it is a C library, which is why the build needs the C toolchain and why CGO is enabled. The HEIC support comes in two packages, MaestroError/go-libheif at 0.3.0 as the direct dependency and strukturag/libheif at 1.16.2 as the underlying C library it wraps. disintegration/imaging at 1.6.2 handles the general image work, and golang.org/x/image at 0.18.0 adds the Go standard library's imaging formats around it.

PDF is jung-kurt/gofpdf at 1.16.2, which is the pure Go PDF generator, so the PDF output does not need another native dependency. The web server is gorilla/mux at 1.8.1, the standard router for a Go service that does not need a framework.

The module targets Go 1.22.1, which matches the golang 1.22 alpine image in the builder stage, and the repository tree is organised the usual way for a Go service, with cmd holding the server entry point, internal for the private packages, pkg for the reusable ones, plus scripts, static for the assets, templates for the front end and tmp for the working area.

The absence of a PDF reading library is the interesting omission, and it explains the matrix. A converter that can write PDF but not read it can turn a document into a PDF and turn a PDF into a Word file by handing the file to LibreOffice, but it cannot re-render a PDF as a PDF without a second engine, and that is the row the readme marks as still bad.

Local development needs Go, LibreOffice and a C toolchain

Running the service from a checkout is two commands, and the requirements are three.

You need Go 1.22 or newer, LibreOffice, which is what does the document conversion, and a C compiler with C++ because the image libraries are native.

bash
go mod download
go run cmd/server/main.go

The first command fetches the modules, and the second starts the server straight from the source with the entry point in the cmd directory, which is the layout the Docker build uses as well, with the build step compiling ./cmd/server into a binary called reubah.

The two requirements that surprise people are the C toolchain and LibreOffice. The first is easy to explain, because the WebP and HEIF libraries are C and the Go bindings need cgo, so a machine without a compiler cannot build the project at all. The second is the reason the runtime image is large, because LibreOffice is a suite rather than a library, and it needs a Java runtime and the Liberation fonts to render documents the way people expect.

The split between the two stages in the Dockerfile is what keeps that cost out of the build image, and it is also what a local checkout does not give you: running from source means LibreOffice has to be installed on your machine, in the right version, with the fonts it needs, and the behaviour you see locally will differ from the container in exactly the ways that a document renderer differs between installations.

Editorial conclusion

Reubah fits a team that needs to convert a document or a batch of images on its own infrastructure and does not want to upload them anywhere, because the service runs from one container, keeps nothing on disk and cleans up after itself, and the compatibility matrix tells you in advance which conversions will work. It is the wrong choice if you need a full office suite in a browser, because the document side depends on LibreOffice being installed in the image, which brings a Java runtime and a large dependency set with it, and it is the wrong choice if you need the API, which is still an unchecked box in the feature list. Before you deploy it, read the matrix for the exact pairs you care about, and note that the container runs with no added capabilities and as a non-root user, which is the right shape for a service that processes untrusted files.

Frequently asked questions

What is Reubah?

It is an MIT licensed Go service with a web interface that converts images and documents, with dark mode and a per format compatibility matrix published in the readme. It stores no files, delivers results immediately and cleans up after itself, and it runs in a container as a non-root user with no added capabilities.

Which image formats can Reubah convert?

JPG and JPEG, PNG, WebP, GIF, BMP and HEIC and HEIF convert between each other and to PDF, but HEIC and HEIF is never a target format. Batch processing is supported for every image format, while optimisation is only available for JPG and JPEG, and background removal is listed as coming soon.

Does Reubah convert PDF to PDF reliably?

Not according to its own matrix, which labels the PDF row as still bad. PDF converts to DOCX and to DOC, but not to ODT, RTF or TXT, and the PDF output is produced by a pure Go PDF library rather than by re-rendering the document.

How do I run Reubah?

With Docker Compose, cloning the repository and running docker-compose up -d, or with a single container that publishes port 8081, mounts a temporary volume at /tmp and sets the PORT variable. The image is published at ghcr.io/dendianugerah/reubah, and running it from source needs Go 1.22, LibreOffice and a C compiler.

Official sources

  1. dendianugerah/reubah on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dendianugerah-reubah.svg)](https://hysenlabs.com/projects/dendianugerah-reubah)

Was this useful?