# Rachoon is an invoicing app you host yourself, PDF engine included

> Rachoon is a self-hosted invoicing platform for freelancers and small businesses, built as a Nuxt front end on an AdonisJS API with PostgreSQL behind it, and it produces its PDFs through a separate Gotenberg service rather than by rendering them in the application. The deployment is three containers and one environment variable you have to generate yourself.

**ad-on-is/rachoon** — 🦝 Rachoon — A self-hostable way to handle invoices

- Repository: https://github.com/ad-on-is/rachoon
- Stars: 979 · Forks: 73
- Language: TypeScript
- License: AGPL-3.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/ad-on-is-rachoon

## What the application does

Rachoon is described as a modern, self-hosted invoicing platform for freelancers, small businesses and anyone who wants full control over their billing, and the name comes from the Bosnian word for invoice. The feature list is short and concrete. Invoices and offers, which in practice means invoices and quotations, can be created and managed. Clients are kept in one searchable place. Payment status can be logged, balances viewed and overdue invoices tracked. Templates are customisable through nunjucks, which is the templating engine the PDF rendering runs on, so the branding of a document is a template rather than a hardcoded page. There is multi-currency and tax support for billing across currencies, a PDF export that produces the finished document, and a dashboard giving a snapshot of revenue, pending payments and client statistics. The first run of the application is a signup page, so the deployment is multi-tenant in the sense that the first account creates the tenant.

## Three containers, and the values you have to change

The documented installation is a Compose file with three services, and the application service is the part worth reading carefully:

```yaml
  rachoon:
    image: ghcr.io/ad-on-is/rachoon
    container_name: rachoon
    environment:
      - APP_KEY=<some-app-key> # min 32 characters - used to encrypt and sign sensitive data
      - DB_CONNECTION=pg
      - GOTENBERG_URL=http://gotenberg:3000
      - PG_HOST=postgres16
      - PG_PORT=5432
      - PG_USER=<root-user>
      - PG_PASSWORD=<root-password>
      - PG_DB_NAME=rachoon
    ports:
      - 8080:8080
```

The other two services are the PDF engine at `gotenberg/gotenberg:8` and the database at `postgres:16`, and the database service mounts a `./rachoon-data` directory for the data and a `./docker/init-db.sh` script into the container's init directory, which is how the schema is set up on first start. The application is reachable on port 8080, and the first step after starting is to visit `http://localhost:8080/signup` and create an account. One detail deserves attention: the compose file committed in the repository, as opposed to the one printed in the documentation, contains literal values for the app key and the database user and password. Those are placeholder credentials in a public repository, and they have to be replaced before the stack is exposed to anything.

## Gotenberg is a service, not a library

The PDF export is produced by a separate container rather than by code inside the application, and the application finds it through the `GOTENBERG_URL` variable. That separation is the interesting architectural decision in the deployment, because it means the HTML to PDF conversion happens in a service whose image carries its own rendering stack. The application image itself confirms this, since the runtime stage installs graphicsmagick and ghostscript alongside caddy and dcron, and it copies a `Caddyfile` and an `entrypoint.sh` into the working directory before starting from that entrypoint. So the same container serves the web interface, delegates document rendering to Gotenberg over the compose network, and schedules work with dcron. The consequence for an operator is that the application container is not stateless in the usual sense, since the image carries a web server, a scheduler and two image processing libraries, and the PDF quality depends on the Gotenberg image version, which is pinned at 8 in the compose file.

## The application key is the one value you must generate

Of the environment variables, `APP_KEY` is the one with a security role rather than a configuration role, and the documentation says in a comment that it must be at least 32 characters and is used to encrypt and sign sensitive data. That is the value that protects whatever the application stores about clients and invoices, so it belongs in your secret store and not in a file that ends up in a repository. `DB_CONNECTION` is set to `pg` to select the PostgreSQL driver, and the `PG_` group points the application at the database container, with the host name matching the service name in the compose file because that is how they resolve on the compose network. The name of the database is `rachoon` and it lives inside a PostgreSQL instance whose own initial database is `postgres`, with the `init-db.sh` script creating the rest. Nothing in the documentation describes a migration command or a backup procedure, so those are the two things to work out yourself before you put real invoices into it.

## A Turborepo monorepo with three build outputs

The source tree is a monorepo managed by Turborepo, and the documentation names the build artefacts for each part, which is the fastest way to understand the layout. The Nuxt front end builds into `.output/`, the AdonisJS backend builds into `build/`, and the shared packages build into `dist/`. The structure is two applications and two packages under a root `turbo.json`:

```text
rachoon/
├── apps/
│   ├── backend/     # AdonisJS API server
│   └── frontend/    # Nuxt.js web application
├── packages/
│   ├── common/      # Shared code
│   └── typescript-config/
└── turbo.json       # Turborepo configuration
```

The three commands for a developer are the usual ones:

```bash
pnpm install
pnpm build
pnpm dev
```

The root manifest is private, named `rachoon-turbo`, pins pnpm as the package manager and requires Node 18 or later, and it injects the workspace packages into the dependency tree so that the front end and the back end resolve the shared code from the workspace rather than from the registry. Formatting is prettier over the TypeScript and Markdown files and linting is delegated to each workspace through turbo. The repository carries both a `pnpm-lock.yaml` and a `bun.lock`, and since the declared package manager is pnpm, the pnpm lock file is the one to trust.

## Tests and the pipeline that runs on every pull request

The testing setup is described as comprehensive, and the shape of it is the usual for an AdonisJS application. The backend uses japa as the test runner, and the documentation gives the command as a directory change and a run:

```bash
cd apps/backend
pnpm test
```

The test structure is layered, with unit tests covering the models and services, integration tests covering the API endpoints, and database seeding for the test data. The pipeline that runs on every pull request and every push has four stages, and the list is short enough to be worth knowing, because it tells you what a contributor has to satisfy. Code quality is checked with linting and formatting. The tests run against a PostgreSQL test database. The build is verified. And the Docker image is created, but only for the main branch, so a pull request is not billed with an image build. The contributing guide is where the detail lives, and the quick start in the README is the familiar sequence of forking, branching, changing, adding tests for new features, making sure the tests pass and opening a pull request.

## Licence and release state

Rachoon is under the AGPL-3.0, and for a self-hosted invoicing tool that is a decision with consequences rather than a formality. The network clause of that licence is what matters here, because the application is used over a network by definition, and the copyleft obligation extends to modified versions offered that way. Anyone who forks it, changes the templates or the invoice logic, and then serves the result to their own users has obligations the README does not discuss, and this review does not interpret the licence text. Beyond that, the repository keeps a `CHANGES.md`, a `CODE_OF_CONDUCT.md` and a `CONTRIBUTING.md` alongside the `LICENSE` file. On versions, the release list shows v1.3.9 and v1.3.8 both published on 2026-05-17, and the last push to the repository was on 2026-05-31, so the project is small and moving, with releases still on the 1.x line.

## Conclusion

Rachoon suits a freelancer or small business that wants its own invoicing system under its own control, with its own branding on the documents and its own database, and it suits a developer who wants a readable example of a Turborepo monorepo with a Nuxt front end and an AdonisJS API. It is a poor fit if you do not want to run three services, since the application, the PDF renderer and the database each get a container, and it is a poor fit if you want to change the app and share the change, because the AGPL-3.0 licence attaches obligations to a network use of the software. Before you deploy it, check three things: that you replace the literal credentials that appear in the committed compose file, that you generate an `APP_KEY` of at least 32 characters, since it is used to encrypt and sign sensitive data, and that your reverse proxy and TLS plan matches the Caddy based entry point the image ships with.

## FAQ

### How do I deploy Rachoon?

With the Compose file from the documentation, which runs three services: the application image ghcr.io/ad-on-is/rachoon on port 8080, a Gotenberg 8 container for PDF rendering, and PostgreSQL 16 with the data directory and the init-db.sh script mounted into it. Then open http://localhost:8080/signup and create the first account.

### What is APP_KEY used for in Rachoon?

It is used to encrypt and sign sensitive data, and the documentation notes it must be at least 32 characters. The compose file committed in the repository contains a literal placeholder value, so you need to replace it with a value you generate yourself before exposing the stack.

### How does Rachoon generate PDF invoices?

Through a separate Gotenberg service rather than by rendering inside the application, with the application reaching it through the GOTENBERG_URL variable. The application image itself installs graphicsmagick and ghostscript, caddy as the web server and dcron for scheduling.

### How do I develop Rachoon locally?

The project is a Turborepo monorepo with pnpm as the package manager and Node 18 or later. Run `pnpm install`, then `pnpm build` for a production build or `pnpm dev` for development. The Nuxt front end builds into .output, the AdonisJS backend into build, and the shared packages into dist.

### What licence is Rachoon under?

AGPL-3.0, with the text in the LICENSE file at the root of the repository. The release list shows v1.3.9 and v1.3.8, both published on 2026-05-17, and the last push to the repository was on 2026-05-31.

## Sources

- [ad-on-is/rachoon on GitHub](https://github.com/ad-on-is/rachoon)
- [Issues](https://github.com/ad-on-is/rachoon/issues)
- [License: AGPL-3.0](https://github.com/ad-on-is/rachoon/blob/main/LICENSE)
- [README](https://github.com/ad-on-is/rachoon/blob/main/README.md)
- [Releases](https://github.com/ad-on-is/rachoon/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ad-on-is-rachoon
