Phase 5: Der eigene API Dienst mit FastAPI

Bis hierhin läuft alles auf dem Server selbst. Jetzt kommt der interessante Teil. Wir bauen einen HTTP Endpunkt, den Du von jedem Rechner im Netzwerk aufrufen kannst. Prompt rein, WAV Datei raus.

Das Repository liefert dafür nur eine Gradio Oberfläche. Die ist für manuelles Ausprobieren nett, für die Anbindung eigener Anwendungen aber ungeeignet. Also schreiben wir einen schlanken FastAPI Wrapper.

Der Dienst soll am Ende drei Dinge können. Beim Systemstart automatisch hochfahren. Auf Port 8770 im LAN erreichbar sein. Und WAV Dateien direkt als Response zurückgeben.

Schritt 1: Zusätzliche Pakete installieren

FastAPI und der ASGI Server Uvicorn fehlen noch in der Umgebung. Beide kommen über uv dazu.

Befehl: cd ~/stable-audio-3

Befehl: uv pip install fastapi uvicorn

Prüfe kurz, ob die Binary in der virtuellen Umgebung liegt.

Befehl: ls -l ~/stable-audio-3/.venv/bin/uvicorn

Diesen Pfad brauchst Du später für den systemd Dienst. Merke ihn Dir.

Schritt 2: Die Datei sa3_api.py anlegen

Damit dieser Beitrag nicht komplett ausufert, habe ich das Python Programm als ZIP Datei zum Download bereitgestellt.

Download: sa3_api.zip

Lade die Datei herunter, entpacke sie und lege sa3_api.py direkt in das geklonte Repository. Der Pfad muss also ~/stable-audio-3/sa3_api.py lauten.

Wichtig: nicht in den Unterordner stable_audio_3 kopieren. Das ist das Python Paket selbst. Von dort aus findet der Import das Modul nicht mehr.

Befehl: ls -l ~/stable-audio-3/sa3_api.py

Was der Python Code macht

Der Wrapper stellt einen API Endpunkt bereit, über den Du Tierstimmen, Umgebungsgeräusche und andere Audiospuren erzeugen kannst. Drei Details sind erklärungsbedürftig.

Der Lock in _generate_sync serialisiert die Anfragen. Parallele Diffusionsläufe auf einer Karte bringen kaum Durchsatz. Sie erhöhen nur das Risiko für Out of Memory. Ein Request nach dem anderen ist hier die richtige Strategie.

Der Warmup im Lifespan erzeugt beim Start einmal eine Sekunde Audio. Damit zahlt der erste echte Request nicht mehr die Initialisierungszeit der CUDA Kernel.

Der API Key wird über den Header X-API-Key geprüft. Setzt Du die Variable nicht, ist der Dienst ohne Authentifizierung erreichbar. Im eigenen LAN mag das reichen, sauber ist es nicht.

Schritt 3: Erster manueller Start

Vor dem systemd Dienst testen wir von Hand. So siehst Du Fehler sofort im Terminal.

Befehl: cd ~/stable-audio-3

Befehl: export HF_TOKEN=hf_dein_token_hier

Befehl: export CUDA_VISIBLE_DEVICES=0

Wenn Du einen API Key möchtest, setzt Du zusätzlich diese Variable. Andernfalls lässt Du sie einfach weg.

Befehl: export SA3_API_KEY=mein-geheimer-key

Jetzt der Start:

Befehl: uv run uvicorn sa3_api:app --host 0.0.0.0 --port 8770

Wichtig ist --host 0.0.0.0. Der Standardwert wäre 127.0.0.1. Dann wäre der Dienst nur lokal erreichbar und Dein Ziel verfehlt.

Der Start dauert einen Moment. Das Modell wird geladen und der Warmup läuft. Warte auf die Zeilen Warmup ok und Application startup complete.

Schritt 4: Der erste Test

Öffne eine zweite SSH Sitzung. Zuerst der Gesundheitscheck direkt auf dem Server.

Befehl: curl http://localhost:8770/health

Und derselbe Aufruf über das Netzwerk, von Deinem Arbeitsrechner aus.

Befehl: curl http://<IP-Adresse des Servers>:8770/health

Die Antwort enthält den Modellnamen und den Namen der GPU. Jetzt die eigentliche Generierung. So sieht der Aufruf mit API Key aus:

Befehl: curl -X POST http://localhost:8770/generate -H "Content-Type: application/json" -H "X-API-Key: mein-geheimer-key" -d '{"prompt":"cow mooing in a barn, close mic","duration":4}' --output /tmp/test.wav

Und so ohne API Key, hier mit der IP Adresse meines Servers:

Befehl: curl -X POST http://192.168.2.119:8770/generate -H "Content-Type: application/json" -d '{"prompt":"cow mooing in a barn, close mic","duration":4}' --output /tmp/test.wav

Achte darauf, den Parameter -H wirklich wegzulassen, wenn kein API Key gesetzt ist. Ein einzelnes -H ohne Wert schluckt den nachfolgenden Parameter. curl meldet dann eine fehlerhafte URL.

Kontrolle:

Befehl: ffprobe /tmp/test.wav

Wenn hier 44100 Hz und vier Sekunden stehen, läuft der Dienst. Ein Hinweis noch: bei --output schreibt curl auch Fehlermeldungen in die Zieldatei. Mit file /tmp/test.wav siehst Du sofort, ob echtes Audio drin ist oder eine JSON Fehlermeldung.

Den manuellen Start beendest Du jetzt mit Strg C.

Schritt 5: Die Firewall öffnen (optional)

Falls ufw aktiv ist, blockiert es eventuell den Port. In diesem Fall gibst Du ihn für Dein eigenes Subnetz frei.

Befehl: sudo ufw status

Befehl: sudo ufw allow from 192.168.2.0/24 to any port 8770 proto tcp

Passe das Subnetz an Dein Netzwerk an. Eine Freigabe für die ganze Welt braucht hier niemand.

Schritt 6: Die Environment Datei anlegen

Der Hugging Face Token und der API Key gehören nicht in die Unit Datei. Die liegt unter /etc und ist für alle lesbar. Besser ist eine separate Datei mit stark eingeschränkten Rechten.

Befehl: sudo nano /etc/stable-audio-3.env

Kopiere den folgenden Inhalt hinein, natürlich mit Deinen eigenen Werten.

# Pflicht: Zugang zu den Modellgewichten
HF_TOKEN=hf_dein_token_hier

# Modellvariante
SA3_MODEL=small-sfx

# Muss dem Index aus nvidia-smi entsprechen.
# Bei nur einer Karte ist das die 0.
CUDA_VISIBLE_DEVICES=0

# Optional: nur setzen, wenn Du einen API Key nutzen willst
#SA3_API_KEY=mein-geheimer-key

# Optional: nur setzen, wenn Du den Cache verschoben hast
#HF_HOME=/opt/hf-cache

Speichern mit Strg O, beenden mit Strg X.

Zwei Punkte sind hier wichtig. Setze keine Anführungszeichen um die Werte, systemd nimmt sie sonst als Teil des Wertes. Und HF_HOME muss exakt dem Wert entsprechen, den Du in Phase 3 verwendet hast. Sonst lädt der Dienst alle Gewichte erneut herunter.

Jetzt noch die Rechte einschränken.

Befehl: sudo chown root:root /etc/stable-audio-3.env

Befehl: sudo chmod 600 /etc/stable-audio-3.env

Schritt 7: Die systemd Datei anlegen

Befehl: sudo nano /etc/systemd/system/stable-audio-3.service

Kopiere den folgenden Inhalt hinein:

[Unit]
Description=Stable Audio 3 SFX API
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=ingmar
Group=ingmar
WorkingDirectory=/home/ingmar/stable-audio-3
EnvironmentFile=/etc/stable-audio-3.env
ExecStart=/home/ingmar/stable-audio-3/.venv/bin/uvicorn sa3_api:app --host 0.0.0.0 --port 8770
Restart=on-failure
RestartSec=10
TimeoutStartSec=300
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Speichern mit Strg O, beenden mit Strg X.

Drei Punkte machen hier häufig Ärger.

  • Erstens der Pfad zu uvicorn. Ich rufe direkt die Binary aus der virtuellen Umgebung auf, nicht uv run. Das ist robuster. systemd hat kein sinnvolles PATH und uv würde zusätzlich versuchen, die Umgebung zu synchronisieren.
  • Zweitens der Benutzer. Er muss dem Besitzer des Repositorys und des Hugging Face Caches entsprechen. Bei mir ist das ingmar. Passe alle vier Zeilen an, in denen mein Benutzername vorkommt.
  • Drittens TimeoutStartSec. Modell laden und Warmup brauchen Zeit. Der Standardwert von 90 Sekunden kann knapp werden.

Schritt 8: Dienst aktivieren und starten

Damit der Dienst wirklich läuft, musst Du ihn aktivieren und starten. Das machst Du mit den folgenden drei Befehlen.

Befehl: sudo systemctl daemon-reload

Befehl: sudo systemctl enable stable-audio-3.service

Befehl: sudo systemctl start stable-audio-3.service

Wenn alles geklappt hat, prüfst Du den Status wie folgt:

Befehl: sudo systemctl status stable-audio-3.service

Erwartet wird active (running). Direkt nach dem Start läuft noch der Warmup. Das Log zeigt Dir den Fortschritt.

Befehl: sudo journalctl -u stable-audio-3.service -f

Beenden der Logausgabe mit Strg C.

Schritt 9: Der Test vom anderen Rechner

Jetzt kommt der Moment der Wahrheit. Wechsle auf Deinen Arbeitsrechner und führe im Terminal den folgenden Befehl aus. Er zeigt Dir, ob der Dienst läuft.

Befehl: curl http://192.168.2.119:8770/health

Kommt eine Antwort mit dem Status ok, ist der Dienst im Netzwerk erreichbar.

Mit dem folgenden Befehl generierst Du die erste Tierstimme. Schau Dir den Aufbau in Ruhe an, um ihn zu verstehen.

Befehl: curl -X POST http://192.168.2.119:8770/generate -H "Content-Type: application/json" -d '{"prompt":"wolf howling in a distant forest at night","duration":6}' --output wolf.wav

Oder dieses Beispiel:

Befehl: curl -X POST http://192.168.2.119:8770/generate -H "Content-Type: application/json" -d '{"prompt":"cow mooing in a barn, close mic","duration":4}' --output cow.wav

Die Datei landet in dem Verzeichnis, in dem Du Dich gerade befindest. Öffne sie mit einem beliebigen Player.

Die interaktive Dokumentation Deines API Endpunktes liefert FastAPI gleich mit. Rufe sie im Browser auf:

Adresse: http://192.168.2.119:8770/docs

Dort testest Du Prompts direkt im Browser, ganz ohne curl.

Schritt 10: Anbindung aus Python

Für Deine eigene Anwendung reichen ein paar Zeilen.

import requests

resp = requests.post(
    "http://192.168.2.119:8770/generate",
    json={"prompt": "heavy rain on a metal roof, steady", "duration": 10},
    timeout=120,
)
resp.raise_for_status()

with open("regen.wav", "wb") as f:
    f.write(resp.content)

print("Dauer:", resp.headers.get("X-Generation-Seconds"), "Sekunden")

Wenn Du einen API Key gesetzt hast, ergänzt Du noch den passenden Header.

headers={"X-API-Key": "mein-geheimer-key"},

Und wenn Du das Audio lieber eingebettet bekommst, setzt Du "format": "json". Dann liefert der Dienst die Datei als Base64 String zurück, zusammen mit Samplerate und Generierungsdauer. Praktisch für Web Frontends.

Nützliche Befehle im Betrieb

Zweck Befehl
Dienst neu starten sudo systemctl restart stable-audio-3
Dienst stoppen sudo systemctl stop stable-audio-3
Autostart deaktivieren sudo systemctl disable stable-audio-3
Letzte 100 Logzeilen sudo journalctl -u stable-audio-3 -n 100
Logs seit heute sudo journalctl -u stable-audio-3 --since today
VRAM Belegung prüfen nvidia-smi

Ein Wort zur Sicherheit

Der Dienst läuft unverschlüsselt über HTTP. Im eigenen LAN ist das vertretbar. Sobald er über Dein Netzwerk hinaus erreichbar sein soll, gehört ein Reverse Proxy davor. Nginx mit einem Let’s Encrypt Zertifikat ist der klassische Weg.

Meine bevorzugte Variante ist eine andere. Ich lasse den Dienst an das LAN gebunden und greife von unterwegs über Tailscale darauf zu. Kein offener Port nach außen, keine Zertifikatsverwaltung, verschlüsselt ist es trotzdem.

Und noch ein Hinweis: der API Key im Klartext Header ist keine ernsthafte Authentifizierung. Er hält versehentliche Zugriffe fern, mehr nicht. Für ein Homelab reicht das.

Troubleshooting

Installation und Modell

Symptom Ursache und Lösung
401 oder 403 beim Download Lizenz auf Hugging Face nicht bestätigt oder HF_TOKEN fehlt
RuntimeError: No CUDA GPUs are available CUDA_VISIBLE_DEVICES zeigt auf einen Index, den es nicht gibt. Mit nvidia-smi prüfen.
CUDA out of memory Andere Prozesse belegen die Karte, Belegung mit nvidia-smi prüfen
Torch findet keine GPU CUDA Version passt nicht zum Treiber, Torch neu installieren
Volle Systempartition HF_HOME wurde nicht gesetzt, Cache liegt unter home
Erster Lauf sehr langsam Normal, Modelldownload und Initialisierung
flash_attn not installed Kein Fehler. Small SFX braucht Flash Attention nicht.

Der Dienst

Symptom Ursache und Lösung
status 203/EXEC Der Pfad in ExecStart stimmt nicht. Mit ls prüfen.
Dienst startet, dann timeout TimeoutStartSec erhöhen, Warmup braucht länger.
401 invalid api key Header X-API-Key fehlt oder stimmt nicht mit der env Datei überein.
Connection refused von extern host 0.0.0.0 fehlt oder die Firewall blockt.
ModuleNotFoundError stable_audio_3 WorkingDirectory zeigt nicht auf das Repository, oder sa3_api.py liegt im falschen Ordner.
Permission denied auf den Cache HF_HOME gehört einem anderen Benutzer als der Dienst.
curl: URL rejected Ein Parameter im curl Aufruf hat keinen Wert, meist ein leeres -H.

Ein Wort zur Lizenz

Stable Audio 3 steht unter der Stability AI Community License. Das ist keine OSI zertifizierte Open Source Lizenz. Die Nutzung ist kostenlos für Forschung, Privatpersonen und Unternehmen unterhalb einer Million US Dollar Jahresumsatz. Darüber ist eine Enterprise Lizenz erforderlich.

Positiv fällt die Datenbasis auf. Stability AI hat ausschließlich lizenziertes Material und Creative Commons Aufnahmen verwendet. Ein Teil stammt aus dem Freesound Archiv unter CC0, CC BY und CC Sampling Plus. Das ist bei generativen Audiomodellen keine Selbstverständlichkeit.

Zusätzlich gelten die Gemma Terms of Use für den Text Encoder. Deren Nutzungsbeschränkungen in Abschnitt 3.2 solltest Du vor einem produktiven Einsatz kennen.

Fazit

Die Installation hat mich keine halbe Stunde gekostet. Der Download der Gewichte war der längste Teil. Das ist für ein generatives Audiomodell erfreulich unspektakulär.

Was mich überzeugt: der minimale Ressourcenbedarf. Zwei bis drei Gigabyte VRAM sind nichts. Das Modell teilt sich die Karte problemlos mit anderen Diensten. Für ein Homelab, in dem ohnehin jede GPU doppelt belegt ist, macht das einen echten Unterschied.

Was Du wissen solltest: Prompt Engineering ist auch hier entscheidend. Vage Beschreibungen liefern matschige Ergebnisse. Präzise Angaben zu Mikrofonabstand, Umgebung und Aufnahmequalität verbessern die Ausgabe deutlich. Für Umgebungsgeräusche, Regen, Wind oder mechanische Sounds liefert das Modell erstaunlich brauchbares Material.

Der eigentliche Gewinn ist für mich aber der API Endpunkt. Damit wird aus einem Kommandozeilenwerkzeug ein Baustein, den jede Anwendung im Netzwerk nutzen kann. Genau das ist für mich souveräne KI. Keine Cloud, keine externen Zugangsdaten, keine Abhängigkeit. Alles läuft im eigenen Rack.

Im nächsten Beitrag baue ich darauf auf und binde den Endpunkt in eine konkrete Anwendung ein.

Hier geht es zum Teil 1: Stable Audio 3 Small SFX auf dem eigenen Server: Installation, Konfiguration und erster Smoke Test 1/2