Case study · Self-hosted invoicing and quotes
Mavri: Self-hosted invoicing and quotes
Free, open-source invoice and quote software you run on your own server: one Go container, one SQLite file, a REST API, signed webhooks and UBL and XRechnung export.
- My role
- Lead developer. I wrote the server, the interface, the API, the installer scripts and the documentation site.
- Licence
- AGPL-3.0

Context
Mavri is invoicing software that stays on your own server. A small business creates quotes and invoices, converts an accepted quote into an invoice, records payments and reads billed, paid, overdue and outstanding amounts from a dashboard, without opening a hosted billing account.
It is free, open source under AGPL-3.0, and ships as one Docker container with one SQLite data folder. Installers cover macOS, Linux and Windows through WSL2.
Problem
Hosted billing tools are easy to start but keep your records inside someone else's service. Self-hosted alternatives often ask for a web server, an API service, a database server and a queue before the first invoice exists.
Users
Freelancers and small businesses who want to own their invoicing data, and developers who want to connect a store or a script to billing through an API.
Operational workflow
Install with one command, create a business profile, add clients and catalog items, write a quote, convert it to an invoice, record the payment and download a backup. Stores can create invoices automatically through signed webhooks.
Engineering challenge
Billing history must not change when reusable records change, an integration must survive retried deliveries, and a rewrite must not strand the records people already have.
Architecture
One Go server serves the React interface and the versioned REST API from the same origin, with SQLite in a persistent data volume. There is no separate API host, queue or database server to operate. The reasoning, and what it does not solve, is in why Mavri serves the web app and API from one container.
Data model decisions
A document keeps a snapshot of the business, customer, line items, currency, tax choices and style used when it was made, so editing a client or a catalog price later does not rewrite an invoice already sent. Why an invoice should keep a snapshot explains the boundary and its cost.
Backend
The Go API exposes versioned REST endpoints, signed store webhooks and an MCP server. Store webhooks are verified with an HMAC-SHA256 signature and made idempotent on the store connection plus the external order ID, so a retry never creates a second invoice. See webhook retries are normal; duplicate invoices are a design bug.
Frontend
A React interface covers businesses, clients, products, quotes, invoices, payments, reports, fifteen document templates and tax guidance. Documents export as UBL 2.1 and XRechnung.
Security
There is no analytics or tracking. Webhooks are signed per connection, the API is versioned, and records stay in the deployment the owner controls. Exported electronic formats are not presented as proof of legal compliance.
Infrastructure
A single container of roughly 54 MB. Quick-start scripts install Docker where it is missing, and the Windows script sets up WSL2 with a real Docker Engine instead of Docker Desktop. A CasaOS install is documented too.
Reliability considerations
The data is one SQLite file plus the data folder, so a backup is a download from Settings or a copy of the folder. Restoring from a backup is part of the documented routine, not an afterthought.
Difficult tradeoffs
SQLite fits a single installation serving a small-business workflow; it is not a promise of unlimited concurrent writes or multi-region replication. I would split the deployment only for a measured reason such as contention or an independently scaled worker.
What I personally worked on
Lead developer across the server, the interface, the API, the installers and the documentation site.
Lessons learned
Moving to the Go and SQLite rewrite meant planning the exit from the old database: a one-time converter that leaves the old file untouched and says plainly that accounts and store connections are not copied. The story is in rewriting an invoicing app means planning the exit from the old database.
Technologies
- Go
- SQLite
- React
- TypeScript
- Docker
Screenshots
Captured from running instances.
Mavri: frequently asked questions
What is Mavri?
Mavri is free, open-source invoicing and quote software that runs on your own server as one Docker container with a SQLite data folder.
Does Mavri cost anything?
No. It is free under the AGPL-3.0 licence, with no paid tiers or feature gating.
Can it export electronic invoices?
Yes. It exports UBL 2.1 and XRechnung. An export format alone is not proof of legal compliance, so check local rules.
Can I connect a store or script?
Yes. Mavri has a versioned REST API, signed webhooks that are safe to retry, and an MCP server.
How do I back it up or move from an older version?
Download a backup from Settings or copy the data folder. Earlier installations use the one-time mavri-legacy converter, which does not copy accounts or store connections.



