feat: Add Rybbit (open-source web analytics) with HA replicas #107

Closed
opened 2026-09-25 15:06:26 +00:00 by znibb · 3 comments
Owner

Overview

Deploy Rybbit — open-source (AGPL-3.0), cookie-less and privacy-friendly alternative to Google Analytics — in the cluster, exposed via Traefik (optionally reachable outside the LAN via Newt). Goal is HA: the stateless app tier runs with 2 replicas each (backend + client), backed by shared Postgres/ClickHouse/Redis.

This issue is for research + planning; implementation happens in follow-up PR(s) once the decisions below are made.

Upstream architecture (from docker-compose.yml / Caddyfile)

Component Image Role Port
backend ghcr.io/rybbit-io/rybbit-backend Fastify/Node API, event ingestion, tracker script 3001
client ghcr.io/rybbit-io/rybbit-client Next.js dashboard 3002
clickhouse clickhouse/clickhouse-server:26.3.17.4 Event/analytics store 8123
postgres postgres:17.4 Metadata, auth 5432
redis redis:8.6.4-alpine Sessions, rate-limit store, caches 6379
caddy caddy:2.10.0 (optional with-webserver profile) TLS + reverse proxy 80/443

We replace Caddy with Traefik. Upstream routing to replicate:

  • /api/* → backend:3001
  • /.well-known/oauth-* and /.well-known/openid-configuration* → backend:3001
  • everything else → client:3002

Latest upstream release at time of writing: v2.9.0 — pin image tags instead of latest.

HA assessment

Safe to scale:

  • client (Next.js) and backend (Fastify) are stateless HTTP servers — no websockets (no @fastify/websocket / socket.io in server/package.json).
  • Tracking sessions live in Redis (session:<siteId>:<fingerprint>, 30 min sliding TTL) rather than in-process, so ingestion can be load-balanced across backend pods.
  • Rate limiting uses a shared Redis store, so limits hold across replicas.

Gotchas:

  • Cron jobs run on the backend "primary" process per pod (server/src/cluster.ts): telemetry, usage check, unclaimed-site cleanup (weekly reports / lifecycle e-mails are cloud-only, i.e. irrelevant with DEPLOYMENT=self-hosted). With N backend pods these run N times — verify idempotency or serialize (leader election / separate single-replica deployment).
  • Pageview batching is in-memory per pod (pageviewQueue.ts: flush every 1 s / 5000 events). A crash loses up to ~1 s of events — mitigate with graceful shutdown (preStop sleep + terminationGracePeriodSeconds).
  • DB init/migrations run at container start (initializeClickhouse() + initPostgres()). Verify two replicas starting concurrently don't race — likely CREATE ... IF NOT EXISTS, but should be tested.
  • Real client IP / geolocation depends on proxy headers (X-Forwarded-For / X-Real-IP, plus CF-Connecting-IP handling in resolveClientIp.ts). Verify Traefik forwards XFF correctly so geo data isn't wrong.
  • Postgres/ClickHouse/Redis stay single-replica (same pattern as Immich's Postgres/Redis here). ClickHouse HA (keeper/replica) is significant extra work — out of scope for v1, note as accepted limitation.

Proposed deployment

kubernetes/apps/rybbit/
  namespace.yaml
  kustomization.yaml
  ingressroute.yaml        # Traefik: /api + /.well-known → backend, / → client
  secret.enc.yaml          # SOPS: PG/CH/Redis passwords, BETTER_AUTH_SECRET
  postgres/                # Deployment + PVC (nfs-csi) + Service — Immich pattern
  clickhouse/              # Deployment + PVC + ConfigMap (network/logging/resource limits) + Service
  redis/                   # Deployment + PVC + Service
  backend/                 # Deployment (replicas: 2, PDB, anti-affinity) + Service
  client/                  # Deployment (replicas: 2, PDB, anti-affinity) + Service
  • Add rybbit to kubernetes/apps/kustomization.yaml.
  • Hostname: analytics.kobbo.se (or stats.kobbo.se?) via Traefik IngressRoute, reachable through the Newt tunnel if wanted.
  • replicas: 2 for backend/client with podAntiAffinity + topology spread and PodDisruptionBudget (minAvailable: 1); works with allowSchedulingOnControlPlanes.
  • ClickHouse storage: local-path vs nfs-csi — ClickHouse on NFS can be flaky (fsync/latency), so consider local-path pinned to one controlplane node.

Open questions / research

  • HA scope: is 2× client/backend + single Postgres/Redis/ClickHouse enough for v1?
  • Cron duplication with >1 backend pod: idempotent, leader election, or CLUSTER_WORKERS tuning?
  • ClickHouse on nfs-csi vs local-path (durability vs performance; node pinning)
  • Backups: pg_dump + ClickHouse BACKUP to S3/MinIO on a CronJob (upstream has ops/backups; cf. krona backup issue #77)
  • Session replay storage: S3-compatible backend (MinIO in cluster?) via @aws-sdk/client-s3
  • MAPBOX_TOKEN / maps feature — needed or leave disabled?
  • DISABLE_SIGNUP=true, BASE_URL, DEPLOYMENT=self-hosted, LITE_DASHBOARD env choices
  • Resource sizing (ClickHouse needs real memory; CLUSTER_WORKERS vs K8s replicas duplication)
  • Migrations as an initContainer/Job, or leave to app startup?

Acceptance criteria

  • Rybbit reachable at chosen hostname over TLS, DISABLE_SIGNUP=true after admin created
  • 2× backend + 2× client, spread across nodes, PDBs in place
  • Traefik routing parity with upstream Caddy (/api, /.well-known, SPA)
  • Tracking snippet from a test site ingests events (real-time dashboard shows data, correct geo from real client IP)
  • Rolling restart / node drain loses no more than the in-memory flush window and keeps sessions alive
  • Postgres + ClickHouse backups scheduled and restore tested
  • docs/rybbit.md added and namespace/app listed in kustomization

References

## Overview Deploy [Rybbit](https://github.com/rybbit-io/rybbit) — open-source (AGPL-3.0), cookie-less and privacy-friendly alternative to Google Analytics — in the cluster, exposed via Traefik (optionally reachable outside the LAN via Newt). Goal is HA: the stateless app tier runs with **2 replicas each** (backend + client), backed by shared Postgres/ClickHouse/Redis. This issue is for research + planning; implementation happens in follow-up PR(s) once the decisions below are made. ## Upstream architecture (from `docker-compose.yml` / `Caddyfile`) | Component | Image | Role | Port | |---|---|---|---| | `backend` | `ghcr.io/rybbit-io/rybbit-backend` | Fastify/Node API, event ingestion, tracker script | 3001 | | `client` | `ghcr.io/rybbit-io/rybbit-client` | Next.js dashboard | 3002 | | `clickhouse` | `clickhouse/clickhouse-server:26.3.17.4` | Event/analytics store | 8123 | | `postgres` | `postgres:17.4` | Metadata, auth | 5432 | | `redis` | `redis:8.6.4-alpine` | Sessions, rate-limit store, caches | 6379 | | `caddy` | `caddy:2.10.0` (optional `with-webserver` profile) | TLS + reverse proxy | 80/443 | We replace Caddy with Traefik. Upstream routing to replicate: - `/api/*` → backend:3001 - `/.well-known/oauth-*` and `/.well-known/openid-configuration*` → backend:3001 - everything else → client:3002 Latest upstream release at time of writing: **v2.9.0** — pin image tags instead of `latest`. ## HA assessment Safe to scale: - `client` (Next.js) and `backend` (Fastify) are stateless HTTP servers — **no websockets** (no `@fastify/websocket` / socket.io in `server/package.json`). - Tracking sessions live in **Redis** (`session:<siteId>:<fingerprint>`, 30 min sliding TTL) rather than in-process, so ingestion can be load-balanced across backend pods. - Rate limiting uses a shared Redis store, so limits hold across replicas. Gotchas: - **Cron jobs run on the backend "primary" process per pod** (`server/src/cluster.ts`): telemetry, usage check, unclaimed-site cleanup (weekly reports / lifecycle e-mails are cloud-only, i.e. irrelevant with `DEPLOYMENT=self-hosted`). With N backend pods these run N times — verify idempotency or serialize (leader election / separate single-replica deployment). - **Pageview batching is in-memory per pod** (`pageviewQueue.ts`: flush every 1 s / 5000 events). A crash loses up to ~1 s of events — mitigate with graceful shutdown (`preStop` sleep + `terminationGracePeriodSeconds`). - **DB init/migrations run at container start** (`initializeClickhouse()` + `initPostgres()`). Verify two replicas starting concurrently don't race — likely `CREATE ... IF NOT EXISTS`, but should be tested. - **Real client IP / geolocation** depends on proxy headers (`X-Forwarded-For` / `X-Real-IP`, plus `CF-Connecting-IP` handling in `resolveClientIp.ts`). Verify Traefik forwards XFF correctly so geo data isn't wrong. - **Postgres/ClickHouse/Redis stay single-replica** (same pattern as Immich's Postgres/Redis here). ClickHouse HA (keeper/replica) is significant extra work — out of scope for v1, note as accepted limitation. ## Proposed deployment ``` kubernetes/apps/rybbit/ namespace.yaml kustomization.yaml ingressroute.yaml # Traefik: /api + /.well-known → backend, / → client secret.enc.yaml # SOPS: PG/CH/Redis passwords, BETTER_AUTH_SECRET postgres/ # Deployment + PVC (nfs-csi) + Service — Immich pattern clickhouse/ # Deployment + PVC + ConfigMap (network/logging/resource limits) + Service redis/ # Deployment + PVC + Service backend/ # Deployment (replicas: 2, PDB, anti-affinity) + Service client/ # Deployment (replicas: 2, PDB, anti-affinity) + Service ``` - Add `rybbit` to `kubernetes/apps/kustomization.yaml`. - Hostname: `analytics.kobbo.se` (or `stats.kobbo.se`?) via Traefik `IngressRoute`, reachable through the Newt tunnel if wanted. - `replicas: 2` for backend/client with `podAntiAffinity` + topology spread and `PodDisruptionBudget` (`minAvailable: 1`); works with `allowSchedulingOnControlPlanes`. - ClickHouse storage: `local-path` vs `nfs-csi` — ClickHouse on NFS can be flaky (fsync/latency), so consider `local-path` pinned to one controlplane node. ## Open questions / research - [ ] HA scope: is 2× client/backend + single Postgres/Redis/ClickHouse enough for v1? - [ ] Cron duplication with >1 backend pod: idempotent, leader election, or `CLUSTER_WORKERS` tuning? - [ ] ClickHouse on `nfs-csi` vs `local-path` (durability vs performance; node pinning) - [ ] Backups: `pg_dump` + ClickHouse `BACKUP` to S3/MinIO on a CronJob (upstream has `ops/backups`; cf. krona backup issue #77) - [ ] Session replay storage: S3-compatible backend (MinIO in cluster?) via `@aws-sdk/client-s3` - [ ] `MAPBOX_TOKEN` / maps feature — needed or leave disabled? - [ ] `DISABLE_SIGNUP=true`, `BASE_URL`, `DEPLOYMENT=self-hosted`, `LITE_DASHBOARD` env choices - [ ] Resource sizing (ClickHouse needs real memory; `CLUSTER_WORKERS` vs K8s replicas duplication) - [ ] Migrations as an `initContainer`/Job, or leave to app startup? ## Acceptance criteria - [ ] Rybbit reachable at chosen hostname over TLS, `DISABLE_SIGNUP=true` after admin created - [ ] 2× backend + 2× client, spread across nodes, PDBs in place - [ ] Traefik routing parity with upstream Caddy (`/api`, `/.well-known`, SPA) - [ ] Tracking snippet from a test site ingests events (real-time dashboard shows data, correct geo from real client IP) - [ ] Rolling restart / node drain loses no more than the in-memory flush window and keeps sessions alive - [ ] Postgres + ClickHouse backups scheduled and restore tested - [ ] `docs/rybbit.md` added and namespace/app listed in kustomization ## References - Rybbit repo: https://github.com/rybbit-io/rybbit - Self-hosting docs: https://rybbit.com/docs/self-hosting - Upstream files examined: `docker-compose.yml`, `Caddyfile`, `server/src/cluster.ts`, `server/src/services/tracker/pageviewQueue.ts`, `server/src/services/sessions/sessionsService.ts`
Author
Owner

Status: research + beslut klara, implementationen ligger i PR #108 (feature/rybbit → develop). Backuper och Kiln-trackern är kvar som separata uppföljningar.

kiln.kobbo.se-analysen (det du ville börja med)

Hur det exponeras idag: *.kobbo.se är ett wildcard i DNS → Pangolin-VPS:en (51.38.141.159), som utfärdar Let's Encrypt-cert per hostname och kör newt-tunneln in till in-cluster Traefik (10.8.40.200). Därifrån tar IngressRoute Host(kiln.kobbo.se) → kiln-app:3000.

Upptäckt: kiln.kobbo.se svarar 200 direkt, utan Pangolin-SSO — medan pi., hermes. och n8n. 302:ar till pangolin.kobbo.se/auth/resource/<id>. Det betyder att notisen i znibb/lovable/docs/deploy.md om att Pangolin blockerar /preview/* (och kräver en Always-allow-regel) inte längre stämmer. Antagligen avsiktligt, men värt att bekräfta — om Kiln ska vara SSO-skyddat är det en egen Pangolin-inställning, inte något Rybbit rör.

Vad det betyder för Rybbit + Kiln: att mäta kiln.kobbo.se kräver ingen ändring i Rybbit-deployen. Trackern laddas i besökarens webbläsare och postar cross-origin till analytics.kobbo.se (Rybbit är cookie-less, så Pangolins SameSite=Lax-cookie är irrelevant). Det som krävs:

  1. Tracker-endpoints är publika. Rybbit har egen auth, så enklast är att inte SSO-gata analytics.kobbo.se. Vill man ändå det (som pi./n8n.) måste Always-allow-regler läggas för /api/script.js, /api/track, /api/site/tracking-config/*, /.well-known/* m.fl. — full lista i docs/rybbit.md.
  2. X-Forwarded-* från Pangolin når backend oförändrat, annars blir geodata fel. Traefik rensar idag alla X-forwarded-headers från newt-podden → PR #108 sätter entryPoints.web.forwardedHeaders.trustedIPs=10.244.0.0/16.
  3. En snippet i Kiln-appen (znibb/lovable, src/app/layout.tsx) — egen liten PR i app-repot, blockerar inte Rybbit-deployen. data-site-id skapas i Rybbits UI, så det kan ändå inte ligga i GitOps.

Behöver beslutet tas i detta steg? Nej. Hostnamn och SSO-val är billiga att ändra senare: DNS-wildcardet finns, klienten använder relativa /api-URL:er (upstream-imagen bakas med tom NEXT_PUBLIC_BACKEND_URL) och backendens BASE_URL är en runtime-env. Det som behöver bestämmas innan deploy är bara (a) hostnamn + Pangolin-resource och (b) SSO av/på — jag lade PR:en på analytics.kobbo.se utan SSO som default, säg till om du vill ha stats. eller SSO i stället.

Övriga beslut (se PR #108 / docs/rybbit.md)

Fråga Beslut
HA-scope 2× backend/client + singel Postgres/Redis/ClickHouse (v1)
Cron >1 podd CLUSTER_WORKERS=0 + DISABLE_TELEMETRY=true; usage-cron är cloud-only; site-baseline Redis-vald → inga dubbelkörningar
Migrationer Entrypoint-migreringar + migrate-initContainer med retry (drizzle-kit tar inget advisory lock)
ClickHouse-lagring nfs-csi (issue #8), NFS-latens accepterad i v1
Klient-IP Traefik trustedIPs (se ovan)
Session replay ClickHouse även self-hosted; ingen S3/MinIO behövs
MAPBOX / LITE_DASHBOARD av i v1
Klientimage upstream-digest funkar tack vare relativa /api-URL:er
Env DISABLE_SIGNUP=true (admin bootstrappas med ett tillfälligt kubectl set env … =false), DISABLE_TELEMETRY=true, CLUSTER_WORKERS=0, BASE_URL=https://analytics.kobbo.se

Kvar

  • Backuper: pg_dump + ClickHouse BACKUP — förslag: delad pipeline enligt #7/#77.
  • Kiln-snippet i znibb/lovable.
  • Pangolin-resource analytics.kobbo.se → 10.8.40.200:80 skapas manuellt (DNS finns redan).
  • Traefik-ändringen appliceras med bash kubernetes/core/traefik/helmrelease.sh (Helm CLI).
Status: research + beslut klara, implementationen ligger i PR #108 (feature/rybbit → develop). Backuper och Kiln-trackern är kvar som separata uppföljningar. ## kiln.kobbo.se-analysen (det du ville börja med) **Hur det exponeras idag:** `*.kobbo.se` är ett wildcard i DNS → Pangolin-VPS:en (51.38.141.159), som utfärdar Let's Encrypt-cert per hostname och kör newt-tunneln in till in-cluster Traefik (10.8.40.200). Därifrån tar `IngressRoute` `Host(kiln.kobbo.se)` → `kiln-app:3000`. **Upptäckt:** `kiln.kobbo.se` svarar **200 direkt, utan Pangolin-SSO** — medan `pi.`, `hermes.` och `n8n.` 302:ar till `pangolin.kobbo.se/auth/resource/<id>`. Det betyder att notisen i `znibb/lovable/docs/deploy.md` om att Pangolin blockerar `/preview/*` (och kräver en Always-allow-regel) inte längre stämmer. Antagligen avsiktligt, men värt att bekräfta — om Kiln ska vara SSO-skyddat är det en egen Pangolin-inställning, inte något Rybbit rör. **Vad det betyder för Rybbit + Kiln:** att mäta kiln.kobbo.se kräver **ingen** ändring i Rybbit-deployen. Trackern laddas i besökarens webbläsare och postar cross-origin till `analytics.kobbo.se` (Rybbit är cookie-less, så Pangolins `SameSite=Lax`-cookie är irrelevant). Det som krävs: 1. Tracker-endpoints är publika. Rybbit har egen auth, så enklast är att inte SSO-gata `analytics.kobbo.se`. Vill man ändå det (som pi./n8n.) måste Always-allow-regler läggas för `/api/script.js`, `/api/track`, `/api/site/tracking-config/*`, `/.well-known/*` m.fl. — full lista i `docs/rybbit.md`. 2. `X-Forwarded-*` från Pangolin når backend oförändrat, annars blir geodata fel. Traefik rensar idag alla X-forwarded-headers från newt-podden → PR #108 sätter `entryPoints.web.forwardedHeaders.trustedIPs=10.244.0.0/16`. 3. En snippet i Kiln-appen (`znibb/lovable`, `src/app/layout.tsx`) — egen liten PR i app-repot, **blockerar inte** Rybbit-deployen. `data-site-id` skapas i Rybbits UI, så det kan ändå inte ligga i GitOps. **Behöver beslutet tas i detta steg?** Nej. Hostnamn och SSO-val är billiga att ändra senare: DNS-wildcardet finns, klienten använder relativa `/api`-URL:er (upstream-imagen bakas med tom `NEXT_PUBLIC_BACKEND_URL`) och backendens `BASE_URL` är en runtime-env. Det som behöver bestämmas innan deploy är bara (a) hostnamn + Pangolin-resource och (b) SSO av/på — jag lade PR:en på `analytics.kobbo.se` utan SSO som default, säg till om du vill ha `stats.` eller SSO i stället. ## Övriga beslut (se PR #108 / docs/rybbit.md) | Fråga | Beslut | | --- | --- | | HA-scope | 2× backend/client + singel Postgres/Redis/ClickHouse (v1) | | Cron >1 podd | `CLUSTER_WORKERS=0` + `DISABLE_TELEMETRY=true`; usage-cron är cloud-only; site-baseline Redis-vald → inga dubbelkörningar | | Migrationer | Entrypoint-migreringar + `migrate`-initContainer med retry (drizzle-kit tar inget advisory lock) | | ClickHouse-lagring | `nfs-csi` (issue #8), NFS-latens accepterad i v1 | | Klient-IP | Traefik trustedIPs (se ovan) | | Session replay | ClickHouse även self-hosted; ingen S3/MinIO behövs | | MAPBOX / LITE_DASHBOARD | av i v1 | | Klientimage | upstream-digest funkar tack vare relativa `/api`-URL:er | | Env | `DISABLE_SIGNUP=true` (admin bootstrappas med ett tillfälligt `kubectl set env … =false`), `DISABLE_TELEMETRY=true`, `CLUSTER_WORKERS=0`, `BASE_URL=https://analytics.kobbo.se` | ## Kvar - Backuper: `pg_dump` + ClickHouse `BACKUP` — förslag: delad pipeline enligt #7/#77. - Kiln-snippet i `znibb/lovable`. - Pangolin-resource `analytics.kobbo.se` → `10.8.40.200:80` skapas manuellt (DNS finns redan). - Traefik-ändringen appliceras med `bash kubernetes/core/traefik/helmrelease.sh` (Helm CLI).
Author
Owner

Smoke-test i klustret (pre-merge) ✅

Pangolin-resourcen verifierad: analytics.kobbo.se har Let's Encrypt-cert och trafiken når in-cluster Traefik.

Jag applicerade appen med scripts/rybbit-apply.sh (ny, samma pre-merge-mönster som pi-devbox-apply.sh). Inget är mergat, så Flux äger den inte än.

Status:

  • backend 2/2, client 2/2, ClickHouse/Postgres/Redis 1/1, båda PDB:erna aktiva.
  • https://analytics.kobbo.se/ 200, /api/health 200, /api/script.js 200, /api/track nås (endast POST).
  • Postgres/ClickHouse/Redis uppkopplade; backend loggar ClickHouse query user provisioned.
  • Migrerings-racet inträffade på riktigt under första starten (pg_type_typname_nsp_index-krock mellan två pods) och retry-loopen löste det utan krasch — precis vad initContainern är till för.

Geo/XFF verifierat med en tillfällig echo-tjänst bakom Traefik:

  • Före fixen: x-real-ip: 10.244.0.0 (newt-podden), x-forwarded-proto: http.
  • Efter entryPoints.web.forwardedHeaders.trustedIPs=10.244.0.0/16 + helm upgrade (Traefik rullade utan downtime, maxUnavailable: 0): x-real-ip: <publik klient-IP>, x-forwarded-proto: https, x-forwarded-port: 443.
  • Andra appar påverkades inte (kiln:s 503 under tiden var dess egen CI-rollout; stabil 200 efter).

Just nu: DISABLE_SIGNUP=true i både kluster och git (säkert default). Admin-kontot bootstrappas genom att tillfälligt sätta DISABLE_SIGNUP=false, skapa kontot i UI:t och sätta tillbaka — dokumenterat i docs/rybbit.md.

Kvar: merge av PR #108 (develop → main) så Flux tar över, skapa admin-kontot, Kiln-snippet och backuper.

## Smoke-test i klustret (pre-merge) ✅ Pangolin-resourcen verifierad: `analytics.kobbo.se` har Let's Encrypt-cert och trafiken når in-cluster Traefik. Jag applicerade appen med `scripts/rybbit-apply.sh` (ny, samma pre-merge-mönster som `pi-devbox-apply.sh`). Inget är mergat, så Flux äger den inte än. **Status:** - backend 2/2, client 2/2, ClickHouse/Postgres/Redis 1/1, båda PDB:erna aktiva. - `https://analytics.kobbo.se/` 200, `/api/health` 200, `/api/script.js` 200, `/api/track` nås (endast POST). - Postgres/ClickHouse/Redis uppkopplade; backend loggar `ClickHouse query user provisioned`. - Migrerings-racet inträffade på riktigt under första starten (`pg_type_typname_nsp_index`-krock mellan två pods) och retry-loopen löste det utan krasch — precis vad initContainern är till för. **Geo/XFF** verifierat med en tillfällig echo-tjänst bakom Traefik: - Före fixen: `x-real-ip: 10.244.0.0` (newt-podden), `x-forwarded-proto: http`. - Efter `entryPoints.web.forwardedHeaders.trustedIPs=10.244.0.0/16` + `helm upgrade` (Traefik rullade utan downtime, `maxUnavailable: 0`): `x-real-ip: <publik klient-IP>`, `x-forwarded-proto: https`, `x-forwarded-port: 443`. - Andra appar påverkades inte (kiln:s 503 under tiden var dess egen CI-rollout; stabil 200 efter). **Just nu:** `DISABLE_SIGNUP=true` i både kluster och git (säkert default). Admin-kontot bootstrappas genom att tillfälligt sätta `DISABLE_SIGNUP=false`, skapa kontot i UI:t och sätta tillbaka — dokumenterat i `docs/rybbit.md`. **Kvar:** merge av PR #108 (develop → main) så Flux tar över, skapa admin-kontot, Kiln-snippet och backuper.
Author
Owner

Implementerad av mergad PR #108 (backend/client 2 replikor, PDB:er, smoke-testad i klustret). Backuper spåras redan i #7/#77 och Kiln-trackern är en kvarvarande uppföljning. Stänger.

Implementerad av mergad PR #108 (backend/client 2 replikor, PDB:er, smoke-testad i klustret). Backuper spåras redan i #7/#77 och Kiln-trackern är en kvarvarande uppföljning. Stänger.
znibb closed this issue 2026-09-28 12:06:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
znibb/kobbo-homelab#107
No description provided.