SSD Nodes Learn 🎉 VPS $4.99/月〜
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-07

VPSでmem0をセルフホストするRAMと構成

mem0を自分のVPSで動かす手順です。常駐RAMは約1GB、ディスクは3〜4GB、2GB VPSの目安、Ollamaの8Bモデルを同居させる場合は8GBからの構成を解説します。

VPS で mem0 をセルフホストする場合に実際に必要な RAM

mem0 のセルフホストでは、3 つのコンテナを実行します。FastAPI メモリーサーバー、pgvector 拡張を有効にした Postgres、Next.js ダッシュボードです。mem0 はエージェント向けのメモリーレイヤーです。会話を送信すると、言語モデルがその会話から長期的に保持すべき事実を抽出し、それらをベクトルとして保存します。後続のクエリでは、関連する事実を取得できます。

3 つのコンテナで、常駐メモリーはおよそ 1 GB、イメージのビルド後に必要なディスク容量は 3〜4 GB と見積もってください。言語モデルを別の場所で実行する場合、2 GB の VPS で無理なく動作します。モデルを Ollama 経由で同じサーバー上で実行すると、必要なリソースの大半をモデルが占めます。4 bit に量子化した 8B モデルだけで約 6 GB を必要とするため、すべてをローカルで動かす構成は 8 GB から始まります。

これらの数値を、この記事を含むブログ記事からそのまま採用しないでください。実際に構築したスタックを測定してください。

docker compose ps
docker stats --no-stream
docker system df -v

docker 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 では、これにより実行可能な状態を維持できるか、スワップが発生するかが変わります。

失われる機能を明確に説明します。以前の検索結果には、エンティティ間のエッジを列挙する relations フィールドが含まれていました。このフィールドは廃止されました。現在は、エンティティが一致すると、統合スコアでそのメモリの順位が上がりますが、たどれる構造はありません。アプリケーションがこれらの関係をたどっていた場合、mem0 は関係を保持しなくなります。その場合は、mem0 の外部で独自のグラフデータベースを管理し、アプリケーションコードからデータを投入してください。

リポジトリ内の compose ファイルは開発用です

server/docker-compose.yamlname: 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 --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 を残しています。この migration 手順は残してください。これがないと、アプリはテーブルのないデータベースに接続して起動するため、最初のクエリですべてのリクエストが失敗します。

NEXT_PUBLIC_API_URL はブラウザーが呼び出す URL なので、http://mem0:8000 ではなく公開 HTTPS アドレスにする必要があります。Next.js はすべての NEXT_PUBLIC_ の値をビルド時に埋め込むため、値を変更するには docker compose up -d --build mem0-dashboard が必要です。単に再起動しただけでは JavaScript に古い値が埋め込まれたままになり、ダッシュボードは誤ったホストを呼び出します。

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 .env

POSTGRES_PASSWORDJWT_SECRETADMIN_API_KEYを設定します。AUTH_DISABLED=falseは設定しません。このフラグの動作は名前のとおりです。有効にすると、サーバーは保持しているすべてのメモリを、ポートに到達できる全員へ渡します。オンボーディングイベントを上流へ送信しない場合は、MEM0_TELEMETRY=falseを設定します。

ADMIN_API_KEYsecrets.compare_digestによりX-API-Keyヘッダーと比較され、一致するとすべてのデータベース検索が省略されます。これは API 全体に対する root の認証情報です。root の認証情報として扱ってください。シェル履歴や git に残さず、プロンプトへ貼り付けないでください。Compose の環境変数ファイルと、そこから Secret が漏れる場所およびAPI key を agent のコンテキストから除外する方法がそのまま当てはまります。このサーバーの呼び出し元は agent だからです。

env_fileから読み込んだ値はコンテナの環境変数に格納され、docker inspectですべて出力されます。dockerグループのメンバーはそれらを読み取れます。また、dockerグループのメンバーは、実質的にホスト上の root です。

API の前段に TLS を配置し、8888 を公開しない

API は 127.0.0.1:8888 で、ダッシュボードは 127.0.0.1:3000 で応答します。nginx が 443 で TLS(トランスポート層セキュリティ)を終端し、両方へ転送します。

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 は見た目以上に重要です。add 呼び出しでは、言語モデルが会話を読み取り、事実を抽出する間、処理がブロックされます。CPU 上でローカルの 8B モデルを実行すると、nginx のデフォルトである 60 秒を定期的に超えます。その場合、モデルが処理を続け、メモリへの書き込みも行われている間に、呼び出し元には 504 Gateway Time-out が返されます。結果として、失敗したと通知されたメモリが残ります。

残りの通信は デフォルト拒否の ufw ポリシーで閉じ、22 と 443 だけを開放します。証明書は、nginx の背後にある Ubuntu 24.04 で certbot を使用する方法で発行します。すでにそのサーバーで 複数の 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-text

VPS 上で Ollama を直接実行する場合のように、Ollama がホスト上で systemd unit としてすでに実行されている場合は、コンテナから 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 が bridge から到達できるアドレスで待ち受けるようにします。ファイアウォールでは 11434 を閉じたままにします。

何も設定する前にモデルの埋め込み次元を確認する

この手順によって、検索が機能するかどうかが決まります。

mem0 の pgvector ストアは、vector vector(1536) の固定されたベクトル幅でテーブルを作成します。これは、embedding_model_dims のデフォルト値が 1536 であり、OpenAI の text-embedding-3-small の幅だからです。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 client library がリクエストをプロセス外へ送信する前に例外を発生させるためです。空でない文字列なら何でも使用できます。

embedding_model_dimsvector store に設定し、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 が公開しているベンチマークスコアは、抽出に最先端モデルを使用して測定されたものです。そのため、VPS 上の 8B モデルで同じ結果が得られる予測値ではなく、上限の目安として扱ってください。小型モデルは曖昧な事実を書き出しやすく、JSON を要求しても文章を返すことがあります。その場合、エラーなしで add call が空の results リストを返すことがあります。

速度もコストになります。CPU だけで抽出すると、1 回の add call に数秒かかります。保存するすべてのメッセージでこの処理が発生します。レイテンシーが問題になる場合は、GPU を接続した VPS を使用するのが適切です。8B モデルに CPU コアを追加しても、期待するほどの改善は得られません。

選択にかかわらず、1 つのコレクション内で埋め込みモデルを混在させないでください。幅が同じでも異なるモデルが生成したベクトルは比較できません。insert は成功し、検索は行を返しますが、その行は誤っています。そして、どこにもエラーは報告されません。

バックアップ: データベースは1つではなく2つあります

mem0 で最もよくあるバックアップのミスは、1つのデータベースだけをダンプすることです。init-db.sh はデフォルトの postgres データベースとともに mem0_app を作成します。これらには異なるデータが格納されています。postgres データベースには、メモリにあたる pgvector コレクションが格納されます。mem0_app には、ユーザー、セッション、API キー、リクエストログが格納されます。

postgres だけを復元すると、メモリは戻りますが、すべてのアカウントと API キーが失われます。そのため、メモリを読み取るための認証ができません。ロールも含め、両方を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 はヘッダー名をそのまま送信します。

追加時の {"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" を送信しています。Ollama を指す openai_base_url とともに "provider": "openai" を使用してください。

Postgres の expected 1536 dimensions, not 768 は、コレクションの作成時と embedder の返却時でベクトルの次元数が異なることを示します。ベクトルストアに embedding_model_dims を設定し、新しい collection_name を使用してください。

モデルを変更した後、どこにもエラーがないのに検索結果の行が意味をなさない場合があります。次元数は一致しているためデータベースでは問題になりませんが、2つのモデルは同じ文を異なる位置に配置します。新しいコレクションを作成し、データを再追加してください。

Ollama への接続中に mem0 のログに Connection refused が表示される場合、多くは openai_base_url127.0.0.1 が原因です。コンテナ内では、そのアドレスはコンテナ自身を指します。サービス名を使用するか、Ollama がホスト上で動作している場合はホストゲートウェイを使用してください。

追加時に nginx から 504 Gateway Time-out が返る場合、モデルの処理時間が proxy_read_timeout を超えています。値を引き上げ、リクエストを再試行する前にメモリがすでに書き込まれていないか確認してください。

docker compose up --build 中の exit code 137 は、out-of-memory killer がダッシュボードのビルドを停止したことを示します。swap を追加するか、より大きなマシンでイメージをビルドしてレジストリに push してください。

error: port 3000 is already in use は、リポジトリの make up ターゲットに起因します。このターゲットは、3000 または 8888 が使用中の場合に起動を拒否します。lsof -iTCP:3000 -sTCP:LISTEN で使用プロセスを確認してください。

FAQ

mem0 をグラフメモリで実行する場合も、Neo4j は必要ですか?

いいえ。2026 年 4 月にリリースされた新しいメモリアルゴリズムでは、オープンソース SDK から graph_storeenable_graph の設定キーが削除されました。エンティティ抽出は通常の add 処理中に実行され、<collection_name>_entities という名前の 2 つ目の pgvector コレクションに書き込まれます。そのため、外部のグラフデータベース、追加のコンテナ、移行手順は不要です。代わりに、検索結果の relations フィールドはなくなりました。エンティティは、たどるエッジを提供するのではなく、メモリのランキングを引き上げます。そのため、これらの関係をたどっていたアプリケーションでは、mem0 の外部に独自のグラフストアが必要です。

self-hosted の mem0 サーバーを実行できる最小の VPS はどの程度ですか?

言語モデルを別の場所でホストする場合、API コンテナ、Postgres、ダッシュボードには、RAM 2 GB と空きディスク容量約 4 GB で十分です。負荷が高くなるのは最初のビルドです。Next.js ダッシュボードのコンパイルには、実行時より多くのメモリが必要で、RAM 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 を指定してください。