Jak zainstalować mem0 na własnym VPS
Instrukcja self-hostingu mem0 z wykorzystaniem Docker Compose. Sprawdź wymagania RAM dla stosu FastAPI, Postgres i Ollama oraz konfigurację TLS dla bezpiecznego API.
Rzeczywiste koszty pamięci RAM przy self-hostingu mem0 na VPS
Self-hosting mem0 oznacza uruchomienie trzech kontenerów: serwera pamięci FastAPI, bazy danych Postgres z rozszerzeniem pgvector oraz panelu sterowania Next.js. mem0 stanowi warstwę pamięci dla agentów. Do systemu przesyłana jest konwersacja, model językowy wyodrębnia z niej trwałe fakty, a te są zapisywane jako wektory, aby późniejsze zapytanie mogło pobrać odpowiednie informacje.
Należy zarezerwować około 1 GB pamięci rezydentnej dla trzech kontenerów oraz od 3 do 4 GB miejsca na dysku po zbudowaniu obrazów. VPS z 2 GB RAM pozwala na komfortowe działanie, o ile model językowy znajduje się na innym serwerze. Gdy model działa na tej samej maszynie za pośrednictwem Ollama, dominuje on nad pozostałymi procesami: model 8B skwantyzowany do 4 bitów wymaga około 6 GB pamięci, więc w pełni lokalna instalacja wymaga 8 GB RAM.
Nie należy polegać wyłącznie na danych z wpisów na blogach, w tym niniejszego. Należy zmierzyć zasoby faktycznie wdrożonego stosu.
docker compose ps
docker stats --no-stream
docker system df -vdocker stats wyświetla pamięć rezydentną dla każdego kontenera. docker system df -v wyświetla zajętość dysku przez każdy obraz oraz wolumen.
Stan ustalony nie jest stanem szczytowym. docker compose up -d --build kompiluje panel Next.js, a proces budowania Node jest najbardziej zasobożernym momentem całej instalacji. Na VPS z 1 GB RAM mechanizm OOM (out-of-memory) killera jądra przerywa proces, a budowanie kończy się błędem exit code 137. Przed poszukiwaniem błędu w Docker należy potwierdzić przyczynę:
dmesg -T | grep -i "killed process"Jeśli serwer wydaje się zbyt rozbudowanym rozwiązaniem dla bieżących potrzeb, istnieją mniejsze alternatywy. lokalny magazyn pamięci agenta bez serwera oraz pamięć działająca bezpośrednio wewnątrz Claude Code pozwalają pominąć bazę danych. Warto wrócić do tego rozwiązania, gdy kilku agentów lub kilka maszyn musi korzystać z tych samych zasobów pamięci.
Czy do obsługi pamięci grafowej mem0 wymagany jest Neo4j?
Nie. Jeśli instrukcja zaleca dodanie kontenera Neo4j, jest ona nieaktualna względem obecnego kodu.
Pamięć grafowa w mem0 wymagała wcześniej zewnętrznej bazy danych grafowych, konfigurowanej za pomocą klucza graph_store z parametrem enable_graph ustawionym na true. Nowy algorytm pamięci, wprowadzony w kwietniu 2026, usunął oba te klucze z biblioteki open source SDK. Ekstrakcja encji odbywa się teraz w ramach standardowej ścieżki add, a encje są zapisywane w drugiej kolekcji pgvector, nazwanej na wzór głównej z dodanym przyrostkiem _entities. Nie jest wymagana żadna migracja. Wbudowane łączenie encji zaczyna działać przy kolejnym wywołaniu add.
Rezygnacja z magazynu grafowego pozwala zaoszczędzić kontener JVM, jego stertę (heap) oraz kilkaset megabajtów obrazu. Na serwerze VPS o pojemności 2 GB stanowi to różnicę między poprawnym działaniem a korzystaniem z partycji wymiany (swap).
Oto lista funkcjonalności, z których należy zrezygnować: wyniki wyszukiwania zawierały wcześniej pole relations, w którym wymieniano krawędzie między encjami. To pole zostało usunięte. Dopasowania encji podnoszą teraz pozycję pamięci w łącznym wyniku, jednak brak jest struktury, którą można przeszukiwać. Jeśli aplikacja korzystała z tych relacji, mem0 nie przechowuje ich już w swojej strukturze; należy wówczas utrzymywać własną bazę danych grafowych poza mem0, zasilaną własnym kodem.
Plik compose w repozytorium jest wersją deweloperską
server/docker-compose.yaml deklaruje name: mem0-dev i należy traktować to dosłownie. Przed uruchomieniem należy zapoznać się z treścią pliku, ponieważ zawiera on pięć elementów nieodpowiednich dla serwera produkcyjnego.
- Buduje on obraz z
server/dev.Dockerfilei montuje katalog roboczy nad obrazem za pomocą.:/app, przez co kontener uruchamia pliki znajdujące się w tym katalogu, a nie te, które zostały wbudowane w obraz. - Polecenie startowe to
rm -rf /app/packages && pip install -q --force-reinstall --no-deps mem0ai && alembic upgrade head && uvicorn main:app --reload. Powoduje ono reinstalacjęmem0aiz PyPI przy każdym uruchomieniu, co oznacza, że wersja oprogramowania na serwerze może zmienić się podczas restartu, który nie miał być aktualizacją. - Ten sam krok z użyciem pip oznacza, że restart przy braku połączenia z Internetem zakończy się niepowodzeniem, zanim uvicorn zdąży wystartować. Serwer pamięci będzie niedostępny, ponieważ usługa PyPI była nieosiągalna.
--reloaduruchamia mechanizm śledzenia zmian plików w uvicorn. Służy on do restartowania procesu podczas edycji kodu, co w środowisku produkcyjnym niepotrzebnie zużywa pamięć i tworzy dodatkowy proces. ProdukcyjnyDockerfilezawiera już--reloadw swoimCMD, więc w obu przypadkach polecenie zostanie nadpisane.- Opublikowane porty to
"8888:8000","8432:5432"oraz"3000:3000". Opublikowanie portu bez wskazania adresu powoduje powiązanie z0.0.0.0, co sprawia, że Postgres odpowiada na zapytania z publicznego Internetu na porcie 8432 natychmiast po uruchomieniu stosu.
Ostatni punkt wymaga osobnego ostrzeżenia. Docker publikuje porty, wpisując własne reguły przed łańcuchem zarządzanym przez ufw, dlatego ufw deny 8432 nie zablokuje portu kontenera opublikowanego w ten sposób. Publikowanie portów przez Docker z pominięciem ufw szczegółowo omawia mechanizm działania tych reguł.
Plik compose dla serwera produkcyjnego
Pracuj wewnątrz server/, zachowaj init-db.sh w miejscu, w którym się znajduje, i zastąp docker-compose.yaml poniższą treścią.
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: bridgeW tym miejscu istotnych jest pięć zmian, z których każda ma swoje uzasadnienie.
Każdy wpis ports zaczyna się od 127.0.0.1, dzięki czemu jądro systemu akceptuje te połączenia wyłącznie z poziomu samej maszyny. Cały ruch z zewnątrz dociera przez reverse proxy, który jako jedyny posiada certyfikat.
Postgres nie posiada żadnego bloku ports. Kontener mem0 uzyskuje do niego dostęp przez mem0_network za pomocą nazwy usługi, więc publikowanie portu 8432 nie przynosi korzyści, a jedynie otwiera zbędny port. Użyj docker compose exec postgres psql -U postgres, gdy potrzebujesz powłoki.
Historia została przeniesiona z bind mount ./history do wolumenu nazwanego. Bind mount wiąże dane z konkretną ścieżką i identyfikatorem użytkownika na hoście, podczas gdy wolumen nazwany jest obiektem, który Docker może migawkować i przenosić. Wolumeny nazwane a bind mounts wyjaśnia, kiedy stosować poszczególne rozwiązania.
Polecenie pomija --reload i zachowuje alembic upgrade head. Ten krok migracji jest niezbędny. Bez niego aplikacja uruchamia się z bazą danych pozbawioną tabel, przez co każde żądanie kończy się błędem przy pierwszym zapytaniu.
NEXT_PUBLIC_API_URL to adres URL wywoływany przez przeglądarkę, więc musi to być publiczny adres HTTPS, a nie http://mem0:8000. Next.js wstawia każdą wartość NEXT_PUBLIC_ na etapie budowania, dlatego zmiana wymaga docker compose up -d --build mem0-dashboard. Zwykły restart zachowuje starą wartość wbudowaną w kod JavaScript, co powoduje, że pulpit nawigacyjny odwołuje się do błędnego hosta.
Sekrety przechowuje się w pliku .env, a plik .env nie powinien być dostępny z Internetu
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 .envSkonfiguruj POSTGRES_PASSWORD, JWT_SECRET oraz ADMIN_API_KEY. Pozostaw AUTH_DISABLED=false bez zmian. Nazwa flagi precyzyjnie określa jej działanie: po jej włączeniu serwer udostępnia całą zawartość swojej pamięci każdemu, kto uzyska dostęp do portu. Ustaw MEM0_TELEMETRY=false, jeśli zdarzenie onboardingu nie powinno być wysyłane do systemów zewnętrznych.
Wartość ADMIN_API_KEY jest porównywana z nagłówkiem X-API-Key za pomocą secrets.compare_digest, a dopasowanie pomija każde zapytanie do bazy danych. Jest to poświadczenie typu root dla całego API. Należy traktować je odpowiednio: nie zapisuj go w historii powłoki, nie dodawaj do git, nie wklejaj do terminala. Artykuły Pliki Compose env i wycieki sekretów oraz ochrona kluczy API przed kontekstem agenta mają tutaj bezpośrednie zastosowanie, ponieważ klientami tego serwera są agenty.
Wartości wczytane z env_file znajdują się w środowisku kontenera, a docker inspect wyświetla je w pełnej postaci. Każdy użytkownik w grupie docker może je odczytać, a każdy użytkownik w grupie docker posiada w praktyce uprawnienia root na hoście.
Umieszczenie TLS przed API zamiast otwierania portu 8888
API odpowiada na 127.0.0.1:8888, a panel sterowania na 127.0.0.1:3000. nginx wykonuje terminację TLS (transport layer security) na porcie 443 i przekazuje ruch do obu usług.
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 ma większe znaczenie, niż się wydaje. Wywołanie dodawania (add) blokuje proces, podczas gdy model językowy analizuje konwersację i wyodrębnia fakty. Lokalny model 8B uruchomiony na CPU regularnie potrzebuje więcej czasu niż domyślny limit 60 sekund w nginx, przez co wywołujący otrzymuje 504 Gateway Time-out, mimo że model wciąż pracuje, a pamięć jest zapisywana. W rezultacie użytkownik otrzymuje informację o błędzie zapisu pamięci, która w rzeczywistości została utworzona.
Zablokuj pozostałe porty za pomocą domyślnej polityki deny w ufw, pozostawiając otwarte jedynie 22 i 443. Wystaw certyfikat, korzystając z certbot na Ubuntu 24.04 za nginx. Jeśli serwer obsługuje już inne aplikacje przez Traefik kierujący ruchem do kilku aplikacji Compose, dodaj mem0 do tego routera zamiast instalować drugie proxy.
Test dymny: dodaj jeden wpis w pamięci i odczytaj go
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"}'Poprawna odpowiedź to obiekt JSON z listą results, gdzie każdy wpis zawiera id, wyodrębniony tekst memory oraz "event": "ADD". Obecny algorytm zwraca wyłącznie zdarzenia typu ADD. Zdarzenia UPDATE oraz DELETE zostały usunięte, więc ich brak nie stanowi błędu.
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}'Fakt dotyczący Postgres 17 powinien zostać zwrócony wraz z wynikiem punktowym. Przekaż identyfikator wewnątrz filters, zgodnie z przykładem. Główny poziom user_id nadal działa, a serwer rejestruje Top-level user_id in /search is deprecated. Use filters={...} instead. przy każdym jego użyciu.
Wyczyść dane po zakończeniu testu, aby nie zanieczyszczały rzeczywistych wyników wyszukiwania:
curl -sS -X DELETE "http://127.0.0.1:8888/memories?user_id=smoke" \
-H "X-API-Key: $MEM0_KEY"Jeśli wyszukiwanie zwraca mniej wierszy niż oczekiwano, sprawdź wartości domyślne przed weryfikacją mechanizmu pobierania. W bieżącym wydaniu top_k domyślnie wynosi 20 (poprzednio 100), a threshold domyślnie wynosi 0.1 zamiast braku limitu, co powoduje odfiltrowywanie słabych dopasowań. Gdy konfiguracja zadziała przez curl, te same punkty końcowe należy podłączyć do agenta, bezpośrednio lub poprzez serwer MCP działający na tym samym VPS.
Uruchomienie mem0 bez klucza OpenAI
Zacznij od blokady, ponieważ napotkasz ją w ciągu pierwszych pięciu minut. Obraz serwera zawiera ustalony zestaw bibliotek dostawców, a /configure odrzuca wszystko spoza tego zakresu:
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.Nie musisz niczego przebudowywać. Ollama udostępnia API kompatybilne z OpenAI pod adresem /v1, obsługując /v1/chat/completions oraz /v1/embeddings, a dostawca openai w mem0 akceptuje openai_base_url. Skieruj ten klucz na Ollama, a wbudowana weryfikacja przejdzie pomyślnie, ponieważ dostawca faktycznie jest openai. Zmienia się tylko adres.
Dodaj Ollama do tego samego projektu Compose:
ollama:
image: ollama/ollama
restart: unless-stopped
networks: [mem0_network]
ports:
- "127.0.0.1:11434:11434"
volumes:
- ollama_models:/root/.ollamaDodaj ollama_models: w sekcji głównej volumes:, a następnie pobierz jeden model czatu i jeden model osadzeń (embedding):
docker compose up -d ollama
docker compose exec ollama ollama pull llama3.1:8b
docker compose exec ollama ollama pull nomic-embed-textJeśli Ollama działa już na hoście jako jednostka systemd, zgodnie z uruchamianiem Ollama bezpośrednio na VPS, nie kieruj kontenera na 127.0.0.1:11434. Wewnątrz kontenera mem0, 127.0.0.1 to sam kontener mem0. Nadaj usłudze mem0 extra_hosts: ["host.docker.internal:host-gateway"], ustaw Environment="OLLAMA_HOST=0.0.0.0:11434" w pliku typu drop-in systemd, aby Ollama nasłuchiwała na adresie dostępnym dla sieci bridge, i zablokuj port 11434 na firewallu.
Sprawdź wymiar osadzeń modelu przed konfiguracją
Ten jeden krok decyduje o tym, czy wyszukiwanie w ogóle zadziała.
Magazyn pgvector w mem0 tworzy tabelę o stałej szerokości wektora, vector vector(1536), ponieważ embedding_model_dims domyślnie przyjmuje wartość 1536, czyli szerokość modelu text-embedding-3-small od OpenAI. nomic-embed-text zwraca 768 wartości. W mem0 nie ma mechanizmu porównującego te dwie liczby, więc niezgodność ujawni się po stronie Postgres przy pierwszej próbie zapisu:
expected 1536 dimensions, not 768Nie ufaj również liczbie podanej w tym akapicie. Zapytaj model:
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']))"To polecenie wyświetli szerokość, której musi używać Twoja kolekcja. Zapisz konfigurację w pliku, ponieważ wklejanie hasła do Postgres przez powłokę to najprostsza droga do błędów w środowisku produkcyjnym.
{
"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"Drugie wywołanie odczytuje konfigurację, co potwierdza, że zapis został wykonany. Następnie powtórz powyższy test poprawności.
Cztery szczegóły w tym pliku JSON nie są oczywiste, a każdy z nich powoduje awarię w przypadku błędnej konfiguracji.
api_key to ciąg znaków ollama, a Ollama ignoruje jego wartość. Nie może być on pusty, ponieważ biblioteka kliencka OpenAI zgłasza błąd przed wysłaniem żądania, jeśli klucz nie jest ustawiony. Dowolny niepusty ciąg znaków zadziała.
embedding_model_dims dotyczy magazynu wektorów, a w konfiguracji embeddera celowo nie ma embedding_dims. mem0 wysyła parametr OpenAI dimensions tylko wtedy, gdy ustawisz embedding_dims, a backendy, które nie implementują obcinania Matryoshka, odrzucają ten parametr. Ustaw szerokość w miejscu tworzenia tabeli i nie zmieniaj ustawień embeddera.
collection_name to nowość. mem0 tworzy tabelę z CREATE TABLE IF NOT EXISTS, więc wskazanie innej szerokości dla istniejącej kolekcji nic nie zmieni: stara kolumna vector(1536) pozostanie, a każdy zapis zakończy się niepowodzeniem. Zmiana szerokości wymaga nowej nazwy kolekcji lub ręcznego usunięcia starej tabeli.
Host w openai_base_url to nazwa usługi Compose ollama, a nie localhost. Kontenery rozwiązują swoje nazwy w ramach współdzielonej sieci.
Koszt w pełni lokalnego rozwiązania
Bądź szczery wobec siebie w kwestii jakości. Opublikowane wyniki testów mem0 uzyskano przy użyciu modeli klasy frontier do ekstrakcji, więc traktuj je jako górny limit, a nie prognozę dla modelu 8B na Twoim VPS. Mniejszy model zapisuje bardziej ogólne fakty i czasami zwraca tekst zamiast wymaganego formatu JSON, co objawia się wywołaniem add zwracającym pustą listę results bez błędu.
Drugim kosztem jest szybkość. Ekstrakcja wykonywana wyłącznie na procesorze (CPU) zajmuje sekundy przy każdym wywołaniu add, a każda zapisywana wiadomość zwiększa ten czas. Jeśli opóźnienie ma znaczenie, uczciwym rozwiązaniem jest VPS z dołączonym GPU. Zwiększanie liczby rdzeni CPU dla modelu 8B pomaga znacznie mniej, niż można by oczekiwać.
Niezależnie od wyboru, obowiązuje jedna zasada: nigdy nie mieszaj modeli osadzeń w jednej kolekcji. Dwa różne modele, które przypadkowo mają tę samą szerokość, generują wektory, których nie można ze sobą porównywać. Zapis się powiedzie, wyszukiwanie zwróci wiersze, ale będą one błędne, a żaden system nie zgłosi błędu.
Kopie zapasowe: istnieją dwie bazy danych, a nie jedna
Najczęstszym błędem przy tworzeniu kopii zapasowej mem0 jest zrzucenie tylko jednej bazy danych. init-db.sh tworzy mem0_app obok domyślnej bazy danych postgres, a przechowują one różne dane. Baza danych postgres zawiera kolekcje pgvector, czyli wspomnienia. mem0_app przechowuje użytkowników, sesje, klucze API oraz logi żądań.
Przywrócenie tylko postgres spowoduje odzyskanie wspomnień, ale utratę wszystkich kont i kluczy API, co uniemożliwi uwierzytelnienie w celu ich odczytania. Należy zrzucić obie bazy oraz role za pomocą jednego polecenia:
docker compose exec -T postgres pg_dumpall -U postgres --clean \
| gzip > "mem0-$(date +%F).sql.gz"Wolumen historii jest oddzielony od Postgres i wymaga osobnej kopii:
docker run --rm -v mem0_mem0_history:/data -v "$PWD:/backup" \
alpine tar czf /backup/mem0-history.tgz -C /data .Docker dodaje prefiks nazwy projektu do nazw wolumenów, dlatego przed założeniem, że używana jest nazwa mem0_mem0_history, należy potwierdzić ją za pomocą docker volume ls.
Przed uznaniem kopii za poprawną, należy przywrócić ją do kontenera tymczasowego i sprawdzić liczbę wierszy:
gunzip -c mem0-2026-08-03.sql.gz \
| docker compose exec -T postgres psql -U postgres -d postgresKopia zapasowa, która nigdy nie została przywrócona, jest jedynie przypuszczeniem. Gdy zrzuty są poprawne, należy przenieść je poza serwer za pomocą migawki restic do pamięci zewnętrznej, ponieważ kopia zapasowa przechowywana na serwerze, który ma chronić, nie zapewnia żadnej ochrony.
Tryby awarii i dokładne komunikaty
{"detail":"Authentication required. Provide a Bearer token or X-API-Key header."} oznacza, że nagłówek jest nieobecny lub zawiera błąd w nazwie. Nazwa to X-API-Key, a narzędzie curl przesyła nazwy nagłówków w sposób dosłowny.
{"detail":"At least one identifier (user_id, agent_id, run_id) is required."} przy operacji dodawania oznacza, że żądanie nie zawierało żadnego z nich. Pamięć musi być przypisana do określonego zakresu, ponieważ filtry wyszukiwania działają dokładnie na tych polach.
LLM provider 'ollama' is not bundled in this image z kodem HTTP 400 oznacza wysłanie "provider": "ollama". Należy użyć "provider": "openai" z parametrem openai_base_url wskazującym na Ollama.
expected 1536 dimensions, not 768 z bazy Postgres oznacza, że kolekcja została utworzona z inną szerokością wektora, niż zwraca model osadzeń (embedder). Należy ustawić embedding_model_dims w magazynie wektorów i użyć nowej collection_name.
Wyszukiwanie zwraca nielogiczne wyniki po zmianie modelu, mimo braku błędów. Szerokość wektora jest zgodna, więc baza danych działa poprawnie, jednak dwa różne modele umieszczają to samo zdanie w różnych punktach przestrzeni wektorowej. Należy utworzyć nową kolekcję i ponownie dodać dane.
Connection refused w logach mem0 podczas łączenia z Ollama zazwyczaj oznacza 127.0.0.1 w openai_base_url. Wewnątrz kontenera ten adres odnosi się do samego kontenera. Należy użyć nazwy usługi lub adresu host gateway, jeśli Ollama działa na hoście.
504 Gateway Time-out z nginx podczas dodawania oznacza, że model przetwarzał żądanie dłużej niż proxy_read_timeout. Należy zwiększyć ten limit i sprawdzić, czy pamięć została zapisana przed ponowieniem żądania.
exit code 137 podczas docker compose up --build oznacza przerwanie procesu budowania dashboardu przez mechanizm out-of-memory killer. Należy dodać przestrzeń wymiany (swap) lub zbudować obraz na maszynie o większych zasobach i wypchnąć go do rejestru.
error: port 3000 is already in use pochodzi z celu make up w repozytorium, który odmawia uruchomienia, gdy porty 3000 lub 8888 są zajęte. Należy zidentyfikować proces zajmujący port za pomocą lsof -iTCP:3000 -sTCP:LISTEN.
FAQ
Czy nadal potrzebuję Neo4j, aby uruchomić mem0 z pamięcią grafową?
Nie. Nowy algorytm pamięci, wydany w kwietniu 2026, usunął klucze konfiguracyjne graph_store oraz enable_graph z otwartego SDK. Ekstrakcja encji odbywa się teraz podczas standardowej operacji dodawania i zapisuje dane do drugiej kolekcji pgvector o nazwie <collection_name>_entities. Dzięki temu nie jest wymagana zewnętrzna baza grafowa, dodatkowy kontener ani proces migracji. Ceną za to jest brak pola relations w wynikach wyszukiwania. Encje podnoszą teraz ranking pamięci, zamiast dostarczać krawędzie do przeglądania, więc aplikacja, która korzystała z tych relacji, musi posiadać własny magazyn grafowy poza mem0.
Jaki jest najmniejszy VPS, na którym można uruchomić własny serwer mem0?
Jeśli model językowy jest hostowany zewnętrznie, 2 GB pamięci RAM i około 4 GB wolnego miejsca na dysku wystarczą dla kontenera API, Postgres i panelu sterowania. Krytycznym momentem jest pierwsze budowanie, ponieważ kompilacja panelu Next.js zużywa więcej pamięci niż jego uruchomienie, a na maszynie z 1 GB RAM proces budowania zostanie przerwany z błędem exit code 137. Jeśli Ollama działa na tym samym serwerze, należy uwzględnić rozmiar modelu: model 8B przy kwantyzacji 4-bitowej wymaga około 6 GB, więc należy zaplanować 8 GB RAM.
Czy mogę uruchomić mem0 bez klucza API OpenAI?
Tak, poprzez punkt końcowy Ollama zgodny z API OpenAI. Ustawienie "provider": "ollama" kończy się niepowodzeniem, ponieważ obraz serwera zawiera tylko biblioteki openai, anthropic i gemini, co skutkuje błędem HTTP 400. Zamiast tego należy zachować "provider": "openai" i ustawić "openai_base_url": "http://ollama:11434/v1" na dowolną niepustą wartość api_key, zarówno dla llm, jak i embedder. Ollama ignoruje klucz, a weryfikacja dostawcy przechodzi pomyślnie, ponieważ dostawcą jest faktycznie openai.
Dlaczego mem0 nie zwraca wyników po przełączeniu na lokalny model osadzeń (embedding)?
Ponieważ tabela pgvector została utworzona ze stałą szerokością. embedding_model_dims domyślnie przyjmuje 1536, nomic-embed-text zwraca 768, a Postgres odrzuca operację wstawiania z błędem expected 1536 dimensions, not 768. mem0 tworzy tabelę z CREATE TABLE IF NOT EXISTS, więc sama zmiana liczby nie wpływa na istniejącą kolekcję. Należy ustawić embedding_model_dims na rzeczywistą szerokość modelu, potwierdzić tę szerokość wywołując /v1/embeddings i zliczając zwracane wartości, a następnie nadać magazynowi wektorów nową nazwę collection_name w tym samym czasie.