# Ledgerix — Cloud / Linux deployment > Officiële handleiding voor het zelf draaien van Ledgerix op een > Linux-server, eigen cloud-VPS (Hetzner / AWS / Azure / Scaleway / > DigitalOcean), of een Kubernetes-cluster. Ledgerix biedt geen > managed SaaS — klanten hosten zelf. **Inhoudsopgave** 1. [Welke aanpak past bij mij?](#welke-aanpak-past-bij-mij) 2. [Snel beginnen — Docker single-host](#snel-beginnen--docker-single-host) 3. [Docker Compose met reverse-proxy + TLS](#docker-compose-met-reverse-proxy--tls) 4. [Native Linux install (systemd, geen Docker)](#native-linux-install-systemd-geen-docker) 5. [Reverse-proxy keuzes — Caddy / Traefik / nginx](#reverse-proxy-keuzes--caddy--traefik--nginx) 6. [Configuratie via environment-variabelen](#configuratie-via-environment-variabelen) 7. [Backups en restore](#backups-en-restore) 8. [Upgrades — naar een nieuwe versie](#upgrades--naar-een-nieuwe-versie) 9. [Health-check en monitoring](#health-check-en-monitoring) 10. [Security-checklist productie](#security-checklist-productie) 11. [OCR (OCRix) als sidecar](#ocr-ocrix-als-sidecar) --- ## Welke aanpak past bij mij? | Doel | Aanbevolen aanpak | |------|-------------------| | Lokaal testen op je laptop | Docker — `docker compose up`, klaar in 2 minuten | | Productie op één VPS | Docker Compose + Caddy (automatische TLS) | | Productie op meerdere hosts / Kubernetes | Het Docker-image deployen via je eigen manifest | | Linux-server zonder Docker | systemd-service met het Linux-binary | | Windows-server | De Inno Setup `Ledgerix-Setup-X.Y.Z.exe` installer (geen Docker nodig) | Voor alle Linux-aanpakken geldt: Ledgerix is een **stateless single binary** met een **SQLite-database in één directory**. Backup = die directory. Upgrade = nieuw binary / nieuw image starten; migraties draaien automatisch bij boot. --- ## Snel beginnen — Docker single-host Vereist: Docker Engine 20.10+ en `docker compose` (v2). Er zijn twee wegen. **A** (aanbevolen) gebruikt het officiële, kant-en-klare image van de downloadpagina — daar zit de licentie-portaal-pubkey al in gebakken, dus je hoeft alleen je licentiesleutel in te voeren. **B** bouwt zelf vanuit de broncode; dat is het geavanceerde pad en vergt dat je de pubkey zelf meegeeft (zie de sectie **Licentie**). ### A. Officieel image gebruiken (aanbevolen) Download de cloud-bundle van — het bestand `ledgerix-cloud-.tar.gz`. Die bevat het Docker-image (mét ingebakken pubkey), een `docker-compose.yml` en de reverse-proxy-configs. ```bash # 1. Uitpakken en het image laden (geen registry/rebuild nodig) tar xzf ledgerix-cloud-.tar.gz cd ledgerix-cloud- docker load < ledgerix-image-.tar # 2. (optioneel) admin-account vooraf scripten i.p.v. de setup-wizard # echo "ADMIN_PASSWORD=KiesEenSterkInitieelWachtwoord!" > .env # chmod 600 .env # 3. Start docker compose up -d docker compose logs -f ledgerix ``` Daarna activeer je je licentie in de UI via **Instellingen → Licentie** met de LXLIC-sleutel die je bij je licentie hebt gekregen. ### B. Zelf bouwen vanuit de broncode (geavanceerd) ```bash # 1. Clone de repo git clone https://github.com/CodingKaspers/ledgerix.git cd ledgerix # 2. Bouw het image. Voor licentie-activatie moet je de portal-pubkey # meebakken (zie de sectie Licentie); zonder pubkey draait de app wel # maar faalt activatie (tenzij je LEDGERIX_DEV_MODE=1 gebruikt). docker build --build-arg LEDGERIX_LICENSE_PUBKEY_BUILTIN= -t ledgerix:latest . # 3. Start (docker-compose.yml in de repo bouwt/gebruikt ledgerix:latest) docker compose up -d docker compose logs -f ledgerix ``` Browse naar `http://127.0.0.1:8080`. Bij de eerste keer verschijnt een **setup-wizard** waarin je het beheerdersaccount (gebruiker + wachtwoord) aanmaakt. Zette je een `ADMIN_PASSWORD`, dan slaat de wizard over en log je in met `admin` + dat wachtwoord (wijzig het daarna via **Gebruikers**). > Default `docker-compose.yml` bindt port 8080 op `127.0.0.1` — niet > publiek bereikbaar. Voor productie zet je er een reverse-proxy voor; > zie volgende sectie. --- ## Docker Compose met reverse-proxy + TLS Voor publieke toegang heb je TLS nodig en wil je niet de Rust-server zelf laten praten met internet. **Caddy** is de eenvoudigste keuze — automatische Let's Encrypt out-of-the-box. ```bash # Installeer Caddy op de host (Debian/Ubuntu) sudo apt install caddy # Plaats de meegeleverde Caddyfile sudo cp deploy/caddy/Caddyfile /etc/caddy/Caddyfile # Bewerk: vervang `ledgerix.example.com` door je echte domein sudo nano /etc/caddy/Caddyfile # Reload sudo systemctl reload caddy ``` Zorg dat: - Je DNS A-record naar de host wijst (vóór de eerste reload — Caddy vraagt direct een certificaat aan). - Poort 80 + 443 publiek bereikbaar zijn (ACME-challenge + TLS-traffic). - De Ledgerix-container op `127.0.0.1:8080` luistert zoals in `docker-compose.yml` ingesteld (default is dit zo). Browse naar `https://ledgerix.example.com` — TLS werkt vanaf nu automatisch en certs vernieuwen via Caddy zonder verdere actie. Alternatieven: zie [reverse-proxy keuzes](#reverse-proxy-keuzes--caddy--traefik--nginx). --- ## Native Linux install (systemd, geen Docker) Voor klanten die geen Docker willen of een minder-resource-intensieve install zoeken. ```bash # 1. Bouw het binary op een Linux-machine (of build-server) # Voor licentie-activatie: bak de portal-pubkey in met de env-var # LEDGERIX_LICENSE_PUBKEY_BUILTIN= vóór cargo build, of geef # 'm op runtime mee via LEDGERIX_LICENSE_PUBKEY (zie sectie Licentie). cargo build --release --bin ledgerix # Resultaat: target/release/ledgerix # 2. Plaats op de doelserver sudo useradd --system --home /opt/ledgerix --shell /usr/sbin/nologin ledgerix sudo mkdir -p /opt/ledgerix/data sudo cp target/release/ledgerix /opt/ledgerix/ sudo cp -r crates/ledgerix-web/static /opt/ledgerix/ sudo cp -r docs /opt/ledgerix/ sudo chown -R ledgerix:ledgerix /opt/ledgerix # 3. Installeer de systemd-unit sudo cp deploy/systemd/ledgerix.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now ledgerix # 4. Check sudo systemctl status ledgerix sudo journalctl -u ledgerix -f ``` De unit-file in [`deploy/systemd/ledgerix.service`](../deploy/systemd/ledgerix.service) bevat strakke sandboxing-defaults (NoNewPrivileges, ProtectSystem=strict, SystemCallFilter, MemoryMax=2G). Pas ze aan voor je specifieke setup indien nodig. Reverse-proxy: zelfde stap als Docker-aanpak. Caddy/Traefik/nginx proxy naar `127.0.0.1:8080`. --- ## Reverse-proxy keuzes — Caddy / Traefik / nginx Alle drie werken; kies wat je al draait of het minst werk geeft. | Optie | Voor wie | Voorbeeld-config | |-------|----------|------------------| | **Caddy** | Single-host, zero-effort TLS | [`deploy/caddy/Caddyfile`](../deploy/caddy/Caddyfile) | | **Traefik** | Multi-service Docker-setup, label-driven | [`deploy/traefik/ledgerix.yml`](../deploy/traefik/ledgerix.yml) | | **nginx** | Bestaande nginx-infra, certbot voor TLS | [`deploy/nginx/ledgerix.conf`](../deploy/nginx/ledgerix.conf) | Alle drie configs zetten security-headers (HSTS, X-Content-Type-Options, Referrer-Policy) en een body-size-limit van 25M voor OCR + Peppol-XML (Caddy `request_body max_size`, nginx `client_max_body_size`, Traefik de `ledgerix-bodylimit`-middleware `buffering.maxRequestBodyBytes`). Ledgerix stuurt z'n eigen CSP server-side; laat de proxy die niet overschrijven. --- ## Configuratie via environment-variabelen Ledgerix is volledig env-driven. Wat de installer/systemd-unit zet kun je in een Docker-context ook gewoon via `-e` of de compose `environment:` sectie meegeven. | Variabele | Default | Doel | |-----------|---------|------| | `DATABASE_URL` | `sqlite:./data/ledgerix.db` | Pad naar de SQLite-file. Voor Docker: `sqlite:/data/ledgerix.db`. | | `PORT` | `8080` | HTTP-poort waarop Ledgerix luistert | | `LEDGERIX_BIND` | `127.0.0.1` | Bind-interface. **In Docker zet je 'm op `0.0.0.0`** zodat de container van buiten bereikbaar is; op een bare-metal install hou je 'm op `127.0.0.1` mét reverse-proxy ervoor. | | `STATIC_DIR` | auto-detect | Map met `static/`-frontend | | `DOCS_DIR` | auto-detect | Map met `docs/`-markdown | | `ADMIN_PASSWORD` | _(leeg)_ | Optioneel. Leeg = setup-wizard maakt het eerste account aan. Gezet = admin-account wordt bij de allereerste boot met dit wachtwoord aangemaakt (daarna genegeerd). | | `LEDGERIX_CORS_ORIGINS` | _(leeg)_ | Komma-gescheiden toegestane origins voor cross-origin requests, of `*` voor de oude permissieve modus. Leeg = alleen same-origin/localhost. | | `LEDGERIX_SINGLE_USER` | _(uit)_ | `1` = single-user kiosk-modus (auto-login, geen loginscherm). Niet gebruiken op een multi-user of internet-facing install. | | `RUST_LOG` | `info` | Tracing-level: `error` / `warn` / `info` / `debug` / `trace` | | `LEDGERIX_LICENSE_PUBKEY` | _(leeg)_ | Licentie-portaal-pubkey op runtime. Nodig om een licentie te kunnen activeren als de pubkey niet is ingebakken bij de build (zie **Licentie** hieronder). Alternatief voor de build-arg, zonder rebuild. | | `LEDGERIX_DEV_MODE` | _(uit)_ | `1` = omzeilt de licentiecontrole volledig (alle features open). **Alleen voor test/dev**, nooit op een productie-install. | > **Compile-time variant:** `LEDGERIX_LICENSE_PUBKEY_BUILTIN` is geen > runtime-env maar een **build-arg** die de pubkey in de binary bakt (zie > Dockerfile en de **Licentie**-sectie). Ofwel bak je 'm in bij de build, > ofwel geef je `LEDGERIX_LICENSE_PUBKEY` op runtime mee — één van beide is > nodig voor licentie-activatie. Alle andere applicatie-instellingen (SMTP, OCR-engine, backup-schedule, KvK-API-key, OAuth-providers) staan in de `settings`-tabel in de database en zijn via **Instellingen** in de UI te beheren. Géén losse config-file om bij te houden. --- ## Licentie Ledgerix controleert bij het activeren van een licentie de handtekening van die licentie tegen de **publieke sleutel van het licentie-portaal**. Die pubkey moet in de build/omgeving zitten, anders faalt activatie met `error.license.portal_pubkey_unset`. **In het normale geval hoef je hier niets voor te doen.** De officiële distributies (de Windows-installer én het cloud-image uit de downloadpagina, pad **A** hierboven) hebben de pubkey al ingebakken. Licentie-activatie is dan simpelweg: je LXLIC-sleutel invoeren via **Instellingen → Licentie**. De pubkey reist dus mee met de uitgifte, precies zoals verwacht. De pubkey wordt alleen een aandachtspunt als je **zelf vanuit de broncode bouwt** (pad **B**), want `keys/` staat bewust niet in de repo (die map bevat óók de geheime signing-key). Dan heb je één van deze opties: 1. **Ingebakken bij de build**: geef de pubkey mee als build-arg. ```bash docker build --build-arg LEDGERIX_LICENSE_PUBKEY_BUILTIN= -t ledgerix:latest . ``` In `docker-compose.yml` kan dit via `LEDGERIX_LICENSE_PUBKEY_BUILTIN` in je `.env` (de compose-`build.args` pikt 'm op). 2. **Op runtime**: zet `LEDGERIX_LICENSE_PUBKEY=` als omgevingsvariabele. Geen rebuild nodig. 3. **Zonder licentie (test/evaluatie)**: `LEDGERIX_DEV_MODE=1` omzeilt de licentiecontrole en zet alle features open. Niet voor productie. > De pubkey is een *publieke* sleutel (geen geheim). Bouw je zelf en heb je > de bijbehorende portal-pubkey nodig, vraag 'm op bij je licentie-uitgifte. --- ## Backups en restore **Wat moet ik back-uppen?** Alles staat in één directory: `/data` (Docker) of `/opt/ledgerix/data` (systemd). Daarin: - `ledgerix.db` — de live database - `ledgerix.db-wal` + `ledgerix.db-shm` — WAL-bestanden (mogen mee in backup) - `backups/` — door Ledgerix zelf gegenereerde backups als je dat in Instellingen hebt aangezet **Drie aanpakken:** 1. **Cloud-volume snapshot** — als je VPS-aanbieder volume-snapshots ondersteunt (Hetzner, AWS EBS, Azure Disk), is een nightly snapshot de eenvoudigste oplossing. Crash-consistency is OK voor SQLite met WAL. 2. **In-app backup** — Activeer in Instellingen → Backup. Schrijft een AES-256-GCM versleutelde `.db.enc` naar `/data/backups/`. Stuur die via `restic` of `rclone` naar S3/B2/etc. 3. **Cold `tar`** — Voor een vluchtige snapshot vanuit een cron-script: ```bash docker run --rm \ -v ledgerix-data:/d \ -v /var/backups/ledgerix:/out \ busybox sh -c "tar czf /out/ledgerix-$(date +%F).tar.gz /d" ``` **Restore:** ```bash # Stop Ledgerix docker compose stop ledgerix # of: sudo systemctl stop ledgerix # Plaats de oude db terug docker run --rm -v ledgerix-data:/d -v /pad/naar/backup:/in busybox \ cp /in/ledgerix.db /d/ledgerix.db # Start docker compose start ledgerix ``` Sinds v0.76.0 kun je een backup ook **als scenario** importeren via de **Backup & herstel**-pagina — risico-vrij rondkijken in oude data zonder je live admin te raken. --- ## Upgrades — naar een nieuwe versie Ledgerix is **stateless**: alle persistente staat zit in de data-directory. Migraties draaien automatisch bij boot (`include_str!` in de storage-crate; idempotent). **Docker — officieel image (pad A):** ```bash # Nieuwe cloud-bundle downloaden, image laden, herstarten docker load < ledgerix-image-.tar docker compose up -d ledgerix ``` **Docker — zelf gebouwd (pad B):** ```bash # Bouw de nieuwe versie (docker-compose.yml gebruikt standaard het lokaal # gebouwde ledgerix:latest); geef zo nodig de licentie-pubkey mee. docker build -t ledgerix:latest . docker compose up -d ledgerix ``` **Systemd:** ```bash sudo systemctl stop ledgerix sudo cp target/release/ledgerix /opt/ledgerix/ledgerix sudo cp -r crates/ledgerix-web/static/* /opt/ledgerix/static/ sudo systemctl start ledgerix ``` **Hou de release-notes bij:** [`CHANGELOG.md`](../CHANGELOG.md). De in-app version-banner waarschuwt sinds v0.11.2 als er een nieuwe release is. **Rollback** is simpel: deploy het vorige binary / image. Een schema- migratie is altijd `CREATE TABLE IF NOT EXISTS` / `ALTER TABLE ADD COLUMN` — bestaande data blijft compatibel met de oudere release. Bewaar wel altijd een pre-upgrade backup als verzekering. --- ## Health-check en monitoring **Health-check endpoint:** ``` GET /api/v1/settings/version → 200 OK { "current": "0.95.41" } ``` Geen auth nodig. Docker `HEALTHCHECK` uit het meegeleverde image gebruikt 'm al; eigen orchestrators (Kubernetes liveness/readiness, HAProxy, Pingdom) kunnen 'm direct pingen. **Tracing-logs** komen op stdout via de `tracing`-crate. Log-niveau stel je in met `RUST_LOG` (`error`/`warn`/`info`/`debug`/`trace`). Gestructureerde JSON-logs richting Loki/Datadog/CloudWatch zijn nog niet ingebouwd; gebruik tot die tijd de JSON-logdriver van je container-runtime (`docker logs`, of `--log-driver` naar je collector). **Systeem-monitor in de app** — `/monitor.html` (admin-only) toont proces-RAM, systeem-RAM, uptime, DB-grootte en laatste backup-status. Voor externe monitoring is dit niet vervangend, wel een snelle sanity- check vanuit de browser. --- ## Security-checklist productie Voor je publiekelijk live gaat: - [ ] **Admin-wachtwoord gewijzigd** vanaf de default (eerste login) - [ ] **2FA actief** voor minimaal alle Admin-accounts (Gebruikers → 2FA) - [ ] **TLS via reverse-proxy** — geen plain HTTP op publiek IP - [ ] **`LEDGERIX_BIND=0.0.0.0` alleen in Docker** (intern netwerk); op bare metal blijft `127.0.0.1` + proxy de standaard - [ ] **Firewall**: alleen poort 80 + 443 publiek, poort 8080 dicht - [ ] **Volume-permissies**: `/data` alleen leesbaar voor de ledgerix-user (UID 10001 in het Docker-image) - [ ] **Backup-strategie aan** — getest met een restore - [ ] **Auto-lock geactiveerd** (Instellingen → Beveiliging) als Ledgerix op een gedeelde machine staat (accountantskantoor) - [ ] **Backup-encryptie wachtwoord** ingesteld als je backups naar een derde-partij-cloud (S3/B2) stuurt - [ ] **Audit-log review-routine** ingericht (wekelijks via de Audit Trail-pagina of CSV-export) - [ ] **Rate-limiters** staan aan default (`LoginRateLimiter` 5/15 min, `HeavyOpRateLimiter` 20/5 min) — wijzig niet zonder gegronde reden Ledgerix's eigen permissies-systeem (groep-gebaseerd RBAC + per- endpoint permissions sinds v0.70) is de primaire verdedigingslinie ná de proxy. Hou per-account toegang minimaal: een boekhouder hoeft geen Admin te zijn. --- ## OCR (OCRix) als sidecar OCR is **optioneel**. Zonder ocrix-service werkt Ledgerix prima — alleen de bon-scan-flow in `/eenvoudig.html` valt terug op handmatige invoer. **Opstelling 1: OCRix uit, alleen Tesseract op de host.** Installeer `tesseract-ocr` + `tesseract-ocr-nld` op de host, zet in Instellingen → OCR `ocr_engine = tesseract`. Voor Docker: voeg de packages toe aan het Dockerfile (komt niet default mee — Tesseract voegt ~50 MB image-grootte toe). **Opstelling 2: OCRix als sidecar-container.** Clone https://github.com/CodingKaspers/ocrix naast deze repo en bouw 'm tot een eigen image (`ocrix:latest`). Uncomment de `ocrix:` block in `docker-compose.yml`. In Ledgerix-instellingen: `ocr_engine = ocrix`, `ocrix_service_url = http://ocrix:8081` (de Docker-DNS-naam van de sidecar). **Opstelling 3: OCR helemaal uit.** Default. Niets doen. De Mailbox-inbox accepteert dan alleen PDFs waar pdf-extract de tekst-laag uit haalt; gescande JPEG-bonnen worden niet auto-uitgelezen maar kunnen wel handmatig geboekt worden.