Install with Docker Compose
The whole stack (database, backend, web app, and TLS) comes up from a single Docker Compose file. Nothing else to install on the host, and no build step: the compose pulls prebuilt images published by CI.
What you need
Section titled “What you need”- Docker with the Compose plugin (Docker Engine 24+ or Docker Desktop).
- For a real deployment: a domain name pointed at your host, with ports 80 and 443 reachable from the internet (TLS certificates are issued and renewed automatically). For a quick look on localhost, none of that.
- Optional, only if you want the integrations: an Anthropic or OpenAI key for AI features (or point it at a local Ollama), a Resend key for outgoing email, Stripe keys for card payments.
Quick start
Section titled “Quick start”Clone the repo, then from its root:
cp .env.example .envOpen .env and set the secrets. Each one is a single command to generate:
| Variable | What it’s for | Generate with |
|---|---|---|
THALERMARK_DOMAIN |
Your public hostname (or localhost to try it out) |
|
BETTER_AUTH_SECRET |
Signs login sessions | openssl rand -base64 32 |
POSTGRES_PASSWORD |
Database admin password | openssl rand -hex 32 |
THALERMARK_APP_PASSWORD |
Password for the app’s own database login | openssl rand -hex 32 |
THALERMARK_PGBOSS_PASSWORD |
Password for the background-job runner’s database login | openssl rand -hex 32 |
STORAGE_URL_SECRET |
Signs receipt download links | openssl rand -hex 32 |
Then bring it up:
docker compose --env-file .env -f docker/docker-compose.yml up -dOpen https://your-domain (or https://localhost; the browser warns once
about the local certificate, accept and proceed). Sign up: the first signup
creates your account and sets up your business.
Turning on the extras
Section titled “Turning on the extras”Everything optional is off-but-safe until you configure it:
- AI (receipt reading, categorization, insights) is set up in the app,
not in
.env: sign in and open Settings → AI, pick a provider (Anthropic, OpenAI, a local Ollama, or any compatible endpoint), paste a key, and click Verify. Verify tests the text model and the vision model separately, and if a local model on modest hardware needs longer to answer, the connection’s Advanced timeout (30 to 300 seconds) gives it the room. - Email: set
RESEND_API_KEYso invoices actually reach customers. Without it, outgoing mail is only logged to the console. - Card payments: set the three
STRIPE_*keys. If your install serves businesses other than your own, also setSTRIPE_REQUIRE_CONNECTED_ACCOUNT=trueso each business’s money settles into its own Stripe account, never yours.
The boot log tells you what came up:
docker compose -f docker/docker-compose.yml logs api | grep -E "transport|storage|Stripe|enabled|disabled"Once you’re signed in, Settings → About shows the build the server is actually running, and quietly notes when the api is older than the web app was built to expect, so you can confirm an upgrade really landed.
Backups, upgrades, and going deeper
Section titled “Backups, upgrades, and going deeper”The stack includes an automatic daily database backup out of the box. For backup rotation, restores, upgrades, object storage, managed Postgres, running behind your own proxy, and the full configuration reference, see DEPLOYMENT.md in the repo: the complete operator’s guide.