Viktor asked to self-host the DJI flight-log analyzer for his DJI Mini 4 Pro (his fork ViktorBarzin/drone-logbook -> upstream arpanghosh8453/open-dronelog). Upstream ghcr image with Keel auto-upgrade, DuckDB data on an encrypted proxmox-lvm PVC (GPS traces = sensitive), NFS /sync-logs drop folder imported every 8h, daily backup CronJob to /srv/nfs/drone-logbook-backup (vaultwarden pattern), Authentik-gated ingress, PROFILE_CREATION_PASS from Vault via ESO. Design + plan in docs/plans/; service-catalog updated.
5.8 KiB
5.8 KiB
Drone Logbook (Open DroneLog) — Design
Date: 2026-07-04 Status: Approved (Viktor, 2026-07-04) Owner request: "I have a DJI Mini 4 Pro. I'm interested in github.com/ViktorBarzin/drone-logbook" → self-host it in the cluster.
Goal
Self-host Open DroneLog (upstream of the
ViktorBarzin/drone-logbook fork) at https://dronelog.viktorbarzin.me so Viktor can import
DJI Fly flight logs from his DJI Mini 4 Pro and analyze them privately: telemetry charts, 3D map
replay, per-flight and lifetime stats. All data stays in the cluster (single DuckDB database).
Decisions (interview, 2026-07-04)
| Question | Decision |
|---|---|
| Deployment form | Self-hosted Docker web app in k8s (not desktop app, not hosted webapp) |
| Exposure | Public dronelog.viktorbarzin.me, Authentik forward-auth (auth = "required") |
| Log ingestion | Both manual web upload and a server-side auto-import drop folder from day one |
| Image source | Upstream ghcr.io/arpanghosh8453/open-dronelog:latest — NOT the fork |
| Fork disposition | Fork is 0 ahead / 372 behind, adds nothing; delete or park it. Only revive (sync + ADR-0002 GHA build) if Viktor starts modifying the code |
Architecture
New Tier-1 stack stacks/drone-logbook/, modeled line-by-line on stacks/freshrss/
(the closest existing shape: single upstream-image app, own data volume, Keel-updated):
- Namespace
drone-logbook, tier4-aux, labelkeel.sh/enrolled=true→ Kyverno injects Keel poll annotations → auto-upgrades as upstream releases (project is actively maintained). - Deployment (1 replica,
Recreate— DuckDB is single-writer/embedded):- image
ghcr.io/arpanghosh8453/open-dronelog:latest(nginx frontend + Axum REST backend, port 80) - memory request=limit 512Mi (DuckDB import/analytics spikes), cpu request 25m, no cpu limit
- standard
KYVERNO_LIFECYCLE_V1/KEEL_IGNORE_IMAGE/KEEL_LIFECYCLE_V1lifecycle ignores
- image
- App data
/data/drone-logbook(DuckDB db, cached DJI decryption keys, uploaded originals):proxmox-lvm-encryptedblock PVCdrone-logbook-data-encrypted, 2Gi, topolvm autoresize → 10Gi ceiling. Encrypted class because flight logs are GPS traces of home/travel — sensitive data defaults toproxmox-lvm-encryptedper the storage decision rule (.claude/CLAUDE.md). Embedded DBs stay off NFS (same rationale documented in the freshrss stack: NFS only for static files). - Backup CronJob
drone-logbook-backup(mandatory for every proxmox-lvm app): daily 01:30 file copy of the data volume → NFS/srv/nfs/drone-logbook-backup(dated dirs, 30-day retention, Pushgateway metrics), pod-affinity co-scheduled with the app pod (RWO volume). 01:30 sits outside the 00:00/08:00/16:00 sync-import windows so the DuckDB file is quiescent; retained upload originals make even a torn copy recoverable by re-import.nfs-mirror(02:00) ships it to sda → Synology offsite. Vaultwarden pattern. - Sync drop folder: static NFS volume (
modules/kubernetes/nfs_volume)192.168.1.127:/srv/nfs/drone-logbook/sync-logs, mounted read-only at/sync-logs;SYNC_LOGS_PATH=/sync-logs,SYNC_INTERVAL="0 0 */8 * * *"(every 8 h). Any producer (Nextcloud sync, scp, a future phone pipeline) drops.txtlogs there; the app imports them automatically.KEEP_UPLOADED_FILES=truekeeps re-importable originals in the PVC. - Ingress via
ingress_factory:name = "dronelog",auth = "required"(Authentik forward-auth),dns_type = "proxied". External Uptime Kuma HTTPS monitor comes automatically with the ingress annotation. Homepage tile (group "Media & Entertainment", iconmdi-quadcopter). - Secrets: Vault KV
secret/drone-logbook(profile_creation_pass) → ExternalSecret (vault-kvClusterSecretStore) → k8s secretdrone-logbook-secrets→ envPROFILE_CREATION_PASS. Gates profile create/delete even for other Authentik-logged-in users. No plan-time secret reads needed (nodata "kubernetes_secret"). NoDJI_API_KEY— bundled default is fine at personal import volume; add later if rate-limited.
Operational notes
- DJI egress dependency: importing a new log file requires the pod to reach DJI's servers
once (flight-log decryption key fetch; keys are then cached in the data dir). Remember this when
egress enforcement lands (Security wave 1, beads
code-8ywc). - The web UI is desktop-first; mobile is functional but basic.
- NFS host prerequisite:
/srv/nfs/drone-logbook/sync-logs(root:www-data, 2775 — same shape as sibling dirs) and/srv/nfs/drone-logbook-backupcreated on 192.168.1.127 and recorded insecrets/nfs_directories.txt./srv/nfsis exported whole-tree, so no/etc/exports(scripts/pve-nfs-exports) change. - Backup story = the daily app-level backup CronJob (above) + the host
daily-backupLVM-snapshot leg + original log files retained both in the drop folder and in the data volume (KEEP_UPLOADED_FILES=true).
Alternatives considered
- Build from the fork (
ghcr.io/viktorbarzin/...via GHA, ADR-0002): rejected for now — fork has zero custom commits; a build chain adds maintenance for no benefit. Revisit if code changes are wanted. auth = "app"+ app profile passwords (would enable theopendronelog-syncnative uploader from anywhere): rejected — a single app password guarding GPS traces of home/travel on the open internet is weaker than Authentik; the sync drop folder covers automated ingestion instead.- Internal-only (.lan + VPN): rejected — Authentik-gated public matches the rest of the homelab and works without VPN while traveling.
- NFS for the DuckDB data: rejected — embedded-DB-on-NFS locking risk; freshrss precedent keeps app DB data on proxmox-lvm.
Implementation
See 2026-07-04-drone-logbook-plan.md.