# Invoicerr: the self-hosted invoicing engine that keeps country rules in data

> Invoicerr is an open-source invoicing application for freelancers and small studios that run their own stack on Docker and Postgres. It covers the document lifecycle, payment reconciliation and national e-invoicing rules, but the compliance catalogue only covers five countries today.

**invoicerr-app/invoicerr** — Invoicerr is a freelance-focused invoicing app that lets you create quotes, generate invoices, track payments, and collect secure signatures.

- Repository: https://github.com/invoicerr-app/invoicerr
- Website: https://docs.invoicerr.app/
- Stars: 731 · Forks: 62
- Language: TypeScript
- License: AGPL-3.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/invoicerr-app-invoicerr

## Quotes, invoices and the paperwork that follows

Invoicerr is an open-source invoicing application that a freelancer or a small studio runs on its own infrastructure rather than on someone else's subscription. The project description calls it invoicing you host yourself, and the scope reaches further than the name suggests: quotes, invoices, credit notes, purchase orders, goods receipts, expenses and received invoices all live in one document engine with one lifecycle and one list screen per type. That breadth answers a concrete problem for a solo consultant who today keeps a quote in one tool, an invoice in a second and a receipt in a mailbox. The stack is TypeScript, with repository topics naming NestJS, Prisma, React, PDF handling and signatures, and the licence is AGPL-3.0. Self-hosting here means the installer owns the database, the retention rules and the archived files.

## Settlement badges are computed from the record, never stored on the document

The document model draws a line that matters for accountants: payment status is derived, not written. Payments, partial payments, deposits, instalments and settlement badges are computed from the record, so a document's state always follows from the underlying payment rows instead of from a flag somebody has to remember to flip. The same care shows up on the purchasing side, where a received invoice is uploaded, read by OCR into an editable proposal and then approved or rejected, and where credit notes correct an invoice line by line rather than being drafted freehand. The price of this choice is that the state lives outside the document table, and the README does not describe how derived settlement is recomputed after a large bank import. An accountant who expects a stored paid column to query directly will not find one.

## A country is a catalogue of sourced facts, not a code branch

Treating a country as data is the sharpest idea in this codebase. Which actions a document allows, which identifiers a party must supply, which correction route applies, which VAT rates exist, which channel and format a buyer requires, and how long an archive must be kept are each a catalogue of sourced facts, with the legal text attached to every entry. Germany, France, Italy, Poland and Portugal ship with their catalogues filled in. Transmission paths then differ by country instead of by conditional in the code: PDP for French business to business through an accredited platform, Chorus Pro for French public buyers, KSeF for Polish clearance, SdI for Italy over a web service and over certified e-mail, and plain e-mail with PDF plus attached e-invoice XML for anywhere else. Output formats cover Factur-X, UBL, CII, XRechnung, Peppol BIS, FatturaPA and FA(3). The country support matrix in the documentation is generated from those data files on every documentation build, so the published matrix cannot drift from the code.

## Bank statement import turns a match into a real payment record

Getting paid is handled as reconciliation rather than as a manual receipt form. A statement can be imported as CSV or OFX, and the application matches it by reference and amount to propose a payment; once a person confirms the suggested match, a genuine payment record exists, and that record is what feeds the computed settlement state. Around that sit client account statements with an aged balance, a client portal where the client sees their own documents, balance, PDFs, payment and quote decisions, and quote signing through a one-time code sent to the signer. Payment methods offered to clients carry their own details onto the document. The dependency to keep in mind is that reconciliation assumes statement references and amounts line up with what Invoicerr wrote on the invoice, so a bank that rewrites reference strings turns an automatic match into a manual queue.

## Deploying the prebuilt image with Compose and Postgres

Installation starts from a prebuilt container image published at ghcr.io/invoicerr-app/invoicerr, built for linux/amd64 and linux/arm64. Thirty-two bit ARM is no longer built, because Node.js stopped publishing linux-armv7l binaries starting with Node 24. The compose file wires that image to Postgres and to a named volume that holds the archived documents.

```yaml
services:
  invoicerr:
    image: ghcr.io/invoicerr-app/invoicerr:latest
    ports:
      - "80:80"
    volumes:
      - documents_data:/data
```

The backend is pointed at Postgres through one environment variable whose password comes from a .env file kept beside the compose file.

```yaml
      - DATABASE_URL=postgresql://invoicerr:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in a .env file next to docker-compose.yml, see .env.example}@invoicerr_db:5432/invoicerr_db
```

Secrets have to exist before the stack starts. The backend refuses to boot in production without BETTER_AUTH_SECRET, and Compose refuses even earlier, because that value signs every session cookie and JWT.

```bash
BETTER_AUTH_SECRET=
POSTGRES_PASSWORD=
```

Both values are meant to be generated with a fresh run of openssl rand -hex 32, and the Postgres password applies only when the volume is created, so an upgrade keeps the older value unless it is changed on purpose. Mail is set at instance level with one provider, and a company may override it in its own settings, which always wins for that company. Building from source instead means a backend and a frontend stage, each installing its own lockfile, with the runtime stage taken from a separate base image.

```dockerfile
FROM --platform=$BUILDPLATFORM node:24-bookworm AS backend-builder
WORKDIR /app
COPY backend/package.json backend/package-lock.json ./
RUN npm ci
RUN DATABASE_URL=not_required npx prisma generate --schema=prisma/schema.prisma
RUN npm run build
```

## Three sign-in shapes and a wide machine facing surface

Access arrives in three forms and they are worth separating. People sign in with e-mail and password, with OIDC or SSO configured either instance-wide or per company, or with an API key. Layered on top are role-based access across multiple companies, webhooks, a REST API, an MCP server and a plugin system, plus 18 interface languages with a per-recipient document language and multi-currency support that keeps rate history. Installing it as a PWA covers the field case of a freelancer working from a phone on a client site. The extension surfaces are the part to check before committing, because the README lists a plugin system and an MCP server as capabilities without describing a plugin manifest, a discovery format or a version policy, so treat them as an area to verify rather than a settled interface.

## Where the archive volume and the catalogue rule Invoicerr out

There are clear cases where this is the wrong tool. A business billing in a country outside the five with filled catalogues will find the compliance promise thin, because adding a country means authoring a new catalogue of sourced facts rather than toggling a setting. A team with no Docker or Postgres experience will find the setup heavier than a hosted accounting product, because the archive path, the signing certificates for PAdES signed PDFs, the encrypted backup bucket and the per-company channel credentials all belong to the operator. The archive volume is deliberately not optional in the compose file: legal archives and received invoices live in the mounted path, and a named volume survives an image pull but not a volume deletion. Clients also get their own portal, so the instance becomes a customer-facing system rather than an internal tool.

## Conclusion

Invoicerr fits freelancers and small studios that want to own their invoices, client portal and legal archives, and it is a poor fit for a team whose billing country has no filled catalogue. Before adopting it, check that the country catalogue for your billing country is filled in, that your bank statement exports can be matched by reference and amount, and that the arm64 image with its Postgres volume fits your host. Read the AGPL-3.0 terms against how you plan to expose the instance to clients.

## FAQ

### What does Invoicerr need before it can start?

The backend needs Postgres and a BETTER_AUTH_SECRET, which the compose file refuses to start without in production, plus a Postgres password stored in a .env file beside the compose file. A mail provider and APP_URL are also expected, since APP_URL is used for e-mail template redirects and as the base URL for the authentication library.

### Which countries does Invoicerr support for e-invoicing?

Germany, France, Italy, Poland and Portugal ship with their country catalogues filled in, covering document formats such as Factur-X, UBL, CII, XRechnung, Peppol BIS, FatturaPA and FA(3). Transmission channels include PDP and Chorus Pro for France, KSeF for Poland, SdI for Italy over a web service and over certified e-mail, and e-mail with PDF plus attached XML for any country.

### Can Invoicerr run on a 32-bit ARM device?

No image is built for linux/arm/v7 any more, because Node.js stopped publishing linux-armv7l binaries starting with Node 24. The published image targets linux/amd64 and linux/arm64.

### Is Invoicerr still being developed?

The last push was on 2026-10-22, and three alpha releases of the 2.0 line appeared in the preceding month, with v2.0.0-alpha.5 published on 2026-10-12. The repository is not archived.

## Sources

- [invoicerr-app/invoicerr on GitHub](https://github.com/invoicerr-app/invoicerr)
- [License: AGPL-3.0](https://github.com/invoicerr-app/invoicerr/blob/dev/LICENSE)
- [Project website](https://docs.invoicerr.app/)
- [README](https://github.com/invoicerr-app/invoicerr/blob/dev/README.md)
- [Releases](https://github.com/invoicerr-app/invoicerr/releases)

---

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