openDesk – single Node mit k3s/longhorn

Eigentlich ist ja alles unter https://www.opendesk.eu/en/blog/opendesk-on-k3s beschrieben nur dass es dann halt doch nicht so funktioniert, weil

  1. Die Minimum Requirements sind tatsächlich einzuhalten – ich bin mit 10 Cores gescheitert weil er dann einen Pod nicht schedulen konnte. Vielleicht Zufall/Pech aber mit 12 hatte ich konsistent keine Probleme – siehe Update 2026-01-02
    32 GB RAM sind machbar – am Ende sind ca. 20 GB in use – aber am Weg gehen 40 GB für Cache drauf – pendelt sich dann auch weit nach unten (10 GB) ein aber langsamer wirds damit sicher nicht.
  2. Mit irgendeiner OpenProject Version ist sticky bit Support beim Kubernetes Storage Provider Voraussetzung geworden, das Deployment bleibt mit dem Default local-path Provider dann hängen.
    (Fußnote 2 auf https://docs.opendesk.eu/operations/requirements/)
  3. Bei mir hat Helm nur bis 3.17.x funktioniert.
    (Fußnote 1 auf https://docs.opendesk.eu/operations/requirements/)
  4. Helm diff Plugin hat bei mir auch nur mit 3.12.4 funktioniert.
  5. Helmfile hat bei mir auch nur mit 1.2.2 funktioniert.

Weil wir anderes Storage brauchen auf Longhorn gewechselt, das hat aber Single Node auch spezielle Herausforderungen weil in der Defaultinstallation das Setup nach einem Reboot vom Host sonst kaputt ist.

Vorgehen:

  1. Das longhorn.yaml aus dem Setup nur runterladen aber nicht gleich applyen.
  2. Das Deployment für longhorn-driver-deployer um folgende Environmentvariablen erweitern damit nur jeweils ein Pod für Attacher/Provisioner/Resizer/Snapshotter erzeugt wird:
    - name: CSI_ATTACHER_REPLICA_COUNT
    value: "1"
    - name: CSI_PROVISIONER_REPLICA_COUNT
    value: "1"
    - name: CSI_RESIZER_REPLICA_COUNT
    value: "1"
    - name: CSI_SNAPSHOTTER_REPLICA_COUNT
    value: "1"
  3. ConfigMap longhorn-default-setting bei data/default-setting.yaml um folgende Settings erweitern (default-data-path natürlich optional, ich hatte zweite Disk eingehängt):
    default-data-path: "/longhorn"
    default-replica-count: 1
    node-drain-policy: "always-allow"
    auto-salvage: true
  4. ConfigMap longhorn-storageClass den parameter „numberOfReplicas“ von „3“ auf „1“ ändern
    numberOfReplicas: "1"
  5. Im Deployment longhorn-ui die Replicas von 2 auf 1 stellen
    replicas: 1
  6. Im values.yaml.gotmpl fürs openDesk deployment (Punkt 3.2 in der Anleitung „Customizing the environment“) Storage umstellen:
    persistence:
    storageClassNames:
    RWO: "longhorn"

Da die Wahrscheinlichkeit dass alle Longhorn Volumes nach einem Reboot korrupt sind bei annähernd 100% liegt sollte man vor so einem Reboot alle Deployments und Statefulsets im openDesk Namespace auf 0 setzen und danach wieder zurück auf 1.

Vorher:
kubectl scale deployment -n <openDesk-namespace> --replicas=0 --all
kubectl scale statefulset -n <openDesk-namespace> --replicas=0 --all

….warten bis alle Longhorn Volumes detached sind….

….Reboot….

Nachher:
kubectl scale deployment -n <openDesk-namespace> --replicas=1 --all
kubectl scale statefulset -n <openDesk-namespace> --replicas=1 --all

( Und ja das ist sicher gegen 2000 Kubernetes Gesetze und der Fahndungszettel für mich hängt schon überall aber für mich funktionierts 😀 )

Updates:

  • 2026-01-02: die CPU Problematik ist heute virulent geworden weil die Komponente openxchangeCoreMW CPU requests von 1 macht (anstatt vermutlich 0.1 wie die meisten anderen auch), daher in die values.yaml.gotmpl fürs Deployment eingefügt:
    resources:
    openxchangeCoreMW:
    requests:
    cpu: 0.1

Fedora 42: FreeRDP 3.18 bricht Remmina 1.4.41

Mit dem Update auf 3.18 von FreeRDP heute wurde Remmina RDP über Remote Desktop Gateway gebrochen (security negotiation failure oder irgendsowas).

Downgrade auf 3.12 hilft aber auch die Flucht nach vorne (3.20):

freerdp 3.20 von https://koji.fedoraproject.org/koji/packageinfo?packageID=11176 besorgen

  • freerdp-3.20.0-1.fc42
    • freerdp-3.20.0-1.fc42.x86_64.rpm
    • freerdp-libs-3.20.0-1.fc42.x86_64.rpm
    • libwinpr-3.20.0-1.fc42.x86_64.rpm
  • mit sudo dnf install ./freerdp-3.20.0-1.fc42.x86_64.rpm ./freerdp-libs-3.20.0-1.fc42.x86_64.rpm ./libwinpr-3.20.0-1.fc42.x86_64.rpm in einem Rutsch installieren

    Qubes OS: Mein Epson Drucker

    Da das jetzt alles viel länger gedauert hat als erwartet fürs nächste Mal hier dokumentiert.

    Drucker: Epson WF-7610 Series, steinalt aber läuft (bisher) wie ein Glöckerl
    + OS: Qubes OS, Debian 13 oder Fedora 42 Template based AppVM


    Ergibt: Spaß!

    Lösungsweg:

    1. In jedem Fall aus dom0: qvm-service <vm> cups on
    2. Software in AppVM (oder Template) installieren:
    3. Sicherstellen dass CUPS läuft: sudo systemctl start cups
    4. Drucker verbinden
      • sudo system-config-printer
      • Add / Network Printer / Find Network Printer / IP bei Host angeben und Find
      • Warten bis er Amok läuft und 2000 Einträge mit der IP-Adresse in der Liste baut – aber auch eben einen Eintrag „Epson WF-7610 (IP-Adresse)
      • Diesen Eintrag auswählen (Host wird mit Port 515 vorbefüllt und Queue „PASSTHRU“) und „Probe“ klicken, „Searching for drivers“ sollte aufgehen und verschwinden.
      • Forward und Apply

    Weg zum Treiber bei Epson:

    Samsung Odyssey G9 + AMD + HDMI + Linux

    Eine Traumkombination!

    • Der Odyssey G9 (ein S49FG910EU bei mir) hat 5120×1440 und kann 144 Hz.
    • Mein Mini PC (Ninkear K7 mit einer Ryzen 7430U) hat nur HDMI.
    • Damit 5120×1440 in höheren Refreshraten läuft braucht man HDMI 2.1.
    • AMD darf unter Linux aus irgendeinem dummen Grund nicht HDMI 2.1 machen.

    Ergo: Linux sieht als höchstes der Gefühle 3840×1080@120 oder so was natürlich nicht Sinn der Sache ist und auch noch Kacke aussieht.

    Lösung für 144 Hz: Display Port.

    Da ich aber keinen neuen Mini PC kaufen will funktioniert folgender 5120×1440@60 Workaround (da ich jetzt auf KDE/Arch fahre und demnächst auf QubesOS – beide mit X11- umsteige):

    /usr/share/sddm/scripts/Xsetup
    
    xrandr --newmode "5120x1440_60" 544.25 5120 5168 5200 5600 1440 1443 1453 1620 +hsync -vsync
    xrandr --addmode HDMI-A-1 "5120x1440_60"
    xrandr --output HDMI-A-1 --mode "5120x1440_60" --verbose
    

    Update: Ein Adapter von USB-C auf Display Port hat das Problem ohne neuen Mini PC gelöst 😀 (Qubes OS/X11 springt instant auf die native Auflösung in 120 Hz, 144 ist aber auch in der Liste)

    vCard via NFC von Webserver

    Weiß ja nicht ob es bei den Kollegen von c’t zufällig funktioniert hat aber bei mir war das unter https://www.heise.de/ratgeber/NFC-Visitenkarten-selbst-beschreiben-10794239.html beschriebene nicht funktionstüchtig…

    Falle 1:

    Scheinbar hat Apple ab iOS 16.2 beschlossen nur mehr vCard 3.0 oder höher zu unterstützen. Outlook, selbst in der neuesten M365 Version exportiert aber 2.1 – Mobile Safari spuckt dann lapidar „Safari cannot download this file“ aus.

    Falle 2:

    Also, man will ja auch was lernen, hab ich mit pythonvCard4 eine 4.0 vCard erzeugt. Mit Bild natürlich. Konnte iOS auch auf Anhieb öffnen, Android auch aber dort wurde ein Kontakt erzeugt der den Base64 Wert vom Bild im Namen hatte.

    Aus irgendeinem Grund macht Android das wenn die Namensfelder („FN“ und „N“) vorm PHOTO Feld stehen. Also zuerst PHOTO dann FN/N im vCard File.

    Falle 3:

    Weiß nimmer genau wer mit was umgehen kann aber auf alle Fälle hab ich jetzt das PHOTO zweimal drin, einmal mit

    PHOTO:data:image/jpeg;base64,...base64 Zeug....

    und einmal mit

    PHOTO;ENCODING=b;MEDIATYPE=image/jpgeg:...base 64 Zeug....

    nur so haben beide Betriebssystem konsistent ein Bild beim Import.

    Bissl Referenzen:

    Wikipedia: https://de.wikipedia.org/wiki/VCard
    vCard 2.1: https://www.rfc-editor.org/rfc/rfc2425.html
    vCard 3.0: https://www.rfc-editor.org/rfc/rfc2426.html
    vCard 4.0: https://www.rfc-editor.org/rfc/rfc6350.html

    Longhorn Backup gegen AGPL3 Minio S3

    Falls ich oder irgendwer es jemals wieder wo braucht 😀 Single host. Kein TLS. Ohne Schusswaffe. Nur um es mal gesehen zu haben.

    Minio AGPL3 runterladen und installieren (erzeugt systemd Service „minio„, User und Gruppe „minio-user“ und Config auf /etc/default/minio) – ist seltsam weil offenbar je nach Download URL andere Lizenz im Executable eingebacken ist:

    wget https://dl.min.io/server/minio/release/linux-amd64/minio.deb
    sudo dpkg -i minio.deb
    

    /etc/default/minio anpassen

    ## Volume to be used for MinIO server.
    MINIO_VOLUMES="/data/minio"
    
    ## Use if you want to run MinIO on a custom port.
    MINIO_OPTS="--address :9198 --console-address :9199"
    
    ## Root user for the server.
    MINIO_ROOT_USER=whatever1
    
    ## Root secret for the server.
    MINIO_ROOT_PASSWORD=whatever2
    
    ## set this for MinIO to reload entries with 'mc admin service restart'
    MINIO_CONFIG_ENV_FILE=/etc/default/minio
    

    Der Datenpfad muss dem minio User gehören:

    sudo chmod -R minio-user:minio-user /data/minio
    

    Minio starten und systemd journal checken was abgeht:

    sudo systemctl start minio
    sudo journalctl -u minio
    

    Im Webinterface (Port 9199 gemäß obiger Config) kann man nicht viel machen außer Buckets verwalten (anlegen/löschen), daher brauchen wir MC, das CLI:

    wget https://dl.min.io/client/mc/release/linux-amd64/mc
    chmod +x mc
    sudo cp mc /usr/local/bin/
    

    mc braucht auch ein config file welches beim ersten Aufruf mit Defaultwerten befüllt wird (die nicht funktionieren oder wir erzeugen selbst eines – die Keys sind User/PW von /etc/default/minio) – die ist unter ~/.mc/config.json zu speichern:

    {
            "version": "10",
            "aliases": {
                    "local": {
                            "url": "http://localhost:9198",
                            "accessKey": "whatever1",
                            "secretKey": "whatever2",
                            "api": "S3v4",
                            "path": "auto"
                    },
            }
    }
    

    Wir erzeugen ein Bucket und setzen die Region des Servers (+Restart desselben):

    mc mb local/longhornbackup
    mc admin config set local region name=myregion
    mc admin service restart local
    

    Dann brauchen wir noch eine Zugriffspolicy (Einschränkung auf unseren Bucket den wir soeben erzeugt haben) die wir dann unserem Backupuser zuordnen (minio-longhornbackup-policy.json):

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "s3:*",
          "Resource": [
            "arn:aws:s3:::longhornbackup/*",
            "arn:aws:s3:::longhornbackup"
          ]
        }
      ]
    }
    

    Zuletzt legen wir einen User samt Access Key für Longhorn Config an und attachen die Policy (das local ist der Alias der Konfiguration aus der ~/.mc/config.json, das Kennwort ist irrelevant weil wir eh mit accesskey zugreifen):

    mc admin user add local longhorn irgendeinkennwort
    mc admin accesskey create local longhorn
    mc admin policy create local longhornbackup ./minio-longhornbackup-policy.json
    mc admin policy attach local longhornbackup --user longhorn
    

    Mit den Werten die die accesskey Erzeugung unter „Access Key“ und „Secret Key“ ausgespuckt hat erzeugen wir ein Kubernetes Secret im longhorn-system Namespace (minio-secret hier, beim AWS_ENDPOINTS Wert den Servernamen auf dem Minio läuft plus den Port für API aus /etc/default/minio dort verwenden):

    kubectl -n longhorn-system create secret generic minio-secret \
        --from-literal=AWS_ACCESS_KEY_ID=.... \
        --from-literal=AWS_SECRET_ACCESS_KEY=.... \
        --from-literal=AWS_ENDPOINTS=http://miniohost_oder_ip:9198
    

    Zuletzt tragen wir das Backup Target im Longhorn Web UI noch unter Setting/Backup Target ein wobei der Teil vor dem @ der Name des Bucket ist und das hinterm @ die Region die wir gesetzt haben:

    Container Applikationsdaten in OneDrive sichern

    Problem: VPS mit rootless Podman Container die (völlig überraschend) Daten erzeugen die man (auch völlig überraschend) sichern will – aber nicht beim Hoster wegen Kosten und 3-2-1 Regel (wenn wir uns mal das 3 und 2 wegdenken).

    Subproblem: Wegen UID Mapping haben die Daten die Container erzeugt einen Owner mit einer gemappten UID und somit kann der (unberechtigte) User der die Podman Container betreibt nicht auf die Daten zugreifen.

    Lösung: Daten mit root und rclone als TAR (wegen Erhalt UIDs) nach OneDrive sichern und mit rclone alte Files löschen lassen.

    1. rclone installieren

    2. rclone config
      1. als Name „onedrive“ angeben
      2. type of storage „Microsoft OneDrive“ auswählen (bei mir 31)
      3. client_id, client_secret überspringen
      4. Region „Microsoft Cloud Global“
      5. Advanced config: no
      6. Auto Config: no
      7. Auf Rechner mit Browser
        rclone authorize "onedrive" --onedrive-auth-url https://login.microsoftonline.com/<tenant-guid>/oauth2/v2.0/authorize --onedrive-token-url https://login.microsoftonline.com/<tenant-guid>/oauth2/v2.0/token
      8. Output in config_token reinpasten
      9. config_type=OneDrive Personal or Business (1 bei mir)
      10. drive_id=das mit „Documents“ oder „Dokumente“ (1 bei mir)
      11. Drive OK bestätigen
    3. ~/.config/rclone/rclone.conf auf /root/.config/rclone/rclone.conf kopieren 

    4. Backupscript erstellen (s.u.) ggf. --min-age anpassen wenn man Backups länger als 3 Monate aufheben will.
      Script geht davon aus dass User mit dem Container laufen podman heißt (-M podman@ im systemctl Call) und der Container mycontainer als Service podman-mycontainer im Ordner /path/to/mycontainerparent läuft und auf /targetfolder/on/onedrive gesichert werden soll.

    5. Aufruf in System crontab einfügen (muss als root laufen weil die Dateien die im Container erzeugt werden ja UID-mapped sind und für normale User nicht zugreifbar sind).
    #!/bin/bash
    FN="mycontainer_"$(date +"%Y%m%d%H%M")".tar.gz"
    rm backup_mycontainer.log &amp;&gt;/dev/null
    echo $(date +"%Y-%m-%d %H:%M:%S") stopping service &>>backup_mycontainer.log
    systemctl --user -M podman@ stop podman-mycontainer &>>backup_mycontainer.log
    echo $(date +"%Y-%m-%d %H:%M:%S") create $FN &>>backup_mycontainer.log
    tar cfz $FN -C /path/to/mycontainerparent mycontainer &>>backup_mycontainer.log
    echo $(date +"%Y-%m-%d %H:%M:%S") rclone $FN &>>backup_mycontainer.log
    rclone copy $FN onedrive:/targetfolder/on/onedrive &>>backup_mycontainer.log
    echo $(date +"%Y-%m-%d %H:%M:%S") remove $FN &>>backup_mycontainer.log
    rm $FN &>>backup_mycontainer.log
    echo $(date +"%Y-%m-%d %H:%M:%S") starting service &>>backup_mycontainer.log
    systemctl --user -M podman@ start podman-mycontainer &>>backup_mycontainer.log
    echo $(date +"%Y-%m-%d %H:%M:%S") delete backups older than 3 months &>>backup_mycontainer.log
    rclone delete onedrive:/targetfolder/on/onedrive --min-age 3M --verbose &>>backup_mycontainer.log
    cat backup_mycontainer.log|s-nail -Sv15-compat -Smta=smtp://…user…:…passwort…@smtpserver.com:587 \ -Ssmtp-use-starttls \ -Ssmtp-auth=login \ -a backup_mycontainer.log \ -s "[mycontainer backup] Completed" \ -r backup-mycontainer@meinedomain.com \ meinuser@meinedomain.com
    

    Proxmox aufgefundene vDisk

    Wollte Storage einer VM von einem LVM Store auf einen anderen verschieben und bin nach Start draufgekommen dass ich nicht das Hakerl bei „delete on source“ gemacht habe – abgebrochen in der Annahme dass er die unfertige Kopie am Ziel dann entfernt.

    Natürlich hat er die Zieldisk nicht gelöscht und man konnte sie auch nicht manuell löschen weil er meinte es gibt eine VM dazu.

    Lösung: qm disk rescan, das erkennt die in der Luft hängende Disk und fügt sie als „unused0“ der VM hinzu wo man sie dann kontrolliert löschen kann.

    Quelle: https://forum.proxmox.com/threads/orphaned-vm-disk-removal.144637/

    TP-Link Switches, Omada und die VLANs

    Obwohl TP-Link seit mehreren Jahren in diversen Supportforen angefleht wird das zu ändern sind neuere Omadataugliche TP-Link Switches ausschließlich über VLAN 1 adoptierbar.

    Mein Lösungsansatz (für mich weil ichs bis zum nächsten Mal garantiert erfolgreich vergessen hab):

    • Port auf der Kiste wo Omada läuft freihalten
    • Bei Bedarf diesen Port auf 192.168.0.irgendwas (<> 1) konfigurieren, kein VLAN
    • Switch anstecken (am besten auf einem Port der auch in der Zielkonfiguration Default VLAN (1) hat) und mit Strom versorgen
    • Warten bis der Switch in Omada auftaucht
    • Adopten
    • [falls noch nicht erledigt VLANs in Omada unter Settings/LAN erzeugen]
    • Bei den Ports die Zielkonfiguration der Ports herstellen (VLANs/Trunks) herstellen.
    • In der Config unter VLAN Interface das gewünschten Management VLAN hinzufügen enablen und editieren
    • „Management VLAN“ enablen und Adresse vergeben (oder für die mutigen halt mit DHCP am Managementinterface arbeiten)

    Wenn man den Switch jetzt an dem Port in Richtung eine Switches dessen Zielport mit dem Management VLAN tagged ist anbindet sollte man normal weiter administrieren können.

    Falls der Konfigurationsport nicht VLAN 1 behalten soll entsprechend konfigurieren.