Wenn die Ablage selbst Hand anlegt: Nextcloud Flow und das Workflow-Script
Nextcloud ist längst kein reiner Dateiserver mehr. Seit Version 18 steckt im Kern eine Workflow-Engine, die auf Ereignisse in der Ablage reagiert – den meisten bekannt unter dem Namen Flow. Mit der Erweiterung Workflow Script lassen sich eigene Skripte als Aktion einhängen. Das ist mehr als eine Spielerei, aber auch kein Selbstläufer. Ein praxisnaher Durchgang durch Konzept, Aufbau, Fallstricke und Betrieb.
Ein Scan-Ordner, drei Handgriffe zu viel
Es fängt meist harmlos an: Ein Multifunktionsgerät schiebt eingescannte Rechnungen in einen Freigabeordner, das Dienstkonto heißt „scan“, die Dateinamen sehen aus wie Scan_2024-11-03_0007.pdf. Irgendwer muss diese Dateien anschließend umbenennen, mit einem Tag versehen, in die passende Jahres- und Monatsstruktur einsortieren und dafür sorgen, dass die Buchhaltung sie überhaupt findet. Drei Handgriffe, jeden Tag, seit Jahren. Der Aufwand pro Datei ist lächerlich klein. Die Summe daraus ist es nicht.
Der naheliegende Reflex ist ein Shell-Skript auf dem Server, das den Ordner beobachtet. Mit inotify oder einem systemd-Path-Unit ist das an einem Nachmittag gebaut, und es funktioniert – bis zu dem Moment, in dem jemand fragt, wer die Datei wann verschoben hat, oder bis die Ablage nicht mehr auf einer lokalen Platte liegt, sondern auf einem SMB-Share, einem S3-Bucket oder einem Object Store als Primärspeicher. Spätestens dann gibt es gar kein lokales Verzeichnis mehr, in dem ein Watcher etwas sehen könnte. Und selbst wenn: Das Skript weiß nichts über Systemtags, Versionen, Freigaben, Papierkorb oder die Rechte der Nutzer. Es arbeitet an der Anwendung vorbei, direkt am Dateisystem. Sobald zwei Instanzen im Spiel sind oder der Speicher nicht mehr lokal, sondern auf S3 oder einem SMB-Share liegt, zerfällt der Ansatz.
Genau hier setzt die Workflow-Engine an. Sie sitzt im Inneren der Anwendung, kennt die Objekte, mit denen Nextcloud arbeitet, und kann dieselben Operationen auslösen, die auch ein Mensch in der Weboberfläche auslösen würde. Der entscheidende Punkt: Automatisierung passiert dort, wo die Metadaten entstehen – nicht daneben.
Flow ist ein Rahmen, kein Programm
Wer zum ersten Mal in den Administrationsbereich wechselt und den Menüpunkt Flow öffnet, erlebt oft eine kleine Enttäuschung. Da ist eine Regelverwaltung mit Ereignissen, Bedingungen und Aktionen, aber die Auswahl wirkt überschaubar. Das hat einen Grund: Die Engine selbst kann fast nichts. Sie ist ein Gerüst, in das Apps ihre Bausteine einhängen. Nextcloud nennt das intern Workflow-Engine; Flow ist der sichtbare Teil davon.
Eine Regel besteht immer aus derselben Dreiteilung. Ein Ereignis löst sie aus, etwa „Datei erstellt“ oder „Tag zugewiesen“. Dann laufen beliebig viele Prüfungen, die als Filter wirken – Dateiname entspricht einem Muster, Dateigröße über einem Schwellwert, Nutzer ist Mitglied einer bestimmten Gruppe. Erst danach kommt die Aktion, und hier wird es interessant, weil Apps eigene Aktionen beisteuern. Automatisches Tagging gehört dazu, die Konvertierung nach PDF, die Texterkennung per OCR, und eben das Ausführen eines Skripts.
Diese Architektur erklärt, warum Flow in einer nackten Installation mager wirkt und mit jeder zusätzlichen App an Gewicht gewinnt. Sie erklärt aber auch, warum manche Betriebsteams frustriert aufgeben: Sie suchen in Flow nach einer Funktion, die in Wahrheit eine andere App liefern müsste. Wer das Prinzip einmal verstanden hat, plant anders – und prüft vor jeder Regel, ob die passende Operations-App überhaupt installiert und in der richtigen Version vorhanden ist.
Ereignisse sind nicht immer so eindeutig, wie sie heißen
Der praktisch unterschätzteste Teil der ganzen Angelegenheit sind die Auslöser. „Datei erstellt“ klingt eindeutig. Es ist es nicht. Ein Upload über die Weboberfläche, ein Sync über den Desktop-Client, ein WebDAV-PUT aus einer Fachanwendung, ein Chunked Upload großer Dateien – sie alle enden in einer erstellten Datei, aber auf unterschiedlichen Wegen. Bei großen Dateien wird das Ereignis erst dann ausgelöst, wenn der letzte Block zusammengesetzt ist. Ein Skript, das auf einen Zwischenstand wartet, wartet für immer.
Dazu kommt, dass nicht jede App jedes Ereignis meldet. Manche Erweiterungen registrieren nur Änderungen an Tags, andere nur an Dateiinhalten, wieder andere nur an Ordnern. Externe Speicher verhalten sich erneut anders, weil Änderungen dort nicht zwangsläufig durch Nextcloud laufen. Wer eine Regel baut, sollte deshalb immer den einfachsten Weg testen, der in der Praxis vorkommt – und das ist in den seltensten Fällen die schöne Web-Oberfläche.
An welcher Stelle das Skript ins Spiel kommt
Die Erweiterung Workflow Script, in der Oberfläche meist schlicht als Flow Script oder als Aktion „Skript ausführen“ sichtbar, liefert genau eine Sache: einen Fluchtpunkt für alles, was sich mit fertigen Bausteinen nicht abbilden lässt. Sie bringt einen Editor mit, in dem sich Skripte anlegen, benennen und versionieren lassen. Regeln greifen später auf diese Skripte zu, so wie sie auf jede andere Aktion zugreifen würden.
Das Muster ist bewusst einfach gehalten. Man legt ein Skript an, beschreibt darin die Logik und hängt es an eine Regel. Die Skripte laufen serverseitig, im Kontext der auslösenden Operation. Sie sehen das betroffene Objekt, also in der Regel eine Datei samt Metadaten, und können darauf zugreifen: Inhalte lesen, Inhalte schreiben, Namen ändern, Tags setzen, zusätzliche Dateien erzeugen, Kommentare hinterlassen. Genau das macht die Erweiterung so nützlich – sie schließt die Lücke zwischen „das kann die Aktion schon“ und „das müsste jemand kurz programmieren“.
Nützlich ist auch, dass sich für ein Skript Eingabefelder definieren lassen. Statt jeden Wert im Quelltext zu verdrahten, bekommt die Regel beim Anlegen eine kleine Maske, in der etwa ein Ordnername, ein Präfix oder ein Schwellwert eingetragen wird. Dasselbe Skript lässt sich dadurch in mehreren Regeln mit unterschiedlicher Parametrisierung verwenden. In der Praxis ist das der Unterschied zwischen einer wartbaren Sammlung von fünf Skripten und einem Friedhof aus dreißig Einzweckdateien.
Die Skriptsprache und was sie nicht ist
Die eingebettete Sprache ist bewusst schlank gehalten und orientiert sich an Lua – klein, ohne Klassen, ohne ausufernde Standardbibliothek. Das ist eine Designentscheidung mit Folgen. Wer aus der PHP- oder Python-Welt kommt, wird zunächst nach Funktionen suchen, die es nicht gibt. Dateien werden nicht über einen vollen Systemzugriff bearbeitet, sondern über die Objekte, die die Engine bereitstellt. Netzwerkaufrufe, Prozessstarts oder der Zugriff auf das Dateisystem außerhalb der Nextcloud-Abstraktion sind nicht vorgesehen. Wer solche Dinge braucht, sollte sie nicht in ein Flow-Skript hineinpressen, sondern einen separaten Dienst davor setzen.
Das klingt nach Einschränkung, ist in Wahrheit eine Absicherung. Ein Skript, das beliebige Shell-Befehle absetzen darf, ist kein Automatisierungswerkzeug mehr, sondern eine Hintertür mit Webinterface. Die Enge der Sprache ist die eigentliche Sicherheitsmaßnahme.
Trotzdem gilt: Ein Flow-Skript ist kein Sandkasten im strengen Sinn. Es läuft mit den Rechten des Servers und damit mit deutlich mehr Möglichkeiten, als der auslösende Nutzer hat. Wer Skripte schreiben darf, hat faktisch Administratorenrechte – auch wenn die Oberfläche das nicht so deutlich sagt. Diese Erkenntnis sollte am Anfang jeder Einführung stehen.
Wie ein Skript aufgebaut ist
Die folgenden Ausschnitte sind sinngemäß und dienen der Veranschaulichung; die konkreten Funktionsnamen hängen von Version und Erweiterungsstand ab. Inhaltlich geht es immer um dasselbe Muster: einen Einstiegspunkt, ein Objekt, ein paar Hilfsmittel, ein Protokolleintrag.
-- Beispiel: Rechnungsscans nach Jahr und Monat sortieren
function handle(file, user, inputs)
if file.mimetype ~= "application/pdf" then
log("Kein PDF, wird übersprungen: " .. file.name)
return
end
local jahr = string.sub(file.name, 6, 9)
local monat = string.sub(file.name, 11, 12)
if jahr == "" or monat == "" then
log("Namensschema unbekannt, keine Aktion")
return
end
local ziel = inputs.zielordner .. "/" .. jahr .. "/" .. monat
move(file, ziel)
tag(file, "eingang-geprueft")
log("Verschoben nach " .. ziel .. "/" .. file.name)
end
Drei Dinge fallen auf. Erstens die frühen Ausstiege: Wer nicht sicher ist, ob die Datei überhaupt passt, bricht ab, statt sich durchzuarbeiten. Zweitens die Protokollierung. In einer Umgebung mit hunderten Ausführungen pro Tag ist ein Logeintrag, der den Fall beschreibt, mehr wert als jede Debug-Konsole. Drittens die Parametrisierung über inputs. Der Zielordner steht nicht im Code, sondern kommt aus der Regel.
Was in solchen Beispielen gern unterschlagen wird: Es gibt keine Transaktion. Scheitert das Skript nach dem Setzen eines Tags, bleibt der Tag gesetzt. Nextcloud rollt nichts zurück, keine Datenbank, keine Dateioperation, nichts. Wer mehrere Schritte ausführt, muss selbst dafür sorgen, dass ein halb ausgeführter Zustand entweder harmlos oder erkennbar ist. Ein bewährtes Muster ist die Reihenfolge: erst prüfen, dann schreiben, dann markieren. Die Markierung als letzter Schritt ist der Indikator, dass alles geklappt hat.
Vier Muster, die sich in der Praxis bewährt haben
Die folgenden Szenarien sind bewusst bodenständig gewählt. Sie stammen aus Umgebungen, in denen Nextcloud als zentrale Ablage dient und nicht als Spielwiese.
Erstens: Rechnungseingang mit Pflichtfeldern
Der Scan-Ordner ist ein Gruppenordner, in den nur das Gerät schreiben darf. Eine Regel reagiert auf „Datei erstellt“, prüft den Zielordner und ruft ein Skript auf, das den Dateinamen in ein einheitliches Schema überführt, einen Systemtag setzt und die Datei in die Buchhaltungsstruktur verschiebt. Zusätzlich legt es eine kleine Begleitdatei mit den ursprünglichen Metadaten ab – Uploadzeitpunkt, ursprünglicher Name, verantwortliches Gerät. Das klingt nach Übervorsicht, spart aber bei jeder Rückfrage aus der Buchhaltung zehn Minuten.
Zweitens: OCR als Vorstufe, Skript als Nachbearbeitung
Die OCR-Aktion schreibt erkannten Text in eine Nebendatei. Ein Skript liest diesen Text, sucht nach Datum, Betrag und Rechnungsnummer, schreibt die Treffer in eine JSON-Datei und entscheidet anhand eines Schwellwerts, ob die Rechnung als geprüft gilt oder in eine Prüfliste wandert. Diese Aufteilung ist deshalb sinnvoll, weil die Erkennung selbst eine eigene App mit eigenem Binärwerkzeug ist und das Skript nur noch die Interpretation übernimmt. Wer beides in ein Skript packen will, baut sich eine Wartungsfalle.
Drittens: Quarantäne für unerwünschte Formate
Nicht jede Ablage soll Office-Dokumente mit Makros annehmen. Eine Regel prüft Dateiendung oder MIME-Typ, ein Skript verschiebt verdächtige Dateien in einen Ordner „Prüfung“, setzt einen Tag und hinterlässt einen Kommentar mit Zeitstempel und Nutzername. Wichtig ist hier die Kombination mit der Zugriffskontrolle, die ebenfalls auf der Workflow-Engine basiert: Wer den Ordner nicht lesen darf, kann ihn auch nicht als Umgehungsweg benutzen.
Viertens: Aufräumen nach Konvention
Projektordner, die seit zwei Jahren nicht mehr angefasst wurden, sollen in ein Archiv wandern – aber nur, wenn sie ein bestimmtes Tag tragen und nicht gerade geteilt sind. Ein Skript prüft Alter, Tags und Freigaben und verschiebt erst dann. Solche Läufe will man nicht hundertfach am Tag, sondern zeitgesteuert. Und hier zeigt sich eine Grenze: Flow bringt keinen Zeitplan-Trigger mit. Wer periodische Läufe braucht, löst sie über einen Cron-Job, der per occ ein Kommando absetzt, oder über einen systemd-Timer, der wiederum die API anspricht.
Debugging: Wo man zuerst nachsieht
Skripte, die nicht laufen, sind selten spektakulär. Meist fehlt eine Bedingung, meist ist ein Pfad anders als gedacht, meist hat der auslösende Nutzer nicht die Rechte, die das Skript voraussetzt. Der erste Blick gehört deshalb in das Nextcloud-Protokoll. Dort landen Fehler der Engine, Ausnahmen der Operation und die Ausgaben des Skripts, sofern es selbst schreibt. Wer schnell arbeiten will, dreht das Log-Level temporär hoch, lässt einen einzelnen Testfall durchlaufen und dreht es danach wieder zurück.
Ein zweiter, oft vergessener Punkt: Die Engine führt Aktionen im Kontext der auslösenden Anfrage aus. Ein Skript, das eine Sekunde braucht, verlängert den Upload um eine Sekunde. Fünf Sekunden fallen auf, dreißig Sekunden sind ein Fehlerbericht. Manche Operationen verschieben ihre Arbeit deshalb in die Job-Queue, andere nicht. Wer nicht sicher ist, misst nach – und zwar mit realistischen Dateigrößen. Ein Test mit einer 40-Kilobyte-PDF sagt nichts über das Verhalten bei einem 300-Megabyte-Video.
Bewährt hat sich eine Testumgebung mit einer Kopie der Produktivkonfiguration, einem eigenen Testnutzer und einem stillen Zielordner. Die Regel wird zunächst ohne Aktion angelegt, nur mit einem Skript, das eine Zeile ins Log schreibt. Erst wenn das Ereignis zuverlässig feuert, kommt die eigentliche Logik dazu. Das ist langsamer, aber es trennt die Fehlersuche in zwei klar getrennte Bereiche: Auslösung und Verarbeitung. Wer beides gleichzeitig debuggt, sucht an zwei Stellen und findet an keiner.
Sicherheit ist hier kein Nebenthema
Skripte in einer Groupware sind privilegierter Code. Sie können Dateien lesen und schreiben, Tags setzen, Inhalte überschreiben. Ein fehlerhaftes Skript kann Daten zerstören, ein bösartiges kann sie exfiltrieren, sofern ihm ein Weg nach draußen offensteht. Deshalb gilt: Die Möglichkeit, Skripte anzulegen, gehört in die Hände einer kleinen Gruppe. In der Standardkonfiguration ist das ohnehin Administratoren vorbehalten – und dabei sollte es bleiben, auch wenn Nutzer eigene Flows anlegen dürfen.
Ergänzend sind ein paar Regeln sinnvoll, die in vielen Installationen fehlen. Schreibrechte auf die Ordner, in die Skripte schreiben, sollten eng gefasst sein. Skripte, die Inhalte verändern, sollten niemals blind überschreiben, sondern versionieren oder umbenennen. Und jedes Skript braucht einen Kommentar am Anfang, der sagt, was es tut, wer es geschrieben hat und welchen Zweck es erfüllt. In sechs Monaten ist das keine Formalie mehr, sondern die einzige Rettung.
Der Klassiker unter den Fehlern: die Schleife
Eine Regel, die auf das Ereignis „Tag zugewiesen“ hört und im Skript selbst ein Tag setzt, hat ein Problem. Sie löst sich mit hoher Wahrscheinlichkeit selbst aus. Die Folge sind Endlosschleifen, die sich in den Logs als gleichförmige Zeilenkaskade zeigen und im schlimmsten Fall die Datenbank füllen. Ähnlich verhält es sich mit Regeln, die auf „Datei geändert“ reagieren und selbst in die Datei schreiben.
Die Gegenmaßnahmen sind unspektakulär, aber wirksam. Erstens: Prüfungen so formulieren, dass sie nach der Aktion nicht mehr zutreffen – ein Tag, das gesetzt wird, sollte in der Bedingung ausgeschlossen sein. Zweitens: Ereignis und Wirkung trennen, also ein Skript nicht auf das Ereignis hören lassen, das es selbst hervorruft. Drittens: bei der Einführung mit einer Testinstanz arbeiten und die Anzahl der Ausführungen beobachten. Zwei Ausführungen pro Datei sind ein Warnsignal, nicht ein Messfehler.
Betrieb, Skalierung, Verantwortlichkeiten
Was in der Testumgebung harmlos ist, wird im Betrieb schnell unübersichtlich. Zwanzig Regeln, geschrieben von drei Personen über zwei Jahre, ohne Namenskonvention, sind ein Problem. Bewährt haben sich sprechende Regelnamen mit Präfix, etwa „Eingang – Rechnungen – Sortierung“, und eine dokumentierte Zuordnung, welches Skript zu welcher Regel gehört. Die Regeln selbst liegen in der Datenbank der Instanz und wandern mit dem Datenbank-Backup. Wer sie versioniert haben will, muss sie zusätzlich außerhalb sichern – von Hand oder über einen Export, der in der Oberfläche nicht vorgesehen ist.
Skalierung ist weniger ein Problem der Engine als eines der Erwartungen. Flow ist kein Nachrichtenbus mit Wiederholungslogik, keiner Warteschlange mit Backoff und keiner garantierten Zustellung. Wer eine Datei verarbeitet, verarbeitet sie einmal. Schlägt der Schritt fehl, bleibt es bei einem Logeintrag. Für viele Anwendungsfälle in der Ablage reicht das. Für alles, was fachlich verbindlich sein muss, braucht es eine vorgelagerte Warteschlange oder eine externe Verarbeitung mit eigenem Monitoring.
Ein weiterer Punkt, der in Projekten gern zu spät kommt: Wer darf Skripte schreiben, wer darf sie ändern, wer prüft sie? In regulierten Umgebungen ist ein Vier-Augen-Prinzip an dieser Stelle nicht übertrieben. Ein Skript, das bei jedem Upload in einen Gruppenordner läuft, ist betriebskritisch, auch wenn es nur eine Zeile ändert.
Was Flow nicht leistet – und was trotzdem sinnvoll ist
Flow ist keine Prozess-Engine im Sinne von BPMN. Es gibt keine parallelen Zweige, keine Wartezustände über Tage, keine Benutzeraufgaben mit Eskalation. Wer solche Anforderungen hat, sollte nicht versuchen, sie in eine Regelkette zu pressen. Dafür gibt es andere Werkzeuge, und sie lassen sich mit Nextcloud verbinden.
Ebenso wenig ist Workflow Script eine Low-Code-Plattform. Es ist ein Werkzeug für Menschen, die wissen, was sie tun, und die bereit sind, ihre Skripte zu pflegen. Der Vergleich mit einem Schraubendreher passt besser als der mit einem Baukasten: klein, präzise, gelegentlich unverzichtbar, aber kein Ersatz für eine Werkstatt.
Und trotzdem: Für eine erstaunlich große Zahl von Aufgaben ist das Gespann aus Engine und Skript die richtige Antwort. Überall dort, wo ein Ereignis in der Ablage eine Entscheidung nach sich zieht, die sich in wenigen Zeilen beschreiben lässt, gewinnt man Zeit zurück. Nicht durch große Automatisierung, sondern durch viele kleine.
Unterm Strich
Nextcloud Flow ist eines dieser Features, die man einmal versteht und danach überall sieht. Die Workflow-Engine sitzt an der richtigen Stelle, sie kennt Metadaten, Rechte und Objekte, und mit Workflow Script bekommt sie einen Ausgang für alles, was sich nicht als fertiger Baustein findet. Der Preis dafür ist Disziplin: klare Regeln, geprüfte Skripte, dokumentierte Zuständigkeiten, eine Testumgebung.
Wer klein anfängt – eine Regel, ein Skript, ein Protokolleintrag – und erst dann erweitert, wird selten enttäuscht. Wer gleich zehn Skripte in Produktion wirft, weil es so schön einfach aussah, wird sich in den Logs wiederfinden. Der Unterschied zwischen beiden Wegen liegt nicht in der Technik. Er liegt in der Frage, ob man Automatisierung als Projekt begreift oder als Bastelei, die man später schon aufräumen wird.