Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Datenbanken 06.08.2026 · 8 min Lesezeit

Chroma mit Docker installieren: Entwicklerfreundliche Embedding-Datenbank für RAG-Pipelines

Chroma 1.5.9 mit Docker Compose betreiben: Daten unter /data, Port nur lokal, Anmeldung per Caddy statt der entfallenen Token-Auth, dazu Python-Client, Backup mit Restore-Test und typische Fehler aus einem Praxistest.

Geprüft am 14.10.2026 · für chroma 1.5.9

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Dunkle Halle mit leuchtenden Lichtpunkten wie ein Netzwerk über einem flachen Gerät, dahinter ein Bergsee in der Dämmerung

Chroma ist eine quelloffene Vektordatenbank (Apache-2.0), die Texte zusammen mit ihren Embeddings speichert und per Ähnlichkeitssuche wiederfindet. In RAG-Anwendungen (Retrieval Augmented Generation) liefert sie einem Sprachmodell die passenden Textstellen aus eigenen Dokumenten. Seit Version 1.0 ist der Server in Rust neu geschrieben. Dabei sind die eingebaute Anmeldung, die alten Umgebungsvariablen und der frühere Datenpfad entfallen. Diese Anleitung beschreibt den Betrieb von Chroma 1.5.9 mit Docker Compose so, wie er heute funktioniert: Daten unter /data, Port nur lokal, Anmeldung über einen vorgeschalteten Reverse Proxy.

Voraussetzungen

  • Linux-Server oder VPS mit x86_64 oder ARM64 (das Image gibt es für amd64 und arm64), 2 CPU-Kerne, 4 GB RAM. Chroma selbst belegte im Test im Leerlauf rund 13 MB RAM, der Bedarf wächst mit der Zahl der Vektoren, weil der HNSW-Index im Arbeitsspeicher liegt.
  • Etwa 1 GB Speicher für die Images (Chroma rund 176 MB, Caddy rund 24 MB) plus Platz für Ihre Daten, SSD empfohlen.
  • Docker Engine mit Compose-Plugin (Docker-Installation).
  • Für Zugriff aus dem Netz: eine Domain, die auf den Server zeigt, und ein Reverse Proxy mit TLS (Caddy als Reverse Proxy einrichten).
  • Ein Embedding-Modell auf Seite der Anwendung. Chroma speichert Vektoren, das Erzeugen der Embeddings übernimmt der Client oder ein separater Dienst.

Eckdaten

PunktWert
Imagechromadb/chroma:1.5.9 (auch ghcr.io/chroma-core/chroma)
Version1.5.9, Release vom 5. Mai 2026, aktuelles Release am 14.10.2026
Port im Container8000 (HTTP, API unter /api/v2)
Datenverzeichnis/data (SQLite-Datei chroma.sqlite3 plus ein Ordner je Index)
Konfigurationoptionale YAML-Datei unter /config.yaml
Startbefehl im Imagechroma run /config.yaml
Authentifizierungkeine eingebaut, Reverse Proxy nötig

Schritt 1: Projektordner anlegen

Legen Sie einen Projektordner mit einem Unterverzeichnis für die Daten an. Chroma läuft im Container als root, die Dateien in chroma-data gehören daher später root. Das ist für den Betrieb unkritisch, wirkt sich aber auf Backup und Restore aus (Schritt 6).

mkdir -p /opt/chroma/chroma-data
cd /opt/chroma

Verifizieren: ls -la /opt/chroma zeigt das leere Verzeichnis chroma-data.

Schritt 2: Passwort für den Proxy erzeugen

Die Chroma-Dokumentation zur Migration auf 1.0 hält fest, dass Chroma keine eingebauten Authentifizierungsverfahren mehr mitliefert. Die in älteren Anleitungen verbreiteten Variablen CHROMA_SERVER_AUTHN_PROVIDER und CHROMA_SERVER_AUTHN_CREDENTIALS haben keine Wirkung. Im Test startete ein Container mit genau diesen Variablen ohne Warnung, und /api/v2/heartbeat antwortete ohne Token mit HTTP 200. Die Anmeldung übernimmt deshalb Caddy. Erzeugen Sie zuerst einen bcrypt-Hash für das Passwort:

docker run --rm caddy:2 caddy hash-password --plaintext 'IhrLangesPasswort'

Die Ausgabe beginnt mit $2a$14$. Schreiben Sie das Passwort in Ihren Passwortmanager, im Caddyfile steht nur der Hash.

Verifizieren: Der Befehl gibt genau eine Zeile mit einem etwa 60 Zeichen langen Hash aus, der mit $2a$ beginnt.

Schritt 3: Compose-Datei und Caddyfile

Die compose.yaml startet Chroma und Caddy in einem gemeinsamen Netz. Chroma veröffentlicht Port 8000 nur an 127.0.0.1, damit Skripte auf dem Server direkt zugreifen können, das Netz aber nicht. Caddy lauscht intern auf 8080 und leitet nach erfolgreicher Anmeldung an chroma:8000 weiter.

services:
  chroma:
    image: chromadb/chroma:1.5.9
    container_name: chroma
    restart: unless-stopped
    volumes:
      - ./chroma-data:/data
    ports:
      - "127.0.0.1:8000:8000"

  caddy:
    image: caddy:2
    container_name: chroma-proxy
    restart: unless-stopped
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
    ports:
      - "127.0.0.1:8080:8080"
    depends_on:
      - chroma

Das Caddyfile im selben Ordner, mit Ihrem Hash aus Schritt 2:

:8080 {
  basic_auth {
    rag $2a$14$HIER_IHR_HASH
  }
  reverse_proxy chroma:8000
}

Zwei Punkte sind bewusst weggelassen. Erstens gibt es keinen healthcheck: Das Image basiert auf Debian 13 ohne curl und wget, ein Healthcheck mit diesen Werkzeugen meldet den Container dauerhaft als unhealthy. Zweitens fehlt eine eigene config.yaml, weil die im Image enthaltene Datei bereits persist_path: "/data" setzt. Eine eigene Datei binden Sie nur ein, wenn Sie Werte wie allow_reset oder CORS ändern wollen. Sie ersetzt dann die mitgelieferte Datei vollständig, persist_path: "/data" muss darin also wieder stehen.

Verifizieren: docker compose config gibt die Konfiguration ohne Fehlermeldung aus und zeigt bei beiden Ports die Adresse 127.0.0.1.

Schritt 4: Stack starten

docker compose up -d
docker compose ps
docker compose logs chroma | tail -n 12

Im Log erscheint nach dem Chroma-Banner die Zeile Saving data to: /data. Steht dort ein anderer Pfad, wurde eine eigene config.yaml ohne persist_path eingebunden.

Verifizieren: docker compose ps zeigt beide Container mit Status Up. curl -s http://127.0.0.1:8000/api/v2/heartbeat liefert JSON der Form {"nanosecond heartbeat":…}.

Schritt 5: Zugriff über den Proxy und erste Collection

Prüfen Sie zuerst, dass der Proxy ohne Anmeldung abweist:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/api/v2/heartbeat
curl -s -o /dev/null -w '%{http_code}\n' -u rag:falsch http://127.0.0.1:8080/api/v2/heartbeat
curl -s -u rag:IhrLangesPasswort http://127.0.0.1:8080/api/v2/heartbeat

Im Test lieferten die ersten beiden Aufrufe 401, der dritte den Heartbeat mit HTTP 200. Aus Python verbinden Sie sich mit dem schlanken Paket chromadb-client und übergeben den Basic-Auth-Header selbst:

pip install chromadb-client==1.5.9
import base64, chromadb

auth = base64.b64encode(b"rag:IhrLangesPasswort").decode()
client = chromadb.HttpClient(host="127.0.0.1", port=8080,
                             headers={"Authorization": "Basic " + auth})
col = client.get_or_create_collection("handbuch")
col.add(ids=["d1", "d2"],
        embeddings=[[0.1, 0.2, 0.3], [0.9, 0.8, 0.7]],
        documents=["Urlaubsantrag im Portal stellen",
                   "Drucker im 2. OG neu starten"])
print(col.count())
print(col.query(query_embeddings=[[0.88, 0.8, 0.7]], n_results=1)["documents"])

Das Beispiel übergibt Vektoren direkt, damit es ohne Modell-Download läuft. In einer echten Anwendung erzeugt ein Embedding-Modell die Vektoren. Alle Einträge einer Collection müssen dieselbe Dimension haben, und für Speichern und Suchen muss dasselbe Modell verwendet werden.

Verifizieren: Das Skript gibt 2 und [['Drucker im 2. OG neu starten']] aus. Im Test lieferte der Client ohne Header beim Heartbeat einen Fehler, mit Header die Treffer wie oben.

Schritt 6: Backup und Wiederherstellung

Alle Daten liegen in chroma-data: die SQLite-Datei chroma.sqlite3 mit Metadaten und Dokumenten sowie je Collection ein Ordner mit dem Vektorindex. Sichern Sie beides gemeinsam und im gestoppten Zustand, damit SQLite-Datei und Index zusammenpassen. Da die Dateien root gehören, packen Sie das Archiv über einen kurzlebigen Container:

cd /opt/chroma
docker compose stop chroma
docker run --rm -v "$PWD/chroma-data":/data:ro -v "$PWD":/backup alpine \
  tar czf /backup/chroma-$(date +%F).tgz -C /data .
docker compose start chroma

Die Wiederherstellung läuft umgekehrt, ebenfalls bei gestopptem Dienst:

docker compose stop chroma
docker run --rm -v "$PWD/chroma-data":/data -v "$PWD":/backup alpine \
  sh -c 'rm -rf /data/* && tar xzf /backup/chroma-2026-09-30.tgz -C /data'
docker compose start chroma

Im Test wurde die Collection nach dem Backup gelöscht, die Liste der Collections war danach leer. Nach dem Restore meldete count wieder beide Einträge. Kopieren Sie das Archiv anschließend auf ein anderes System, etwa mit restic.

Verifizieren: tar tzf chroma-2026-09-30.tgz listet ./chroma.sqlite3 und mindestens einen Index-Ordner. Nach einem Probe-Restore liefert col.count() den Wert von vor dem Backup.

Schritt 7: Updates

Lesen Sie vor jedem Update die Release Notes und die Migrationsseite der Chroma-Dokumentation. Erstellen Sie ein Backup nach Schritt 6, ändern Sie dann den Tag in der compose.yaml und starten Sie neu:

docker compose pull
docker compose up -d
docker compose logs chroma | tail -n 12

Bleiben Sie bei festen Versionsnummern. Unter chromadb/chroma erscheinen fast täglich Entwicklungsstände wie 1.5.10.dev304, die nicht für den Betrieb gedacht sind. Halten Sie Client und Server auf derselben Minor-Version.

Verifizieren: docker compose images zeigt den neuen Tag, der Heartbeat antwortet, und col.count() liefert dieselbe Zahl wie vor dem Update.

Typische Fehler

  • Daten nach Neuerstellen weg: Das Volume zeigt auf /chroma/chroma, den Pfad der Versionen vor 1.0. Seit 1.0 speichert Chroma unter /data. Korrigieren Sie den Mount, bevor Sie Daten laden.
  • API ohne Anmeldung erreichbar: CHROMA_SERVER_AUTHN_* gesetzt und Port mit 8000:8000 veröffentlicht. Die Variablen wirken nicht mehr. Port an 127.0.0.1 binden und Proxy mit Auth vorschalten.
  • Container dauerhaft unhealthy: Healthcheck mit curl oder wget. Beide fehlen im Image. Healthcheck entfernen und von außen per Heartbeat überwachen.
  • {"error":"Unimplemented","message":"The v1 API is deprecated. Please use /v2 apis"} mit HTTP 410: Ein alter Client oder ein Skript ruft /api/v1 auf. Client aktualisieren, Pfade auf /api/v2 umstellen.
  • Collection expecting embedding with dimension of 3, got 2: Die Collection wurde mit einem anderen Embedding-Modell befüllt. Pro Modell eine eigene Collection anlegen.
  • {"error":"ChromaError","message":"Reset is disabled by config"} mit HTTP 403: Das ist die sichere Voreinstellung. allow_reset: true nur in Testumgebungen setzen.
  • Bind for 127.0.0.1:8000 failed: port is already allocated: Ein anderer Dienst belegt den Port. Linke Seite der Portzuordnung ändern, etwa 127.0.0.1:8010:8000.

Häufige Fragen

Warum meldet /api/v2/version 1.0.0?

Im Test mit dem Image 1.5.9 lieferte /api/v2/version den Wert "1.0.0". Die Image-Version prüfen Sie deshalb mit docker compose images, nicht über diesen Endpunkt.

Braucht Chroma PostgreSQL oder Redis?

Nein. Der Einzelknoten speichert alles in SQLite und lokalen Indexdateien. Eine verteilte Variante mit mehreren Diensten gibt es, sie ist für kleine Umgebungen aber nicht nötig.

Kann ich statt Basic Auth einen Token verwenden?

Ja, sofern Ihr Proxy das prüft, zum Beispiel über einen festen Header. Der Client übergibt beliebige Header über headers=. Basic Auth mit langem Passwort und TLS ist für die meisten Umgebungen ausreichend und einfach zu betreiben.

Wann ist eine andere Vektordatenbank sinnvoller?

Wenn Sie Benutzerverwaltung, API-Keys oder Snapshots direkt in der Datenbank brauchen, bietet Qdrant diese Funktionen eingebaut (Lokales RAG-System mit Qdrant). Chroma punktet mit einfacher Python-API und geringem Ressourcenbedarf.

Fazit

Chroma 1.x ist schnell eingerichtet, verlangt aber drei bewusste Entscheidungen: Daten unter /data, Port nur lokal und Anmeldung außerhalb der Datenbank. Mit Caddy als Proxy, festem Versions-Tag und einem regelmäßig geprüften Archiv des Datenverzeichnisses eignet sich Chroma gut als Wissensspeicher für interne RAG-Anwendungen.

Weiterführende Anleitungen und Quellen