mem0 auf VPS selbst hosten: RAM, Compose und TLS
Betreiben Sie mem0 auf dem eigenen VPS: 1 GB RAM fur drei Container, mindestens 8 GB mit lokalem Ollama-Modell, Compose, TLS und localhost-Bindung.
Was Self-Hosting von mem0 auf einem VPS tatsächlich an RAM kostet
Self-Hosting von mem0 bedeutet, drei Container zu betreiben: den FastAPI-Memory-Server, Postgres mit der Erweiterung pgvector und ein Next.js-Dashboard. mem0 ist eine Memory-Schicht für Agents. Sie senden eine Unterhaltung an mem0. Ein Sprachmodell extrahiert daraus dauerhafte Fakten. Diese Fakten werden als Vektoren gespeichert, damit eine spätere Abfrage die relevanten Fakten wieder abrufen kann.
Planen Sie für die drei Container ungefähr 1 GB residenten Speicher ein. Nach dem Erstellen der Images benötigen Sie 3 bis 4 GB Speicherplatz. Ein VPS mit 2 GB RAM betreibt diesen Stack problemlos, wenn das Sprachmodell an einem anderen Ort läuft. Läuft das Modell auf demselben System über Ollama, benötigt es deutlich mehr Ressourcen als alle anderen Komponenten: Ein auf 4 Bit quantisiertes 8B-Modell benötigt allein ungefähr 6 GB RAM. Eine vollständig lokale Installation beginnt daher bei 8 GB.
Übernehmen Sie diese Werte nicht aus einem Blogbeitrag, auch nicht aus diesem. Messen Sie den Stack, den Sie tatsächlich erstellt haben.
docker compose ps
docker stats --no-stream
docker system df -vdocker stats gibt den residenten Speicher pro Container aus. docker system df -v gibt den Speicherplatz aus, den jedes Image und jedes Volume belegt.
Der Speicherbedarf im laufenden Betrieb entspricht nicht dem Spitzenwert. docker compose up -d --build kompiliert das Next.js-Dashboard. Dieser Node-Build ist der speicherintensivste Moment der gesamten Installation. Auf einem VPS mit 1 GB RAM beendet der Out-of-Memory-Killer des Kernels den Prozess. Der Build endet dann mit exit code 137. Bestätigen Sie zuerst die Ursache, bevor Sie nach einem Docker-Fehler suchen:
dmesg -T | grep -i "killed process"Wenn ein Server für Ihren Bedarf zu aufwendig ist, sind die kleineren Optionen eine echte Alternative. Ein lokaler Agent-Memory-Speicher ganz ohne Server und Memory, das direkt in Claude Code gespeichert wird benötigen beide keine Datenbank. Kehren Sie hierher zurück, wenn mehrere Agents oder mehrere Rechner auf dieselben Memories zugreifen müssen.
Benötige ich Neo4j für den Graph-Speicher von mem0?
Nein. Wenn eine Anleitung Sie auffordert, einen Neo4j-Container hinzuzufügen, ist sie älter als der aktuelle Code.
Der Graph-Speicher in mem0 bezeichnete früher eine externe Graphdatenbank. Sie wurde unter einem graph_store-Schlüssel konfiguriert, wobei enable_graph auf true gesetzt wurde. Der neue Speicheralgorithmus, der im April 2026 veröffentlicht wurde, hat beide Schlüssel aus dem Open-Source-SDK entfernt. Die Entitätsextraktion läuft jetzt innerhalb des normalen Add-Pfads. Die Entitäten werden in eine zweite pgvector-Sammlung geschrieben. Deren Name entspricht dem Namen der Hauptsammlung mit dem angehängten Suffix _entities. Eine Migration ist nicht erforderlich. Die integrierte Entitätsverknüpfung wird beim nächsten Add-Aufruf aktiv.
Der Verzicht auf den Graph-Speicher spart einen JVM-Container, dessen Heap und mehrere hundert Megabyte beim Image ein. Auf einem 2-GB-VPS entscheidet das darüber, ob das System läuft oder auf den Swap ausweicht.
Folgendes entfällt dabei. Früher enthielten Suchergebnisse ein relations-Feld mit den Kanten zwischen Entitäten. Dieses Feld gibt es nicht mehr. Treffer für Entitäten erhöhen jetzt die Position einer Erinnerung im kombinierten Score. Eine durchlaufbare Struktur gibt es nicht. Wenn Ihre Anwendung diese Beziehungen durchlaufen hat, speichert mem0 sie nicht mehr. Sie müssen außerhalb von mem0 eine eigene Graphdatenbank führen und sie mit Ihrem eigenen Code befüllen.
Die Compose-Datei im Repository ist für die Entwicklung vorgesehen.
server/docker-compose.yaml deklariert name: mem0-dev, und genau das tut sie auch. Lesen Sie die Datei, bevor Sie sie ausführen, denn für einen Server sind darin fünf Dinge falsch.
- Sie erstellt das Image aus
server/dev.Dockerfileund bindet mit.:/appIhr Checkout-Verzeichnis über das Image. Der Container führt dadurch aus, was in diesem Verzeichnis liegt, statt das zu verwenden, was Sie erstellt haben. - Der Befehl lautet
rm -rf /app/packages && pip install -q --force-reinstall --no-deps mem0ai && alembic upgrade head && uvicorn main:app --reload. Dadurch wirdmem0aibei jedem Start erneut von PyPI installiert. Die Version, die Ihr Server ausführt, kann sich dadurch während eines Neustarts ändern, den Sie nicht als Upgrade geplant haben. - Derselbe pip-Schritt führt dazu, dass ein Neustart ohne ausgehende Netzwerkverbindung fehlschlägt, bevor uvicorn überhaupt startet. Ihr Memory-Server ist dann nicht verfügbar, weil PyPI nicht erreichbar war.
--reloadstartet den Dateiüberwacher von uvicorn. Dieser startet den Prozess neu, wenn Sie Code bearbeiten. In der Produktion verbraucht er Speicher und einen zweiten Prozess, ohne dort einen Nutzen zu haben. Die produktiveDockerfileenthält--reloadebenfalls in ihremCMD. Sie müssen den Befehl daher in jedem Fall überschreiben.- Die veröffentlichten Ports sind
"8888:8000","8432:5432"und"3000:3000". Ein veröffentlichter Port ohne vorangestellte Adresse wird an0.0.0.0gebunden. Dadurch nimmt Postgres in dem Moment Verbindungen aus dem öffentlichen Internet auf Port 8432 an, in dem der Stack startet.
Dieser letzte Punkt verdient eine eigene Warnung. Docker veröffentlicht einen Port, indem es seine eigenen Regeln vor der von ufw verwalteten Chain einfügt. Daher schließt ufw deny 8432 keinen veröffentlichten Container-Port. Docker veröffentlicht Ports direkt an ufw vorbei erläutert die beteiligten Regeln.
Eine Compose-Datei für einen echten Server
Arbeiten Sie innerhalb von server/, behalten Sie init-db.sh an dieser Stelle bei und ersetzen Sie docker-compose.yaml durch Folgendes.
name: mem0
services:
mem0:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
env_file: .env
ports:
- "127.0.0.1:8888:8000"
networks: [mem0_network]
volumes:
- mem0_history:/app/history
depends_on:
postgres:
condition: service_healthy
command: >
sh -c "alembic upgrade head &&
uvicorn main:app --host 0.0.0.0 --port 8000"
environment:
- PYTHONUNBUFFERED=1
- DASHBOARD_URL=https://mem0.example.com
- APP_DB_NAME=mem0_app
- AUTH_DISABLED=false
- MEM0_TELEMETRY=false
postgres:
image: pgvector/pgvector:pg17
restart: unless-stopped
shm_size: "128mb"
networks: [mem0_network]
environment:
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
healthcheck:
test: ["CMD-SHELL", "pg_isready -q -U ${POSTGRES_USER:-postgres}"]
interval: 5s
timeout: 5s
retries: 5
volumes:
- postgres_db:/var/lib/postgresql/data
- ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh
mem0-dashboard:
build: ./dashboard
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
networks: [mem0_network]
environment:
- NEXT_PUBLIC_API_URL=https://mem0.example.com
- API_INTERNAL_URL=http://mem0:8000
depends_on:
mem0:
condition: service_started
volumes:
postgres_db:
mem0_history:
networks:
mem0_network:
driver: bridgeHier sind fünf Änderungen wichtig. Jede hat einen bestimmten Grund.
Jeder ports-Eintrag beginnt mit 127.0.0.1. Der Kernel akzeptiert diese Verbindungen daher nur vom Server selbst. Alles von außerhalb erreicht den Dienst über den Reverse Proxy. Nur dieser verwaltet ein Zertifikat.
Postgres hat überhaupt keinen ports-Block. Der mem0-Container erreicht Postgres über mem0_network anhand des Servicenamens. Das Veröffentlichen von 8432 bringt daher keinen Vorteil und kostet Sie einen offenen Port. Verwenden Sie docker compose exec postgres psql -U postgres, wenn Sie eine Shell benötigen.
Der Verlauf wird vom ./history-Bind-Mount in ein benanntes Volume verschoben. Ein Bind-Mount bindet die Daten an einen Pfad und eine uid auf diesem Host. Ein benanntes Volume ist dagegen ein Docker-Objekt, das Docker sichern und verschieben kann. Benannte Volumes im Vergleich zu Bind-Mounts erläutert, wann welche Variante geeignet ist.
Der Befehl entfernt --reload und behält alembic upgrade head bei. Behalten Sie diesen Migrationsschritt bei. Ohne ihn startet die Anwendung mit einer Datenbank ohne Tabellen, und jede Anfrage schlägt bei der ersten Abfrage fehl.
NEXT_PUBLIC_API_URL ist die URL, die Ihr Browser aufruft. Daher muss dort die öffentliche HTTPS-Adresse stehen, nicht http://mem0:8000. Next.js fügt jeden NEXT_PUBLIC_-Wert zur Build-Zeit fest in die Anwendung ein. Eine Änderung erfordert daher docker compose up -d --build mem0-dashboard. Ein einfacher Neustart behält den alten Wert im JavaScript bei, und das Dashboard ruft den falschen Host auf.
Geheimnisse stehen in .env, und .env bleibt aus dem Internet heraus
cd server
cp .env.example .env
openssl rand -hex 32 # paste into JWT_SECRET
openssl rand -hex 32 # paste into ADMIN_API_KEY
chmod 600 .envSetzen Sie POSTGRES_PASSWORD, JWT_SECRET und ADMIN_API_KEY. Lassen Sie AUTH_DISABLED=false unverändert. Der Name beschreibt die Funktion dieses Flags korrekt: Wenn es aktiviert ist, übergibt der Server jeden Speicherinhalt, den er hält, an jeden, der den Port erreichen kann. Setzen Sie MEM0_TELEMETRY=false, wenn das Onboarding-Ereignis nicht an ein Upstream-System gesendet werden soll.
ADMIN_API_KEY wird mit dem X-API-Key-Header anhand von secrets.compare_digest verglichen. Bei einer Übereinstimmung werden alle Datenbankabfragen übersprungen. Dabei handelt es sich um eine Root-Credential für die gesamte API. Behandeln Sie sie entsprechend: nicht in der Shell-Historie speichern, nicht in Git ablegen und nicht in eine Eingabeaufforderung einfügen. Compose-Env-Dateien und wie Geheimnisse daraus nach außen gelangen und API-Schlüssel aus dem Kontext eines Agenten heraushalten gelten hier direkt, weil Agenten die Aufrufer dieses Servers sind.
Die aus env_file geladenen Werte befinden sich in der Container-Umgebung, und docker inspect gibt sie vollständig aus. Jeder in der Gruppe docker kann sie lesen. Jeder in der Gruppe docker hat auf dem Host effektiv Root-Rechte.
TLS vor die API setzen, statt Port 8888 zu öffnen
Die API antwortet auf 127.0.0.1:8888 und das Dashboard auf 127.0.0.1:3000. nginx beendet TLS (Transport Layer Security) auf Port 443 und leitet den Datenverkehr an beide Dienste weiter.
server {
listen 443 ssl;
server_name mem0.example.com;
ssl_certificate /etc/letsencrypt/live/mem0.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mem0.example.com/privkey.pem;
location ~ ^/(memories|search|configure|auth|api-keys|docs|openapi.json) {
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 180s;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}proxy_read_timeout ist wichtiger, als es zunächst wirkt. Ein Add-Aufruf wird blockiert, während das Sprachmodell die Unterhaltung einliest und Fakten extrahiert. Ein lokales 8B-Modell auf der CPU benötigt regelmäßig länger als der nginx-Standardwert von 60 Sekunden. Der Aufrufer erhält dann 504 Gateway Time-out, während das Modell noch arbeitet und der Speicher weiterhin geschrieben wird. Dadurch entsteht ein Speicher, bei dem Ihnen ein Fehlschlag gemeldet wurde, obwohl er tatsächlich angelegt wurde.
Schließen Sie den übrigen Datenverkehr mit einer standardmäßig ablehnenden ufw-Richtlinie, sodass nur 22 und 443 geöffnet bleiben. Stellen Sie das Zertifikat mit certbot auf Ubuntu 24.04 hinter nginx aus. Wenn der Server bereits andere Anwendungen mit Traefik und dem Routing mehrerer Compose-Anwendungen veröffentlicht, fügen Sie mem0 diesem Router hinzu, statt einen zweiten Proxy zu installieren.
Smoke-Test: Ein Memory hinzufügen und wieder auslesen
export MEM0_KEY='<the ADMIN_API_KEY from .env>'
curl -sS -X POST http://127.0.0.1:8888/memories \
-H "Content-Type: application/json" \
-H "X-API-Key: $MEM0_KEY" \
-d '{"messages":[{"role":"user","content":"I deploy with Docker Compose and I run Postgres 17."}],"user_id":"smoke"}'Eine erfolgreiche Antwort ist ein JSON-Objekt mit einer results-Liste. Jeder Eintrag enthält ein id, den extrahierten memory-Text und "event": "ADD". Der aktuelle Algorithmus gibt nur ADD-Ereignisse zurück. UPDATE- und DELETE-Ereignisse wurden entfernt. Ihr Fehlen ist daher kein Fehler.
curl -sS -X POST http://127.0.0.1:8888/search \
-H "Content-Type: application/json" \
-H "X-API-Key: $MEM0_KEY" \
-d '{"query":"which database do I run?","filters":{"user_id":"smoke"},"top_k":5}'Die Information zu Postgres 17 sollte mit einem Score zurückgegeben werden. Übergeben Sie den Bezeichner innerhalb von filters, wie gezeigt. Ein user_id auf oberster Ebene funktioniert weiterhin. Der Server protokolliert Top-level user_id in /search is deprecated. Use filters={...} instead. jedes Mal, wenn Sie es verwenden.
Räumen Sie anschließend auf, damit die Testdaten echte Suchen nicht beeinflussen:
curl -sS -X DELETE "http://127.0.0.1:8888/memories?user_id=smoke" \
-H "X-API-Key: $MEM0_KEY"Wenn die Suche weniger Zeilen zurückgibt als erwartet, prüfen Sie die Standardwerte, bevor Sie die Retrieval-Funktion verantwortlich machen. In der aktuellen Version ist top_k standardmäßig auf 20 gesetzt, statt wie zuvor auf 100. threshold ist standardmäßig auf 0.1 gesetzt und nicht auf none. Dadurch werden schwache Treffer automatisch herausgefiltert. Sobald dies mit curl funktioniert, verwenden Sie dieselben Endpunkte auch für einen Agenten, entweder direkt oder über einen MCP-Server auf demselben VPS.
mem0 ganz ohne OpenAI-Key ausführen
Beginnen Sie mit dem Blocker, weil Sie bereits in den ersten fünf Minuten darauf stoßen werden. Das Server-Image enthält eine feste Auswahl an Provider-Bibliotheken, und /configure lehnt alles ab, was nicht dazugehört:
LLM provider 'ollama' is not bundled in this image. Bundled providers: openai, anthropic, gemini. To use another provider, install its Python package, rebuild the container, and extend BUNDLED_LLM_PROVIDERS in server/main.py.Sie müssen nichts neu bauen. Ollama stellt unter /v1 eine mit OpenAI kompatible API bereit, die /v1/chat/completions und /v1/embeddings abdeckt. Außerdem akzeptiert der OpenAI-Provider von mem0 einen openai_base_url. Richten Sie diesen Key auf Ollama, besteht die gebündelte Prüfung, weil der Provider tatsächlich openai ist. Nur die Adresse ändert sich.
Fügen Sie Ollama demselben Compose-Projekt hinzu:
ollama:
image: ollama/ollama
restart: unless-stopped
networks: [mem0_network]
ports:
- "127.0.0.1:11434:11434"
volumes:
- ollama_models:/root/.ollamaFügen Sie ollama_models: unter dem Schlüssel volumes: der obersten Ebene hinzu. Laden Sie anschließend ein Chat-Modell und ein Embedding-Modell:
docker compose up -d ollama
docker compose exec ollama ollama pull llama3.1:8b
docker compose exec ollama ollama pull nomic-embed-textWenn Ollama bereits als systemd-Unit auf dem Host läuft, wie unter Ollama direkt auf einem VPS ausführen, dürfen Sie den Container nicht auf 127.0.0.1:11434 verweisen. Innerhalb des mem0-Containers bezeichnet 127.0.0.1 den mem0-Container. Geben Sie dem mem0-Service extra_hosts: ["host.docker.internal:host-gateway"]. Setzen Sie Environment="OLLAMA_HOST=0.0.0.0:11434" in einem systemd-Drop-in, damit Ollama auf einer für die Bridge erreichbaren Adresse lauscht. Lassen Sie Port 11434 weiterhin in der Firewall gesperrt.
Fragen Sie das Modell nach seiner Embedding-Dimension, bevor Sie etwas konfigurieren
Dieser eine Schritt entscheidet, ob die Suche überhaupt funktioniert.
Der pgvector-Speicher von mem0 erstellt seine Tabelle mit einer festen Vektorbreite, vector vector(1536), weil embedding_model_dims standardmäßig 1536 verwendet, die Breite von OpenAIs text-embedding-3-small. nomic-embed-text gibt 768 Werte zurück. Innerhalb von mem0 werden diese beiden Zahlen nicht verglichen. Die Abweichung wird daher beim ersten Insert von Postgres gemeldet:
expected 1536 dimensions, not 768Vertrauen Sie auch der Zahl in diesem Absatz nicht. Fragen Sie das Modell:
curl -sS http://127.0.0.1:11434/v1/embeddings \
-H "Content-Type: application/json" \
-d '{"model":"nomic-embed-text","input":"dimension check"}' \
| python3 -c "import json,sys; print(len(json.load(sys.stdin)['data'][0]['embedding']))"Damit wird die Breite ausgegeben, die Ihre Collection verwenden muss. Schreiben Sie die Konfiguration in eine Datei, weil beim Einfügen eines Postgres-Passworts durch Shell-Quoting Tippfehler in die Produktion gelangen.
{
"vector_store": {
"provider": "pgvector",
"config": {
"host": "postgres",
"port": 5432,
"dbname": "postgres",
"user": "postgres",
"password": "<POSTGRES_PASSWORD from .env>",
"collection_name": "memories_local_768",
"embedding_model_dims": 768
}
},
"llm": {
"provider": "openai",
"config": {
"model": "llama3.1:8b",
"api_key": "ollama",
"openai_base_url": "http://ollama:11434/v1",
"temperature": 0.2
}
},
"embedder": {
"provider": "openai",
"config": {
"model": "nomic-embed-text",
"api_key": "ollama",
"openai_base_url": "http://ollama:11434/v1"
}
}
}curl -sS -X POST http://127.0.0.1:8888/configure \
-H "Content-Type: application/json" \
-H "X-API-Key: $MEM0_KEY" \
-d @config.json
curl -sS http://127.0.0.1:8888/configure -H "X-API-Key: $MEM0_KEY"Der zweite Aufruf liest die Konfiguration zurück. Damit prüfen Sie, ob der Schreibvorgang erfolgreich war. Wiederholen Sie anschließend den obigen Smoke-Test.
Vier Details in dieser JSON-Datei sind nicht offensichtlich. Jeder Fehler bei einem dieser Details führt zu Problemen.
api_key ist die Zeichenfolge ollama, und Ollama ignoriert ihren Wert. Sie darf nicht leer sein, weil die OpenAI-Clientbibliothek ohne gesetzten Key einen Fehler auslöst, bevor eine Anfrage den Prozess verlässt. Jede nicht leere Zeichenfolge funktioniert.
embedding_model_dims gehört in den Vektorspeicher. Für den Embedder gibt es absichtlich kein embedding_dims. mem0 sendet den OpenAI-Parameter dimensions nur, wenn Sie embedding_dims setzen. Backends, die keine Matryoshka-Trunkierung implementieren, lehnen diesen Parameter sofort ab. Setzen Sie die Breite dort, wo die Tabelle erstellt wird, und lassen Sie den Embedder unverändert.
collection_name ist neu. mem0 erstellt seine Tabelle mit CREATE TABLE IF NOT EXISTS. Wenn Sie eine andere Breite an eine vorhandene Collection übergeben, ändert sich überhaupt nichts: Die alte Spalte vector(1536) bleibt bestehen, und jeder Insert schlägt fehl. Eine Änderung der Breite erfordert einen neuen Collection-Namen. Alternativ löschen Sie die alte Tabelle manuell.
Der Host in openai_base_url ist der Compose-Service-Name ollama, nicht localhost. Container lösen sich in ihrem gemeinsamen Netzwerk über den Service-Namen auf.
Die Kosten des vollständig lokalen Betriebs
Seien Sie bei der Qualität realistisch. Die veröffentlichten Benchmark-Werte von mem0 wurden mit Frontier-Modellen für die Extraktion ermittelt. Betrachten Sie sie daher als Obergrenze und nicht als Prognose für ein 8B-Modell auf Ihrem VPS. Ein kleines Modell formuliert Fakten ungenauer. Außerdem gibt es manchmal Fließtext zurück, obwohl JSON angefordert wurde. Das zeigt sich als Add-Aufruf, der ohne Fehler eine leere Liste results zurückgibt.
Die Geschwindigkeit ist der zweite Kostenfaktor. Eine reine CPU-Extraktion benötigt pro Add-Aufruf mehrere Sekunden, und jede gespeicherte Nachricht verursacht diese Kosten. Wenn diese Latenz relevant ist, ist ein VPS mit angeschlossener GPU die sachgerechte Lösung. Mehr CPU-Kerne helfen bei einem 8B-Modell deutlich weniger, als viele erwarten.
Eine Regel gilt unabhängig von Ihrer Entscheidung: Mischen Sie niemals Embedding-Modelle innerhalb einer Collection. Zwei verschiedene Modelle können dieselbe Breite haben und trotzdem nicht vergleichbare Vektoren erzeugen. Der Insert ist erfolgreich, die Suche gibt Zeilen zurück, und die Ergebnisse sind falsch. An keiner Stelle wird ein Fehler gemeldet.
Backups: Es gibt zwei Datenbanken, nicht eine
Der häufigste Fehler bei mem0-Backups besteht darin, nur eine Datenbank zu dumpen. init-db.sh erstellt neben der standardmäßigen postgres-Datenbank auch mem0_app. Beide enthalten unterschiedliche Daten. Die Datenbank postgres enthält die pgvector-Collections, also die Memories. mem0_app enthält Benutzer, Sitzungen, API-Keys und Request-Logs.
Wenn Sie nur postgres wiederherstellen, kommen die Memories zurück, während alle Konten und API-Keys fehlen. Dann kann sich nichts authentifizieren, um die Memories zu lesen. Erstellen Sie mit einem Befehl Dumps von beiden Datenbanken sowie den Rollen:
docker compose exec -T postgres pg_dumpall -U postgres --clean \
| gzip > "mem0-$(date +%F).sql.gz"Das History-Volume ist von Postgres getrennt und benötigt eine eigene Kopie:
docker run --rm -v mem0_mem0_history:/data -v "$PWD:/backup" \
alpine tar czf /backup/mem0-history.tgz -C /data .Docker versieht Volume-Namen mit dem Projektnamen. Prüfen Sie Ihren Namen daher mit docker volume ls, bevor Sie mem0_mem0_history voraussetzen.
Stellen Sie die Daten in einem temporären Container wieder her und prüfen Sie die Zeilenanzahl, bevor Sie den Ergebnissen vertrauen:
gunzip -c mem0-2026-08-03.sql.gz \
| docker compose exec -T postgres psql -U postgres -d postgresEin Backup, das Sie noch nie wiederhergestellt haben, ist nur eine Vermutung. Sobald die Dumps korrekt sind, übertragen Sie sie mit restic-Snapshots in einen externen Speicher vom Server, denn ein Backup auf dem Server, den es schützen soll, schützt nichts.
Fehlerbilder und die genau angezeigten Zeichenfolgen
{"detail":"Authentication required. Provide a Bearer token or X-API-Key header."} bedeutet, dass der Header fehlt oder falsch geschrieben ist. Der Name lautet X-API-Key, und curl sendet Header-Namen unverändert.
{"detail":"At least one identifier (user_id, agent_id, run_id) is required."} bei einem Add bedeutet, dass die Anfrage keinen dieser Header enthielt. Eine Memory muss einem bestimmten Bereich zugeordnet sein, weil die Suche genau nach diesen Feldern filtert.
LLM provider 'ollama' is not bundled in this image mit HTTP 400 bedeutet, dass Sie "provider": "ollama" gesendet haben. Verwenden Sie "provider": "openai" mit openai_base_url, das auf Ollama zeigt.
expected 1536 dimensions, not 768 von Postgres bedeutet, dass die Collection mit einer bestimmten Breite erstellt wurde, der Embedder aber eine andere Breite liefert. Setzen Sie embedding_model_dims im Vector Store und verwenden Sie eine neue collection_name.
Die Suche liefert nach einem Modellwechsel unsinnige Zeilen, ohne dass irgendwo ein Fehler auftritt. Die Breite stimmt weiterhin, daher ist die Datenbank zufrieden. Zwei Modelle ordnen denselben Satz jedoch an unterschiedlichen Positionen an. Erstellen Sie eine neue Collection und fügen Sie die Daten erneut hinzu.
Connection refused in den mem0-Logs beim Zugriff auf Ollama bedeutet normalerweise 127.0.0.1 in openai_base_url. Innerhalb des Containers bezeichnet diese Adresse den Container selbst. Verwenden Sie den Servicenamen oder das Host-Gateway, wenn Ollama auf dem Host ausgeführt wird.
504 Gateway Time-out von nginx bei einem Add bedeutet, dass das Modell länger als proxy_read_timeout benötigt hat. Erhöhen Sie diesen Wert und prüfen Sie vor einem erneuten Senden der Anfrage, ob die Memory trotzdem geschrieben wurde.
exit code 137 während docker compose up --build bedeutet, dass der Out-of-Memory-Killer den Dashboard-Build beendet. Fügen Sie Swap hinzu oder erstellen Sie das Image auf einer größeren Maschine und übertragen Sie es in eine Registry.
error: port 3000 is already in use stammt aus dem make up-Target des Repositorys. Dieses startet nicht, wenn Port 3000 oder 8888 bereits belegt ist. Ermitteln Sie den Besitzer mit lsof -iTCP:3000 -sTCP:LISTEN.
FAQ
Benötige ich weiterhin Neo4j, um mem0 mit Graph-Speicher zu betreiben?
Nein. Der neue Speicheralgorithmus, der im April 2026 veröffentlicht wurde, hat die Konfigurationsschlüssel graph_store und enable_graph aus dem Open-Source-SDK entfernt. Die Entitätsextraktion läuft jetzt während eines normalen Hinzufügens und schreibt in eine zweite pgvector-Sammlung mit dem Namen <collection_name>_entities. Daher sind keine externe Graphdatenbank, kein zusätzlicher Container und kein Migrationsschritt erforderlich. Der Nachteil ist, dass das Feld relations in den Suchergebnissen nicht mehr vorhanden ist. Entitäten erhöhen jetzt die Rangfolge eines Speichers, anstatt durchlaufbare Kanten bereitzustellen. Eine Anwendung, die diese Beziehungen durchlaufen hat, benötigt daher außerhalb von mem0 einen eigenen Graphspeicher.
Was ist der kleinste VPS, auf dem ein selbst gehosteter mem0-Server ausgeführt werden kann?
Wenn das Sprachmodell an anderer Stelle gehostet wird, reichen 2 GB RAM und etwa 4 GB freier Speicherplatz für den API-Container, Postgres und das Dashboard aus. Eng wird es beim ersten Build, weil die Kompilierung des Next.js-Dashboards mehr Speicher benötigt als sein Betrieb. Auf einem System mit 1 GB wird der Build mit exit code 137 beendet. Wenn Ollama auf demselben Server läuft, muss die Größe stattdessen am Modell ausgerichtet werden: Ein 8B-Modell mit 4-Bit-Quantisierung benötigt allein ungefähr 6 GB. Planen Sie daher 8 GB ein.
Kann ich mem0 ohne einen OpenAI-API-Schlüssel ausführen?
Ja, über Ollamas OpenAI-kompatiblen Endpunkt. Das Setzen von "provider": "ollama" schlägt fehl, weil das Server-Image nur die Bibliotheken openai, anthropic und gemini enthält und HTTP 400 zurückgibt. Belassen Sie stattdessen "provider": "openai" und setzen Sie "openai_base_url": "http://ollama:11434/v1" mit einem beliebigen nicht leeren api_key, sowohl für das llm als auch für den Embedder. Ollama ignoriert den Schlüssel. Die gebündelte Anbieterprüfung wird bestanden, weil der Anbieter tatsächlich openai ist.
Warum liefert mem0 keine Ergebnisse mehr, nachdem ich auf ein lokales Embedding-Modell umgestellt habe?
Die pgvector-Tabelle wurde mit einer festen Vektordimension erstellt. embedding_model_dims ist standardmäßig auf 1536 gesetzt, nomic-embed-text liefert 768, und Postgres lehnt das Einfügen mit expected 1536 dimensions, not 768 ab. mem0 erstellt die Tabelle mit CREATE TABLE IF NOT EXISTS. Eine Änderung dieser Zahl allein wirkt sich daher nicht auf eine vorhandene Sammlung aus. Setzen Sie embedding_model_dims auf die tatsächliche Dimension Ihres Modells, bestätigen Sie diese Dimension, indem Sie /v1/embeddings aufrufen und die zurückgegebenen Werte zählen, und geben Sie dem Vektorspeicher gleichzeitig eine neue collection_name.