Vom Klick zum Script: ComfyUI per API steuern
Von der visuellen Workflow-Erstellung in ComfyUI bis zur automatisierten Mediengenerierung per API

Die Automatisierung wiederkehrender Aufgaben ist ein Kernprinzip effizienter Systemadministration. Wenn es jedoch um kreative Prozesse wie die Erstellung von Titelbildern für Blog-Artikel oder Podcasts geht, scheint eine manuelle Vorgehensweise oft unausweichlich. Mit ComfyUI, einer extrem flexiblen, Node-basierten Weboberfläche zum Beispiel für die Bildgenerierung mit beliebigen KI-Modellen, lässt sich dieser Prozess jedoch nicht nur steuern, sondern über eine simple API auch vollständig in die eigene Toolchain integrieren. So wird die Kommandozeile zur Kreativwerkstatt.
Was ist ComfyUI?
ComfyUI ist eine grafische Benutzeroberfläche für verschiedene KI Modelle, die sich auf HuggingFace finden lassen. Anstatt sich durch unzählige Menüs und Einstellungen zu klicken, werden in ComfyUI einzelne Funktionsblöcke, sogenannte Nodes, auf einer Arbeitsfläche platziert und miteinander verbunden. Jeder Node hat eine spezifische Aufgabe: Einer lädt das KI-Modell, ein anderer nimmt den positiven und negativen Prompt entgegen, ein weiterer führt den eigentlichen Generierungsprozess durch (KSampler
) und wieder ein anderer dekodiert das Ergebnis in ein sichtbares Bild.
Als kleines Beispiel habe ich hier mal juggernautXL mit einem Prompt zur Erstellung eines SciFi-Roboters in einer Cyberpunk-Welt genutzt:

Man kann sich die Arbeit mit ComfyUI wie das Spielen auf einem modularen Synthesizer vorstellen: Einzelne Module werden über Kabel verbunden, um einen einzigartigen Klang zu erzeugen. Ähnlich verknüpft man in ComfyUI Nodes, um individuelle Workflows für die Bildgenerierung aufzubauen. Diese Flexibilität ermöglicht komplexe Abläufe – etwa die gezielte Bildmanipulation oder die Restaurierung alter Fotos, wie man es inzwischen auch von NanoBanana kennt. Ein wesentlicher Vorteil dabei: Auf dem eigenen Rechner ist man in der Auflösung nicht beschränkt. Ebenso die Auswahl der Personen in Bildern ist nicht eingeschränkt. So lassen sich dann auch Celebrities in beliebigen Situationen darstellen.

Ebenso lassen sich damit meine Podcasts produzieren und die Titelbilder meiner Blog-Artikel generieren:

Installation unter Debian
Die Installation von ComfyUI auf einem Debian-Server ist unkompliziert und erfordert lediglich git
und eine aktuelle python
-Version.
1. Abhängigkeiten installieren
Zunächst werden die notwendigen Pakete über den Paketmanager installiert:
sudo apt update
sudo apt install git python3-pip python3-venv -y
2. ComfyUI-Repository klonen
Anschließend wird das offizielle ComfyUI-Repository von GitHub geklont. Es empfiehlt sich, dies in einem passenden Verzeichnis, wie zum Beispiel /opt/
, zu tun.
cd /opt
sudo git clone https://github.com/comfyanonymous/ComfyUI.git
sudo chown -R $(whoami):$(whoami) ComfyUI
cd ComfyUI
3. Python Virtual Environment einrichten
Um System-Abhängigkeiten sauber zu halten, wird eine virtuelle Python-Umgebung im ComfyUI-Verzeichnis erstellt und aktiviert.
python3 -m venv venv
source venv/bin/activate
4. Python-Abhängigkeiten installieren
Nun werden die für ComfyUI erforderlichen Python-Pakete über pip
installiert.
pip install -r requirements.txt
5. KI-Modell herunterladen
ComfyUI benötigt mindestens ein sogenanntes "Checkpoint"-Modell, um Bilder generieren zu können. Diese Modelle enthalten die eigentliche "Intelligenz" und sind oft mehrere Gigabyte groß. Ein populäres Modell ist "Juggernaut XL". Es kann von Plattformen wie Hugging Face oder Civitai heruntergeladen und im passenden Verzeichnis abgelegt werden.
# Beispiel-Download für Juggernaut XL v9
wget -P models/checkpoints/ https://huggingface.co/RunDiffusion/Juggernaut-XL-v9/resolve/main/Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors
6. ComfyUI starten
Nach Abschluss der Installation kann der Server gestartet werden. Der --listen
Parameter sorgt dafür, dass die Weboberfläche im Netzwerk erreichbar ist.
python main.py --listen
Der Server ist nun standardmäßig unter http://DEINE_SERVER_IP:8188
erreichbar.
Vom User-Interface zur API: Der Workflow-Export
Die wahre Stärke von ComfyUI für die Automatisierung liegt in seiner Fähigkeit, jeden erstellten Workflow als JSON-Objekt zu exportieren. Dieses JSON-Objekt beschreibt exakt die Nodes, deren Verbindungen und Einstellungen. Es ist die Blaupause, die direkt an die ComfyUI-API gesendet werden kann.
Im File-Menü von ComfyUI gibt es einen Eintrag "Export (API)". Ein Klick darauf speichert den aktuellen Workflow als workflow_api.json
-Datei. Diese Datei ist die Grundlage für das nachfolgende Automatisierungs-Script.

Ein Beispiel: Bash-Script zur Bildgenerierung
Mit der exportierten API-Definition lässt sich der gesamte Prozess der Bildgenerierung nun über die Kommandozeile steuern. Ein Bash-Script, das curl
zum Senden von HTTP-Anfragen und jq
zur Bearbeitung des JSON-Objekts verwendet, ist hierfür das perfekte Werkzeug.

Das folgende Script kapselt die gesamte Logik in einer einzigen Funktion.
#!/bin/bash
#
# ComfyUI Image Generation Script
# (c) M. Meister
#
# --- Konfiguration ---
# URL des laufenden ComfyUI Servers
comfyui_server_url="http://ai:8188"
# Die ID des "SaveImage" Nodes im exportierten Workflow
comfyui_save_node_id="19"
# Standardwerte für die Bildgenerierung
image_negative_prompt="ugly, tiling, poorly drawn hands, poorly drawn feet, poorly drawn face, out of frame, extra limbs, disfigured, deformed, body out of frame, bad anatomy, watermark, signature, cut off, low contrast, underexposed, overexposed, bad art, beginner, amateur, distorted face"
image_width=1280
image_height=896
image_steps=25
image_cfg=8
# --- Funktion zur Bildgenerierung ---
function generiere_titelbild () {
local positive_prompt="$1"
local output_filename="$2"
echo "› [1/4] Bereite Workflow für ComfyUI vor..."
# Hier fügt man einfach das exportierte Objekt ein.
# ▌ ▌ ▌ ▗▀▖▜ ▜▘▞▀▖▞▀▖▙ ▌ ▞▀▖▌ ▖ ▌ ▐
# ▌▖▌▞▀▖▙▀▖▌▗▘▐ ▐ ▞▀▖▌ ▌▄▄▖▐ ▚▄ ▌ ▌▌▌▌▄▄▖▌ ▌▛▀▖▗▖▞▀▖▌▗▘▜▀
# ▙▚▌▌ ▌▌ ▛▚ ▜▀ ▐ ▌ ▌▐▐▐ ▌▐ ▖ ▌▌ ▌▌▝▌ ▌ ▌▌ ▌ ▌▛▀ ▛▚ ▐ ▖
# ▘ ▘▝▀ ▘ ▘ ▘▐ ▘▝▀ ▘▘ ▝▘ ▝▀ ▝▀ ▘ ▘ ▝▀ ▀▀ ▄▘▝▀▘▘ ▘ ▀
read -r -d '' workflow_template <<'EOF'
{
"13": { "inputs": { "text": ["17", 0], "clip": ["21", 1] }, "class_type": "CLIPTextEncode" },
"14": { "inputs": { "text": ["17", 0], "clip": ["21", 1] }, "class_type": "CLIPTextEncode" },
"15": { "inputs": { "text": ["18", 0], "clip": ["21", 1] }, "class_type": "CLIPTextEncode" },
"16": { "inputs": { "text": ["18", 0], "clip": ["21", 1] }, "class_type": "CLIPTextEncode" },
"17": { "inputs": { "value": "" }, "class_type": "PrimitiveString" },
"18": { "inputs": { "value": "" }, "class_type": "PrimitiveString" },
"19": { "inputs": { "filename_prefix": "MeisterSecurityBlog", "images": ["23", 0] }, "class_type": "SaveImage" },
"21": { "inputs": { "ckpt_name": "juggernautxlRagnarok.k3mq.safetensors" }, "class_type": "CheckpointLoaderSimple" },
"22": { "inputs": { "steps": 25, "cfg": 8, "sampler_name": "dpmpp_2m", "scheduler": "karras", "add_noise": "enable", "control_after_generate": "randomize", "start_at_step": 0, "end_at_step": 10000, "return_with_leftover_noise": "disable", "model": ["21", 0], "positive": ["25", 0], "negative": ["26", 0], "latent_image": ["24", 0] }, "class_type": "KSamplerAdvanced" },
"23": { "inputs": { "samples": ["22", 0], "vae": ["21", 2] }, "class_type": "VAEDecode" },
"24": { "inputs": { "width": 1280, "height": 896, "batch_size": 1 }, "class_type": "EmptyLatentImage" },
"25": { "inputs": { "conditioning_1": ["14", 0], "conditioning_2": ["13", 0] }, "class_type": "ConditioningCombine" },
"26": { "inputs": { "conditioning_1": ["15", 0], "conditioning_2": ["16", 0] }, "class_type": "ConditioningCombine" }
}
EOF
# Generiert einen zufälligen Seed
local seed=$RANDOM
# Hier überschreibe ich die Werte, die beim Export eingetragen waren
local workflow_payload
workflow_payload=$(jq --arg prompt "$positive_prompt" \
--arg neg_prompt "$image_negative_prompt" \
--argjson w "$image_width" \
--argjson h "$image_height" \
--argjson s "$image_steps" \
--argjson c "$image_cfg" \
--argjson seed "$seed" \
'.["17"].inputs.value=$prompt |
.["18"].inputs.value=$neg_prompt |
.["24"].inputs.width=$w |
.["24"].inputs.height=$h |
.["22"].inputs.steps=$s |
.["22"].inputs.cfg=$c |
.["22"].inputs.noise_seed=$seed' <<< "$workflow_template")
echo "› [2/4] Sende Generierungs-Anfrage an ComfyUI-Server..."
# Sendet den Workflow an die /prompt API und erhält eine prompt_id zurück
local queue_response
queue_response=$(printf '{"prompt":%s}' "$workflow_payload" | curl -s --fail -X POST -H "Content-Type: application/json" -d @- "$comfyui_server_url/prompt")
local prompt_id
prompt_id=$(echo "$queue_response" | jq -r .prompt_id)
if [[ -z "$prompt_id" || "$prompt_id" == "null" ]]; then
echo "Fehler: ComfyUI gab keine 'prompt_id' zurück. Server-Antwort:" 1>&2
echo "$queue_response" 1>&2
return 1
fi
echo " Bildgenerierung in Warteschlange (ID: $prompt_id)..."
echo "› [3/4] Warte auf Fertigstellung..."
# Fragt die /history API so lange ab, bis der Job abgeschlossen ist
local history_response
while true; do
history_response=$(curl -s --fail "$comfyui_server_url/history/$prompt_id")
if echo "$history_response" | jq -e ".\"$prompt_id\"" > /dev/null; then
break
fi
sleep 2
done
echo "› [4/4] Lade generiertes Bild herunter..."
# Extrahiert die Bild-Metadaten aus der History-Antwort
local image_data
image_data=$(echo "$history_response" | jq -r ".\"$prompt_id\".outputs[\"$comfyui_save_node_id\"].images[0]? // \"\"")
if [[ -z "$image_data" ]]; then
echo "Fehler: Bild-Metadaten konnten in der ComfyUI-Antwort nicht gefunden werden." 1>&2
echo "DEBUG: Komplette History-Antwort von ComfyUI (ID: $prompt_id):" 1>&2
echo "$history_response" | jq . 1>&2
return 1
fi
local filename subfolder type
filename=$(echo "$image_data" | jq -r .filename)
subfolder=$(echo "$image_data" | jq -r .subfolder)
type=$(echo "$image_data" | jq -r .type)
if [[ -z "$filename" ]]; then
echo "Fehler: Der Dateiname in den Bild-Metadaten ist leer." 1>&2
return 1
fi
# Baut die Download-URL zusammen und lädt das Bild herunter
local download_url="$comfyui_server_url/view?filename=${filename}&subfolder=${subfolder}&type=${type}"
curl -s --fail -o "$output_filename" "$download_url"
echo " Bild erfolgreich als '$output_filename' gespeichert."
}
# --- Script-Aufruf ---
if [[ $# -ne 2 ]]; then
echo "Verwendung: $0 \"<Prompt für das Bild>\" <output_dateiname.png>"
exit 1
fi
generiere_titelbild "$1" "$2"
Die Funktionsweise im Detail:
- JSON-Template: Das mit
read
eingeleseneworkflow_template
ist eine 1:1-Kopie der exportiertenworkflow_api.json
. Es dient als Vorlage. - Parameterisierung mit
jq
: Der Befehljq
ist das Herzstück der Dynamik. Er nimmt das Template entgegen und ersetzt die Werte an spezifischen Stellen (.["17"].inputs.value=$prompt
) durch die in der Shell definierten Variablen. So werden Prompt, Bildgröße und andere Parameter zur Laufzeit in den Workflow "injiziert". - Anstoßen der Generierung: Ein
curl
-Befehl sendet das modifizierte JSON-Objekt perPOST
-Request an den/prompt
-Endpunkt der ComfyUI-API. Der Server nimmt den Auftrag entgegen und antwortet mit einerprompt_id
. - Warten auf das Ergebnis: Da die Bildgenerierung einige Sekunden dauern kann, wird der
/history/{prompt_id}
-Endpunkt in einerwhile
-Schleife abgefragt. Sobald die Antwort ein Objekt mit der entsprechendenprompt_id
enthält, ist der Prozess abgeschlossen. - Bild herunterladen: Aus der finalen History-Antwort werden mit
jq
der Dateiname, der Unterordner und der Typ des generierten Bildes extrahiert. Diese Informationen werden genutzt, um eine URL für den/view
-Endpunkt zu konstruieren und das fertige Bild mit einem letztencurl
-Aufruf herunterzuladen.
Das Script in Aktion
Um das Script zu verwenden, wird es mit zwei Argumenten aufgerufen: dem gewünschten Prompt in Anführungszeichen und dem Zieldateinamen.
./generate_image.sh "Midshot, seductive smiling woman, dusk setting, outdoor, floral dress, black belt, standing, bokeh background, street lights, tree-lined pathway, medium shot, contemplative expression, curly hair, natural makeup, hoop earrings, hand in pocket, warm color tone, blurred background, shallow depth of field, fashionable attire, young adult, dusk lighting
" "pretty_woman.png"
› [1/4] Bereite Workflow für ComfyUI vor...
› [2/4] Sende Generierungs-Anfrage an ComfyUI-Server...
Bildgenerierung in Warteschlange (ID: 0a8b3c9d-8f9e-4b1c-8a1e-5d9c7b0f6a2d)...
› [3/4] Warte auf Fertigstellung...
› [4/4] Lade generiertes Bild herunter...
Bild erfolgreich als 'pretty_woman.png' gespeichert.
Die Konsole gibt den Fortschritt aus und nach kurzer Zeit liegt das fertige Bild im aktuellen Verzeichnis zur weiteren Verarbeitung im Script. Und das sieht dann ähnlich aus, wie im Browserfenster der ComfyUI. Aufgrund des zufälligen seed-value ist die Darstellung natürlich nicht identisch, aber doch sehr ähnlich:

Die Ergebnisse sind auf jeden Fall sehr beeindruckend, dafür dass alles auf dem eigenen Rechner erstellt wurde und keine Daten ins Internet abgeflossen sind.
Noch ein Beispiel: open-webui
Natürlich kann man auch einer webbasierten KI das Erzeugen von Bildern und Sprache ermöglichen. Lässt man vor dem Export der API-JSON-Datei den Positiv-Prompt leer und hinterlegt anschließend den Inhalt der Datei in open-webui, dann trägt open-webui den Prompt automatisch in das erste Eingabefeld ein, das leer ist.

Beschreibt man in seinem lokalen KI-Chat ein Bild, ruft open-webui dieses gemäß der im JSON-Objekt hinterlegten Workflow-Beschreibung bei ComfyUI ab:

Enorme Flexibilität
Man erkennt schnell, welche Freiheiten sich daraus ergeben. So ließe sich etwa ein Script erstellen, das bei jedem Bedarf an generativen Elementen – ob Musik, Text, Bild oder Video – lediglich auf ComfyUI zurückgreift. Allein das JSON-Objekt entscheidet dabei, welchen Medientyp man erhält. Und sogar die Beschreibung des Workflows, sprich das JSON-Objekt selbst, kann sich mit ollama spontan erzeugen lassen.
Natürlich kann ich, falls ich nicht selbst programmieren möchte, auch über n8n auf ComfyUI zugreifen. Mit diesen leistungsstarken Werkzeugen sind die Grenzen einzig durch die eigene Kreativität gesetzt 😄.
Schattenseiten
Diese Technologie eröffnet die Möglichkeit, unzensierte und potenziell schädliche Inhalte zu produzieren. Beispielsweise könnten Videos entstehen, die Veo3 ablehnen würde; durch frei wählbare virtuelle Darsteller ließen sich sogar Entführungen mit quälenden Szenen inszenieren. Solche Inhalte bergen ein hohes Missbrauchsrisiko — von Erpressung bis hin zur bewussten Verbreitung von Fake-News — und werfen ernste ethische und rechtliche Fragen auf.
Aber es sind auch bereits die Abzocker selbst durch KI ausgetrickst worden, was eindrucksvoll zeigt, wie echt die Ergebnisse wirken.
Fazit
ComfyUI beweist eindrucksvoll, dass eine grafische, node-basierte Oberfläche und eine leistungsstarke, skriptbare API sich nicht ausschließen. Die Möglichkeit, komplexe Mediengenerierungs-Workflows visuell zu entwerfen und sie anschließend per JSON-Export in automatisierte Prozesse zu überführen, eröffnet ein enormes Potenzial. Ob für die automatische Erstellung von Social-Media-Assets, die Generierung von Content für Blogs oder die Integration in größere Multimedia-Pipelines – die Kommandozeile wird damit zur Brücke zwischen Code und Kreativität.