mem0をVPSでセルフホストする手順と必要なメモリ容量
mem0のメモリサーバーをVPSで構築する方法を解説します。必要なメモリ量の目安や、ビルド時に発生するexit code 137の対処法、Ollamaを併用する場合の推奨スペックをまとめました。Docker Composeによる構成例も紹介します。
VPS 上で mem0 をセルフホストする場合に実際に必要な RAM
mem0 をセルフホストするには、3 つのコンテナを実行します。FastAPI のメモリサーバー、pgvector 拡張機能を有効にした Postgres、Next.js のダッシュボードです。mem0 はエージェント向けのメモリ層です。会話を mem0 に送ると、言語モデルがその会話から長期的に保持すべき事実を抽出します。抽出した事実はベクトルとして保存され、後続のクエリで関連する事実を取得できます。
3 つのコンテナに必要な常駐メモリは、おおよそ 1 GB と見積もってください。イメージのビルド後に必要なディスク容量は 3 から 4 GB です。言語モデルを別の場所で実行する場合、2 GB の VPS で問題なく動作します。同じサーバー上で Ollama を使ってモデルを実行すると、他の要素よりもモデルが大幅に多くのリソースを消費します。4 ビットに量子化した 8B モデルだけで約 6 GB を必要とするため、すべてをローカルで実行する構成は 8 GB から始まります。
これらの数値を、このページを含むブログ記事からそのまま引用しないでください。実際に構築したスタックを測定してください。
docker compose ps
docker stats --no-stream
docker system df -vdocker stats はコンテナごとの常駐メモリを表示します。docker system df -v は各イメージと各ボリュームが使用しているディスク容量を表示します。
定常状態の使用量はピーク時の使用量ではありません。docker compose up -d --build は Next.js ダッシュボードをコンパイルするため、この Node のビルド中がインストール全体で最もメモリを消費します。1 GB の VPS ではカーネルの out-of-memory killer がビルドを停止し、ビルドは exit code 137 で終了します。Docker のバグを疑う前に、原因を確認してください。
dmesg -T | grep -i "killed process"サーバーが必要以上に大掛かりだと感じる場合は、より小規模な選択肢があります。サーバーをまったく使わないローカルエージェントメモリストア と Claude Code 自体に保存するメモリ は、どちらもデータベースを使用しません。複数のエージェントまたは複数のマシンで同じメモリを読み取る必要が生じたら、ここに戻ってください。
mem0 のグラフメモリに Neo4j は必要ですか?
いいえ。Neo4j コンテナの追加を指示するガイドがある場合、そのガイドはコードより古いものです。
mem0 のグラフメモリは、以前は外部グラフデータベースを意味していました。graph_store キーの下で設定し、enable_graph を true に設定していました。2026 年 4 月にリリースされた新しいメモリアルゴリズムでは、オープンソース SDK からこの 2 つのキーが削除されました。エンティティ抽出は通常の add 処理の内部で実行され、エンティティはメインのコレクション名に _entities を付加した名前の 2 つ目の pgvector コレクションに書き込まれます。セットアップ手順はありません。このコレクションは初回使用時に作成されるため、組み込みのエンティティリンクは次の add 呼び出しから機能します。
グラフストアを削除すると、JVM コンテナ、そのヒープ、数百 MB のイメージが不要になります。2 GB の VPS では、正常に動作するか swap が発生するかを分ける差になります。
失われる機能を明確に説明します。以前は検索結果に、エンティティ間のエッジを列挙する relations フィールドが含まれていました。このフィールドはなくなりました。現在は、エンティティが一致すると結合スコア内でメモリの順位が上がりますが、たどれる構造はありません。アプリケーションがこれらの関係をたどっていた場合、mem0 はそれらを保持しなくなっています。mem0 の外部で独自のコードからデータを投入し、独自のグラフデータベースを維持する必要があります。
リポジトリ内の compose ファイルは開発用です
server/docker-compose.yaml は name: mem0-dev を宣言しており、その内容どおりに動作します。実行する前に必ず確認してください。このファイルには、サーバーで使うには不適切な点が5つあります。
server/dev.Dockerfileからビルドし、.:/appでチェックアウトしたディレクトリをイメージにマウントします。そのため、コンテナはビルドした内容ではなく、そのディレクトリにある内容を実行します。- コマンドは
rm -rf /app/packages && pip install -q --force-reinstall --no-deps mem0ai && alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --reloadです。起動のたびに PyPI からmem0aiを再インストールするため、アップグレードしたつもりのない再起動でも、サーバーが実行するバージョンが変わる可能性があります。 - 同じ pip の処理により、外部ネットワークに接続できない状態で再起動すると、uvicorn が実行される前に失敗します。PyPI に到達できないため、メモリサーバーが停止します。
--reloadは uvicorn のファイル監視機能を起動します。コードを編集したときにプロセスを再起動するための機能ですが、本番環境では有効な処理をせず、メモリと追加のプロセスを消費します。本番用のDockerfileにもCMD内の--reloadが含まれているため、どちらの場合もコマンドを上書きする必要があります。- 公開されるポートは
"8888:8000"、"8432:5432"、"3000:3000"です。アドレスを指定しない公開ポートは0.0.0.0にバインドされるため、スタックを起動した瞬間に Postgres が 8432 番ポートでインターネットから接続可能になります。
最後の点には、個別に注意が必要です。Docker は ufw が管理するチェーンより前に独自のルールを追加してポートを公開するため、ufw deny 8432 では公開されたコンテナポートを閉じられません。Docker が ufw を経由せずにポートを公開する仕組みでは、関係するルールを順に説明しています。
実サーバー向けの compose ファイル
server/ 内で作業し、init-db.sh はそのまま残して、docker-compose.yaml を次の内容に置き換えます。
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: bridgeここで重要な変更は 5 つあり、それぞれに理由があります。
すべての ports エントリは 127.0.0.1 で始まるため、カーネルはこれらの接続を同じホストからのものだけ受け入れます。外部からの接続はすべてリバースプロキシ経由になります。証明書を保持するのもリバースプロキシだけです。
Postgres には ports ブロックがありません。mem0 コンテナはサービス名を使って mem0_network 経由で接続するため、8432 を公開しても利点はなく、開放ポートが 1 つ増えるだけです。シェルが必要な場合は docker compose exec postgres psql -U postgres を使用します。
履歴の保存先を ./history bind mount から named volume に変更します。bind mount では、このホスト上の 1 つのパスと 1 つの uid にデータが固定されます。一方、named volume は Docker が snapshot を取得して移動できるオブジェクトです。named volume と bind mount の比較 では、それぞれを使うべき場面を説明しています。
コマンドから --reload を削除し、alembic upgrade head を残しています。この移行手順は残してください。これがないと、アプリはテーブルのないデータベースに対して起動し、すべてのリクエストが最初のクエリで失敗します。
NEXT_PUBLIC_API_URL はブラウザーが呼び出す URL なので、http://mem0:8000 ではなく公開 HTTPS アドレスにする必要があります。Next.js は NEXT_PUBLIC_ の値をビルド時にインライン化しますが、このダッシュボードの image は代わりにプレースホルダーを組み込み、コンテナの起動時に entrypoint がコンテナ内の実際の値へ置き換えます。そのため、これを変更するにはコンテナを再作成する docker compose up -d mem0-dashboard が必要で、rebuild は不要です。docker compose restart mem0-dashboard は、ファイルに古い値が残っている既存のコンテナを再利用するため、ダッシュボードは誤ったホストを呼び出し続けます。
Secrets は .env に保存し、.env はインターネットから隔離する
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 .envPOSTGRES_PASSWORD、JWT_SECRET、ADMIN_API_KEYを設定します。AUTH_DISABLED=falseは false のままにします。このフラグの動作は名前どおりです。有効にすると、サーバーは保持しているすべてのメモリを、ポートに到達できるすべての相手に渡します。オンボーディングイベントを上流へ送信しない場合は、MEM0_TELEMETRY=falseを設定します。
ADMIN_API_KEYは、secrets.compare_digestを使用してX-API-Keyヘッダーと比較されます。一致すると、データベースでのキー単位の検索が完全に省略されます。その場合、リクエストはサーバー上で最初に作成されたユーザーとして実行されます。これは API 全体に対する root 認証情報です。root 認証情報として扱ってください。シェル履歴に残さず、git に保存せず、プロンプトに貼り付けないでください。Compose の環境ファイルと、そこから Secret が漏れる場所およびAPI キーをエージェントのコンテキストから除外する方法は、どちらもそのまま適用されます。このサーバーの呼び出し元はエージェントだからです。
env_fileから読み込まれた値はコンテナ環境に置かれ、docker inspectはそれらを完全な形で出力します。dockerグループのメンバーはそれらを読み取れます。また、dockerグループのメンバーは、ホスト上で実質的に root と同等の権限を持ちます。
API の前段に TLS を置き、8888 を公開しない
API は 127.0.0.1:8888 で応答し、ダッシュボードは 127.0.0.1:3000 で応答します。nginx は 443 で TLS (transport layer security) を終端し、両方へ転送します。
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|reset|generate-instructions|auth|api-keys|entities|requests|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;
}
}ダッシュボードのブラウザーコードは、NEXT_PUBLIC_API_URL にあるそのパターンのすべての API パスを呼び出します。/entities と /requests も含まれます。1 つでも除外すると、nginx はその呼び出しを Next.js に渡すため、データが必要なダッシュボードページではデータの代わりに 404 が返されます。
proxy_read_timeout は見た目以上に重要です。add 呼び出しは、言語モデルが会話を読み取り、事実を抽出している間、ブロックします。CPU 上のローカル 8B モデルでは、nginx のデフォルトである 60 秒を定期的に超えることがあります。その場合、モデルがまだ処理中で、メモリの書き込みも継続しているにもかかわらず、呼び出し元には 504 Gateway Time-out が返ります。結果として、失敗したと通知されたメモリが保存されます。
残りの通信は デフォルト拒否の ufw ポリシーで閉じ、22、80、443 番ポートだけを開放します。nginx の背後にある Ubuntu 24.04 で certbot を使用する方法で証明書を発行します。この手順では HTTP-01 チャレンジを使用するため、ポート 80 には到達できる必要があります。Let's Encrypt は証明書の発行時と、更新のたびに、HTTP-01 チャレンジをポート 80 でのみ実行します。80 番ポートを閉じたままにする場合は、DNS-01 チャレンジに切り替える必要があります。すでに 複数の Compose アプリを Traefik でルーティングしているサーバーであれば、2 台目のプロキシをインストールせず、そのルーターに mem0 を追加します。
スモークテスト: メモリを 1 件追加して読み戻す
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"}'正常なレスポンスは、results リストを含む JSON オブジェクトです。各エントリには id、抽出された memory テキスト、"event": "ADD" が含まれます。現在のアルゴリズムが返すのは ADD イベントだけです。UPDATE イベントと DELETE イベントは削除されているため、存在しなくてもバグではありません。
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}'Postgres 17 に関する事実は、スコア付きで返されるはずです。次の例のように、識別子を filters の中に渡します。トップレベルの user_id も引き続き使用でき、使用するたびにサーバーは Top-level user_id in /search is deprecated. Use filters={...} instead. をログに記録します。
テストデータが実際の検索結果に影響しないよう、使用後に削除します。
curl -sS -X DELETE "http://127.0.0.1:8888/memories?user_id=smoke" \
-H "X-API-Key: $MEM0_KEY"検索結果の行数が想定より少ない場合は、検索処理を疑う前にデフォルト値を確認してください。現在のリリースでは、top_k のデフォルト値が 100 から 20 に変更されています。また、threshold のデフォルト値は none ではなく 0.1 であるため、関連性の低い一致は自動的に除外されます。curl で動作を確認できたら、これらと同じエンドポイントをエージェントに接続します。直接接続することも、同じ VPS 上で実行する MCP server 経由で接続することもできます。
OpenAI keyなしでmem0を実行する
最初に障害要因を確認します。最初の5分以内に遭遇するためです。サーバーイメージには固定されたプロバイダーライブラリだけが含まれており、/configureはそれ以外を拒否します。
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.何も再ビルドする必要はありません。Ollamaは/v1でOpenAI互換APIを提供し、/v1/chat/completionsと/v1/embeddingsに対応しています。また、mem0のopenaiプロバイダーはopenai_base_urlを受け付けます。そのキーをOllamaに向けると、プロバイダーが実際にopenaiであるため、組み込みチェックを通過します。変更するのはアドレスだけです。
同じComposeプロジェクトにOllamaを追加します。
ollama:
image: ollama/ollama
restart: unless-stopped
networks: [mem0_network]
ports:
- "127.0.0.1:11434:11434"
volumes:
- ollama_models:/root/.ollamaトップレベルのvolumes:キーの下にollama_models:を追加し、チャットモデルを1つ、埋め込みモデルを1つ取得します。
docker compose up -d ollama
docker compose exec ollama ollama pull llama3.1:8b
docker compose exec ollama ollama pull nomic-embed-textVPSでOllamaを直接実行する場合のように、Ollamaがホスト上でsystemdユニットとしてすでに動作している場合は、コンテナから127.0.0.1:11434を指定しないでください。mem0コンテナ内では、127.0.0.1はmem0コンテナ自身を指します。mem0サービスにextra_hosts: ["host.docker.internal:host-gateway"]を指定し、systemdのdrop-inでEnvironment="OLLAMA_HOST=0.0.0.0:11434"を設定して、ブリッジから到達できるアドレスでOllamaが待ち受けるようにします。ファイアウォールでは11434を閉じたままにします。
何も設定する前にモデルの埋め込み次元数を確認する
この手順によって、検索が機能するかどうかが決まります。
mem0のpgvectorストアは、固定幅のベクトル用にテーブルを作成します。vector vector(1536)です。これは、embedding_model_dimsがOpenAIのtext-embedding-3-smallの幅である1536をデフォルトにするためです。nomic-embed-textは768個の値を返します。mem0内部ではこの2つの数値を比較しないため、最初のinsertでPostgresから不一致が報告されます。
expected 1536 dimensions, not 768この段落の数値もそのまま信用しないでください。モデルに確認します。
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']))"これにより、コレクションで使用する必要がある幅が表示されます。設定はファイルに書き込みます。シェルのクォートを通してPostgresのパスワードを貼り付けると、本番環境に入力ミスが入りやすいためです。
{
"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"2回目の呼び出しでは設定を読み戻します。書き込みが成功したことを確認するためのチェックです。その後、上記のスモークテストをもう一度実行します。
このJSONには分かりにくい点が4つあります。いずれも、誤ると何かが機能しなくなります。
api_keyは文字列ollamaであり、Ollamaはその値を無視します。空にはできません。キーが設定されていないと、OpenAIクライアントライブラリがリクエストをプロセス外へ送る前に例外を発生させるためです。空でない文字列なら何でも機能します。
embedding_model_dimsはベクトルストアに指定します。embedderにはembedding_dimsを意図的に指定しません。mem0は、embedding_dimsを設定した場合にだけOpenAIのdimensionsパラメーターを送信します。Matryoshka truncationを実装していないバックエンドは、このパラメーターをそのまま拒否します。テーブル作成時に幅を指定し、embedderには指定しないでください。
collection_nameは新しい項目です。mem0はCREATE TABLE IF NOT EXISTSでテーブルを作成するため、既存のコレクションに別の幅を指定しても何も変わりません。古いvector(1536)列が残り、すべてのinsertが失敗します。幅を変更するには新しいコレクション名を使うか、古いテーブルを手動で削除します。
openai_base_urlのホストには、localhostではなくComposeのサービス名ollamaを指定します。コンテナは共有ネットワーク上でサービス名によって相互に名前解決します。
完全ローカル構成にかかるコスト
品質については、正直に判断してください。mem0の主要なベンチマークスコアはマネージドプラットフォームでの結果です。READMEによると、そのプラットフォームにはオープンソースSDKでは利用できない独自の最適化が含まれており、使用するモデルスタックの名前は明記されていません。元のLOCOMO結果の基になった研究論文では、すべてのモデル呼び出しをホスト型モデルであるGPT-4o-miniに対して実行しています。いずれにしても、これらのスコアはVPS上の8Bモデルで得られる結果の予測ではなく、上限と考えてください。小さなモデルは曖昧な事実を書き込みやすく、JSONを要求しても文章を返すことがあります。その場合、エラーなしでadd呼び出しが空のresultsリストを返すことがあります。
もう1つのコストは速度です。CPUだけで抽出すると、add呼び出しごとに数秒かかり、保存するメッセージごとにその処理が発生します。要求されたJSONの後まで文章を生成し続けるモデルでは、さらに遅くなります。そのため、num_predictで応答を制限すると、1回のadd呼び出しにかかる時間へ上限を設定できます。この遅延が問題になる場合は、GPUを接続したVPSが現実的な解決策です。8BモデルにCPUコアを追加しても、期待するほど改善しません。マシンを変更するより、モデルを変更するほうが安価な対策です。VPS上のNemotron 3.5 Lightningでは、取得するタグ、必要なRAM、CPUだけで許容できる速度になるかを確認できます。
どの構成を選んでも、1つのルールは変わりません。1つのコレクション内で埋め込みモデルを混在させないでください。幅が同じでも、異なるモデルが生成したベクトルは比較できません。insertは成功し、検索も行を返します。しかし、その行は誤っており、どこにもエラーは報告されません。
バックアップ: データベースは1つではなく2つある
mem0 のバックアップで最も多いミスは、単一のデータベースだけをダンプすることです。init-db.sh はデフォルトの postgres データベースと並んで mem0_app を作成し、両者には異なるデータが格納されます。postgres データベースにはメモリに相当する pgvector コレクションが格納されます。mem0_app にはユーザー、セッション、API キー、リクエストログが格納されます。セルフホストのアプリケーションは、それぞれ独自の方法で状態を分割します。そのため、同じ役割を果たす 2 つの写真サーバーでもバックアップコマンドは異なります。ダンプを信頼する前に、アプリケーションが何を保存しているかを確認してください。その対極にあるのが、90 年代のビデオ店として再構築された Jellyfin ライブラリのような構成です。これはカタログ全体を別のサービスから読み込むため、独自の設定をコピーするだけで済みます。一方、mem0 では両方のデータベースが必要です。そうしないと、リストアしても使いものになりません。
postgres だけを復元するとメモリは戻りますが、すべてのアカウントと API キーが失われます。そのため、メモリを読み取るための認証ができません。roles とともに、両方を1つのコマンドでダンプします。
docker compose exec -T postgres pg_dumpall -U postgres --clean \
| gzip > "mem0-$(date +%F).sql.gz"履歴ボリュームは Postgres とは別であり、個別にコピーする必要があります。
docker run --rm -v mem0_mem0_history:/data -v "$PWD:/backup" \
alpine tar czf /backup/mem0-history.tgz -C /data .Docker はプロジェクト名をボリューム名の先頭に付けます。docker volume ls で実際の名前を確認してから、mem0_mem0_history だと判断してください。
復元用の一時コンテナに復元し、すべてを信頼する前に行数を確認します。
gunzip -c mem0-2026-08-03.sql.gz \
| docker compose exec -T postgres psql -U postgres -d postgres一度も復元していないバックアップは、推測にすぎません。ダンプが正しいことを確認したら、restic のスナップショットをオフサイトストレージへ送信してください。保護対象のサーバー上にしか存在しないバックアップは、何も保護しません。
失敗するケースと表示される正確な文字列
{"detail":"Authentication required. Provide a Bearer token or X-API-Key header."} は、ヘッダーがないか、スペルが誤っていることを示します。名前は X-API-Key で、curl はヘッダー名をそのまま送信します。
add 時の {"detail":"At least one identifier (user_id, agent_id, run_id) is required."} は、リクエストにそれらのフィールドが1つも含まれていないことを示します。検索ではそのフィールドだけを対象にフィルタリングするため、メモリには必ずスコープを指定する必要があります。
HTTP 400 とともに LLM provider 'ollama' is not bundled in this image が表示される場合は、"provider": "ollama" を送信しています。openai_base_url を Ollama に向けた状態で "provider": "openai" を使用してください。
Postgres の expected 1536 dimensions, not 768 は、コレクションの作成時と embedder の返却時で幅が異なることを示します。vector store に embedding_model_dims を設定し、新しい collection_name を使用してください。
モデルを変更した後、エラーがどこにもないのに、検索結果に意味のない行が返されることがあります。幅は一致しているため、データベースは正常と判断します。しかし、2つのモデルは同じ文を異なる位置に配置します。新しいコレクションを作成し、再度 add してください。
Ollama への接続中に mem0 のログへ Connection refused が表示される場合、通常は openai_base_url の 127.0.0.1 を意味します。コンテナ内では、そのアドレスはコンテナ自身を指します。サービス名を使用するか、Ollama がホスト上で動作している場合はホストゲートウェイを使用してください。
add 時に nginx から 504 Gateway Time-out が返される場合、モデルの処理時間が proxy_read_timeout を超えています。値を引き上げ、リクエストを再試行する前に、メモリがすでに書き込まれていないか確認してください。
docker compose up --build 中の exit code 137 は、プロセスが SIGKILL で終了したことを示します。致命的シグナル N は 128 + N として報告され、SIGKILL のシグナル番号は 9 だからです。小規模な VPS で dashboard をビルドしている場合、最も可能性が高い送信元は out-of-memory killer です。そのため、対処する前に最初のセクションの dmesg チェックを実行してください。プロセスが kill されていることが確認されたら、swap を追加するか、より大きなマシンでイメージをビルドして registry に push してください。
error: port 3000 is already in use は、repo の make up target によって発生します。この target は、3000 または 8888 が使用中の場合に起動を拒否します。所有者は lsof -iTCP:3000 -sTCP:LISTEN で確認できます。
FAQ
mem0 をグラフメモリで実行する場合、Neo4j はまだ必要ですか?
いいえ。2026 年 4 月にリリースされた新しいメモリアルゴリズムでは、オープンソース SDK から graph_store と enable_graph の設定キーが削除されました。エンティティの抽出は通常の add 処理中に実行され、<collection_name>_entities という名前の 2 番目の pgvector コレクションに書き込まれます。そのため、外部のグラフデータベース、追加のコンテナ、セットアップ手順は不要です。代わりに、検索結果の relations フィールドはなくなりました。エンティティは、たどるエッジを提供するのではなく、メモリのランキングを引き上げます。そのため、これらの関係をたどっていたアプリケーションでは、mem0 の外部に独自のグラフストアが必要です。
自己ホスト型の mem0 サーバーを実行できる最小の VPS はどの程度ですか?
言語モデルを別の場所でホストする場合、API コンテナ、Postgres、ダッシュボードには RAM 2 GB と空きディスク容量約 4 GB で十分です。メモリが最も厳しくなるのは初回ビルドです。Next.js ダッシュボードのコンパイルには、実行時より多くのメモリが必要だからです。1 GB の環境では、ビルドが exit code 137 によって強制終了されます。Ollama を同じサーバーで実行する場合は、モデルを基準にサイズを決めてください。4-bit 量子化の 8B モデルだけでおよそ 6 GB 必要なので、8 GB を計画してください。
OpenAI API key なしで mem0 を実行できますか?
はい。Ollama の OpenAI 互換エンドポイントを使用できます。"provider": "ollama" を設定すると失敗します。サーバーイメージに含まれているライブラリは openai、anthropic、gemini だけで、HTTP 400 が返るためです。代わりに "provider": "openai" はそのままにし、llm と embedder の両方に、空でない任意の api_key を指定して "openai_base_url": "http://ollama:11434/v1" を設定します。Ollama はこの key を無視します。また、実際の provider が openai であるため、組み込みの provider チェックも通過します。
ローカルの embedding model に切り替えた後、mem0 が結果を返さないのはなぜですか?
pgvector のテーブルが固定幅で作成されているためです。embedding_model_dims のデフォルト値は 1536 ですが、nomic-embed-text は 768 を返すため、Postgres が expected 1536 dimensions, not 768 で insert を拒否します。mem0 は CREATE TABLE IF NOT EXISTS を使用してテーブルを作成するため、既存のコレクションでは数値だけを変更しても反映されません。embedding_model_dims にモデルの実際の幅を設定し、/v1/embeddings を呼び出して返される値の個数を数え、その幅を確認してください。同時に、vector store に新しい collection_name を指定します。