SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-28

Self-host mem0 trên VPS cần bao nhiêu RAM?

Tự chạy mem0 trên VPS với mức RAM thực tế, Compose bind localhost, TLS trước API và Ollama local. VPS 2 GB đủ nếu model chạy ở máy khác.

Chi phí RAM thực tế khi self-hosting mem0 trên VPS

Self-hosting mem0 nghĩa là chạy 3 container: memory server FastAPI, Postgres với extension pgvector và dashboard Next.js. mem0 là một lớp memory cho agent. Bạn gửi một cuộc hội thoại vào đó, language model trích xuất các thông tin bền vững từ cuộc hội thoại, rồi lưu các thông tin đó dưới dạng vector để truy vấn sau này có thể lấy lại những thông tin liên quan.

Hãy dự trù khoảng 1 GB resident memory cho 3 container và 3 đến 4 GB disk sau khi build xong các image. VPS 2 GB chạy ổn khi language model nằm ở nơi khác. Khi model chạy trên cùng máy qua Ollama, model sẽ dùng phần tài nguyên lớn hơn hẳn mọi thành phần khác: model 8B được quantise xuống 4 bit cần khoảng 6 GB riêng, nên cấu hình chạy hoàn toàn local cần từ 8 GB.

Đừng lấy các con số này từ một bài blog, kể cả bài này. Hãy đo chính stack bạn đã build.

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

docker stats in resident memory theo từng container. docker system df -v in dung lượng disk mà từng image và từng volume đang sử dụng.

Mức sử dụng ổn định không phải mức đỉnh. docker compose up -d --build build dashboard Next.js, và quá trình build bằng Node này là thời điểm dùng nhiều tài nguyên nhất trong toàn bộ quá trình cài đặt. Trên VPS 1 GB, kernel out-of-memory killer sẽ dừng quá trình này và build kết thúc với exit code 137. Hãy xác nhận nguyên nhân trước khi tìm lỗi trong Docker:

dmesg -T | grep -i "killed process"

Nếu một server có vẻ quá phức tạp so với nhu cầu của bạn, vẫn có các lựa chọn nhỏ gọn hơn. memory store local cho agent, không cần server và memory chạy ngay bên trong Claude Code đều không cần database. Hãy quay lại giải pháp này khi nhiều agent hoặc nhiều máy cần đọc chung một memory.

Có cần Neo4j cho graph memory của mem0 không?

Không. Nếu một hướng dẫn yêu cầu bạn thêm một Neo4j container, hướng dẫn đó cũ hơn code hiện tại.

Trước đây, graph memory trong mem0 có nghĩa là một graph database bên ngoài, được cấu hình bằng key graph_store với enable_graph đặt thành true. Thuật toán memory mới, được phát hành vào April 2026, đã xóa cả hai key khỏi SDK mã nguồn mở. Việc trích xuất entity hiện chạy trong luồng add thông thường, và các entity được ghi vào một collection pgvector thứ hai có tên giống collection chính, kèm _entities. Không cần chạy migration. Entity linking tích hợp sẵn sẽ bắt đầu hoạt động từ lần gọi add tiếp theo.

Bỏ graph store giúp loại một JVM container, phần heap của nó và vài trăm megabyte image. Trên VPS 2 GB, đây có thể là khác biệt giữa chạy bình thường và swap.

Dưới đây là những gì bạn phải đánh đổi. Trước đây, kết quả tìm kiếm có trường relations liệt kê các edge giữa các entity. Trường đó đã bị xóa. Các kết quả khớp entity hiện làm tăng vị trí của memory trong điểm kết hợp, và không còn cấu trúc nào để bạn duyệt. Nếu ứng dụng của bạn từng duyệt các quan hệ đó, mem0 không còn lưu chúng nữa. Bạn phải tự duy trì một graph database bên ngoài mem0 và dùng code của mình để cập nhật dữ liệu cho nó.

Tệp compose trong repo là cấu hình compose dành cho development

server/docker-compose.yaml khai báo name: mem0-dev, và nó thực sự chạy như vậy. Hãy đọc tệp này trước khi chạy, vì có 5 điểm không phù hợp với server.

  • Tệp build từ server/dev.Dockerfile và mount checkout của bạn đè lên image bằng .:/app. Vì vậy, container chạy nội dung hiện có trong thư mục đó thay vì nội dung bạn đã build.
  • Command của nó là rm -rf /app/packages && pip install -q --force-reinstall --no-deps mem0ai && alembic upgrade head && uvicorn main:app --reload. Command này cài lại mem0ai từ PyPI mỗi lần khởi động. Vì vậy, version chạy trên server có thể thay đổi trong một lần restart mà bạn không nghĩ là một lần nâng cấp.
  • Cùng bước pip đó cũng khiến lần restart không có outbound network bị lỗi trước khi uvicorn kịp chạy. Khi đó memory server bị down vì không truy cập được PyPI.
  • --reload khởi động file watcher của uvicorn. Tính năng này dùng để restart process khi bạn sửa code. Trong production, nó chỉ tốn memory và thêm một process mà không có tác dụng cần thiết. Dockerfile của production cũng chứa --reload trong CMD, nên trong cả hai trường hợp bạn đều phải override command.
  • Các port được publish là "8888:8000", "8432:5432" và "3000:3000". Port được publish mà không có địa chỉ ở phía trước sẽ bind vào 0.0.0.0. Vì vậy, Postgres sẽ nhận kết nối từ public internet trên port 8432 ngay khi stack khởi động.

Điểm cuối cùng cần được cảnh báo riêng. Docker publish port bằng cách ghi rule riêng lên trước chain do ufw quản lý. Vì vậy, ufw deny 8432 không đóng được port của container đã publish. Docker publish port trực tiếp vượt qua ufw giải thích các rule liên quan.

Tệp compose cho server thật

Làm việc trong server/, giữ nguyên init-db.sh ở vị trí hiện tại và thay docker-compose.yaml bằng nội dung sau.

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

Có 5 thay đổi quan trọng ở đây. Mỗi thay đổi đều có lý do.

Mỗi entry ports đều bắt đầu bằng 127.0.0.1, nên kernel chỉ chấp nhận các kết nối đó từ chính máy chủ. Mọi kết nối từ bên ngoài đi qua reverse proxy, là thành phần duy nhất giữ certificate.

Postgres hoàn toàn không có block ports. Container mem0 kết nối đến nó qua mem0_network bằng service name, nên publish cổng 8432 không mang lại lợi ích gì mà chỉ mở thêm một cổng. Dùng docker compose exec postgres psql -U postgres khi cần mở shell.

Phần history chuyển từ bind mount ./history sang named volume. Bind mount gắn dữ liệu với một path và một uid trên host này, còn named volume là một object mà Docker có thể snapshot và di chuyển. Named volume và bind mount giải thích khi nào nên dùng từng loại.

Command này bỏ --reload và giữ lại alembic upgrade head. Hãy giữ bước migration đó. Nếu bỏ, app sẽ khởi động với database chưa có table và mọi request đều fail ở query đầu tiên.

NEXT_PUBLIC_API_URL là URL mà browser gọi, nên phải là địa chỉ HTTPS public, không phải http://mem0:8000. Next.js inline mọi giá trị NEXT_PUBLIC_ tại thời điểm build, nên thay đổi giá trị này cần docker compose up -d --build mem0-dashboard. Restart đơn thuần vẫn giữ giá trị cũ đã được nhúng vào JavaScript, khiến dashboard gọi nhầm host.

Secrets nằm trong .env, và .env không được đưa lên Internet

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

Đặt POSTGRES_PASSWORD, JWT_SECRET và ADMIN_API_KEY. Giữ nguyên AUTH_DISABLED=false. Tên của flag này mô tả đúng chức năng của nó: khi bật, server sẽ cung cấp toàn bộ nội dung memory mà nó đang giữ cho bất kỳ ai có thể truy cập cổng đó. Đặt MEM0_TELEMETRY=false nếu bạn không muốn gửi sự kiện onboarding lên upstream.

ADMIN_API_KEY được so sánh với header X-API-Key bằng secrets.compare_digest, và khi khớp thì bỏ qua mọi lần tra cứu database. Đây là credential root cho toàn bộ API. Hãy xử lý nó như vậy: không để trong shell history, không commit vào git và không dán vào prompt. Các file env của Compose và nơi secret bị rò rỉ từ đó và giữ API key ngoài context của agent đều áp dụng trực tiếp, vì các caller của server này là agent.

Các giá trị được tải từ env_file nằm trong environment của container, và docker inspect sẽ in toàn bộ chúng. Bất kỳ ai trong group docker đều có thể đọc chúng, còn bất kỳ ai trong group docker về thực tế có quyền root trên host.

Đặt TLS phía trước API thay vì mở cổng 8888

API trả lời trên 127.0.0.1:8888 và dashboard trên 127.0.0.1:3000. nginx thực hiện TLS termination trên cổng 443 rồi chuyển tiếp đến cả hai.

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 quan trọng hơn bạn nghĩ. Lệnh add bị block trong khi language model đọc conversation và trích xuất fact. Một model local 8B chạy trên CPU thường mất nhiều thời gian hơn timeout mặc định 60 giây của nginx. Khi đó caller nhận 504 Gateway Time-out trong lúc model vẫn đang xử lý và memory vẫn được ghi. Kết quả là bạn có một memory nhưng lại được báo rằng thao tác thất bại.

Đóng các cổng còn lại bằng chính sách ufw mặc định deny, chỉ để mở 22 và 443. Cấp certificate bằng certbot trên Ubuntu 24.04 phía sau nginx. Nếu máy đã dùng Traefik để định tuyến nhiều ứng dụng Compose ở phía trước các app khác, hãy thêm mem0 vào router đó thay vì cài thêm một proxy.

Kiểm thử nhanh: thêm một memory rồi đọc lại

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"}'

Phản hồi hợp lệ là một JSON object có danh sách results. Mỗi entry chứa id, phần văn bản memory đã trích xuất và "event": "ADD". Algorithm hiện tại chỉ trả về các event ADD. Các event UPDATE và DELETE đã bị loại bỏ, nên việc chúng không xuất hiện không phải là lỗi.

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}'

Fact về Postgres 17 phải được trả về kèm score. Truyền identifier bên trong filters như minh họa. user_id ở cấp cao nhất vẫn hoạt động, và server ghi log Top-level user_id in /search is deprecated. Use filters={...} instead. mỗi lần bạn sử dụng nó.

Dọn dẹp dữ liệu sau khi kiểm thử để dữ liệu test không làm nhiễu các lần search thực tế:

curl -sS -X DELETE "http://127.0.0.1:8888/memories?user_id=smoke" \
  -H "X-API-Key: $MEM0_KEY"

Nếu search trả về ít row hơn dự kiến, hãy kiểm tra các giá trị mặc định trước khi kết luận retrieval có vấn đề. Trong release hiện tại, top_k mặc định là 20, giảm từ 100, còn threshold mặc định là 0.1 thay vì không giới hạn. Vì vậy, các match yếu sẽ được tự động lọc. Sau khi curl hoạt động, bạn có thể kết nối chính các endpoint này vào một agent, trực tiếp hoặc thông qua một MCP server chạy trên cùng VPS.

Chạy mem0 hoàn toàn không cần OpenAI key

Bắt đầu với điểm chặn này, vì bạn sẽ gặp nó trong 5 phút đầu tiên. Server image đi kèm một bộ provider library cố định, và /configure từ chối mọi thứ nằm ngoài bộ đó:

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.

Bạn không cần rebuild bất cứ thứ gì. Ollama cung cấp API tương thích với OpenAI tại /v1, hỗ trợ /v1/chat/completions và /v1/embeddings, còn openai provider của mem0 chấp nhận một openai_base_url. Trỏ key đó đến Ollama thì bundled check sẽ pass, vì provider thực sự là openai. Chỉ có địa chỉ thay đổi.

Thêm Ollama vào cùng Compose project:

  ollama:
    image: ollama/ollama
    restart: unless-stopped
    networks: [mem0_network]
    ports:
      - "127.0.0.1:11434:11434"
    volumes:
      - ollama_models:/root/.ollama

Thêm ollama_models: bên dưới key cấp cao nhất volumes:, sau đó pull một chat model và một embedding model:

docker compose up -d ollama
docker compose exec ollama ollama pull llama3.1:8b
docker compose exec ollama ollama pull nomic-embed-text

Nếu Ollama đã chạy trên host dưới dạng systemd unit, như trong chạy Ollama trực tiếp trên VPS, không trỏ container đến 127.0.0.1:11434. Bên trong mem0 container, 127.0.0.1 là mem0 container. Cấp cho mem0 service extra_hosts: ["host.docker.internal:host-gateway"], đặt Environment="OLLAMA_HOST=0.0.0.0:11434" trong systemd drop-in để Ollama listen trên một địa chỉ mà bridge có thể truy cập, và giữ port 11434 đóng tại firewall.

Hỏi model về embedding dimension trước khi cấu hình bất cứ thứ gì

Bước này quyết định retrieval có hoạt động hay không.

pgvector store của mem0 tạo table với vector width cố định là vector vector(1536), vì embedding_model_dims mặc định là 1536, bằng width của OpenAI text-embedding-3-small. nomic-embed-text trả về 768 value. Bên trong mem0 không có phần nào so sánh hai con số này, nên sai lệch sẽ xuất hiện từ Postgres ngay ở lần insert đầu tiên:

expected 1536 dimensions, not 768

Cũng không nên tin con số trong đoạn này. Hãy hỏi 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']))"

Lệnh này in ra width mà collection của bạn phải sử dụng. Ghi cấu hình vào một file, vì paste Postgres password qua shell quoting là cách typo lọt vào production.

{
  "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"

Lệnh gọi thứ hai đọc lại cấu hình. Đây là bước kiểm tra xác nhận thao tác ghi đã thành công. Sau đó chạy lại smoke test ở trên.

Có 4 chi tiết trong JSON đó không dễ nhận ra. Mỗi chi tiết đều làm hỏng hệ thống nếu cấu hình sai.

api_key là chuỗi ollama, và Ollama bỏ qua giá trị của nó. Giá trị này không được để trống, vì OpenAI client library sẽ raise trước khi bất kỳ request nào rời process nếu không có key. Mọi chuỗi không rỗng đều dùng được.

embedding_model_dims được đặt trên vector store, và cố ý không có embedding_dims trên embedder. mem0 chỉ gửi OpenAI dimensions parameter khi bạn đặt embedding_dims, còn các backend không implement Matryoshka truncation sẽ từ chối parameter này hoàn toàn. Đặt width tại nơi table được tạo và để embedder ở trạng thái mặc định.

collection_name là thiết lập mới. mem0 tạo table với CREATE TABLE IF NOT EXISTS, nên trỏ một width khác vào collection hiện có sẽ không có tác dụng: column vector(1536) cũ vẫn giữ nguyên và mọi insert đều fail. Thay đổi width cần một collection name mới, hoặc bạn phải tự xóa table cũ.

Host trong openai_base_url là Compose service name ollama, không phải localhost. Các container resolve lẫn nhau bằng service name trên shared network.

Chi phí của mô hình chạy hoàn toàn local

Hãy đánh giá chất lượng một cách thực tế. Các benchmark score được công bố của mem0 được đo bằng frontier model thực hiện việc trích xuất. Vì vậy, hãy xem chúng là mức trần thay vì dự báo cho model 8B trên VPS của bạn. Model nhỏ thường tạo ra fact mơ hồ hơn và đôi khi trả về prose dù được yêu cầu JSON. Điều này biểu hiện bằng việc add call trả về một results list rỗng mà không có lỗi.

Tốc độ là chi phí còn lại. Việc trích xuất chỉ bằng CPU mất vài giây cho mỗi add call, và mọi message bạn lưu đều phải chịu chi phí này. Model lan man vượt quá JSON được yêu cầu sẽ làm tình hình tệ hơn. Vì vậy, giới hạn câu trả lời bằng num_predict sẽ đặt giới hạn cho thời gian chạy của từng add call. Nếu latency này ảnh hưởng đến hệ thống, dùng VPS có gắn GPU là giải pháp thực tế. Tăng thêm CPU core cho model 8B giúp ít hơn nhiều so với kỳ vọng. Đổi model thường rẻ hơn đổi máy, và Nemotron 3.5 Lightning trên VPS cho biết tag cần pull, lượng RAM model cần và CPU-only có đủ nhanh để sử dụng hay không.

Dù chọn phương án nào, vẫn có một quy tắc: không bao giờ trộn các embedding model trong cùng một collection. Hai model khác nhau dù tình cờ có cùng width vẫn tạo ra các vector không thể so sánh. Insert vẫn thành công, search vẫn trả về row, nhưng các row đó sai và không có thành phần nào báo lỗi.

Backup: có hai database, không phải một

Lỗi phổ biến nhất khi backup mem0 là chỉ dump một database. init-db.sh tạo mem0_app cùng với database mặc định postgres, và mỗi database lưu một loại dữ liệu khác nhau. Database postgres lưu các collection pgvector, tức các memory. mem0_app lưu user, session, API key và request log. Mỗi ứng dụng self-hosted phân tách state theo cách riêng. Vì vậy, hai photo server làm cùng một việc vẫn cần các lệnh backup khác nhau. Hãy kiểm tra ứng dụng lưu những gì trước khi tin tưởng một bản dump. Ở đầu kia của phạm vi này là trường hợp như một thư viện Jellyfin được dựng lại thành cửa hàng video kiểu thập niên 90. Thư viện đó đọc toàn bộ catalogue từ một service khác nên gần như chỉ cần sao chép configuration của chính nó. Ngược lại, mem0 cần cả hai database. Nếu không, bản restore sẽ không dùng được.

Nếu chỉ restore postgres, các memory sẽ quay lại nhưng toàn bộ account và API key sẽ mất. Khi đó không có gì có thể authenticate để đọc chúng. Hãy dump cả hai database cùng với role bằng một lệnh:

docker compose exec -T postgres pg_dumpall -U postgres --clean \
  | gzip > "mem0-$(date +%F).sql.gz"

Volume history tách biệt với Postgres và cần được sao lưu riêng:

docker run --rm -v mem0_mem0_history:/data -v "$PWD:/backup" \
  alpine tar czf /backup/mem0-history.tgz -C /data .

Docker thêm tên project vào trước tên volume. Hãy xác nhận tên volume bằng docker volume ls trước khi giả định mem0_mem0_history.

Hãy restore vào một container tạm rồi kiểm tra số lượng row trước khi tin rằng backup hợp lệ:

gunzip -c mem0-2026-08-03.sql.gz \
  | docker compose exec -T postgres psql -U postgres -d postgres

Một backup chưa từng được restore chỉ là phỏng đoán. Khi các bản dump đã đúng, hãy đẩy chúng ra khỏi máy bằng restic snapshot đến storage bên ngoài, vì backup nằm trên chính server mà nó cần bảo vệ thì không bảo vệ được gì.

Các failure mode và chính xác các chuỗi bạn sẽ thấy

{"detail":"Authentication required. Provide a Bearer token or X-API-Key header."} có nghĩa là header bị thiếu hoặc viết sai. Tên đúng là X-API-Key, và curl gửi nguyên văn tên header.

{"detail":"At least one identifier (user_id, agent_id, run_id) is required."} khi add có nghĩa là request không có field nào trong số đó. Một memory phải được gắn với một phạm vi cụ thể, vì search lọc chính xác theo các field đó.

LLM provider 'ollama' is not bundled in this image cùng HTTP 400 có nghĩa là bạn đã gửi "provider": "ollama". Dùng "provider": "openai" với openai_base_url trỏ đến Ollama.

expected 1536 dimensions, not 768 từ Postgres có nghĩa là collection được tạo với một width, còn embedder trả về width khác. Đặt embedding_model_dims trên vector store và dùng collection_name mới.

Search trả về các row không hợp lý sau khi đổi model nhưng không xuất hiện error ở đâu. Width vẫn khớp nên database không báo lỗi, nhưng hai model đặt cùng một câu vào các vị trí khác nhau. Tạo collection mới rồi add lại dữ liệu.

Connection refused trong log của mem0 khi kết nối đến Ollama thường có nghĩa là 127.0.0.1 trong openai_base_url. Bên trong container, địa chỉ đó trỏ đến chính container. Dùng service name, hoặc host gateway khi Ollama chạy trên host.

504 Gateway Time-out từ nginx khi add có nghĩa là model chạy lâu hơn proxy_read_timeout. Tăng giá trị này, rồi kiểm tra memory đã được ghi chưa trước khi retry request.

exit code 137 trong lúc docker compose up --build là out-of-memory killer đang dừng quá trình build dashboard. Thêm swap, hoặc build image trên máy lớn hơn rồi push image lên registry.

error: port 3000 is already in use xuất phát từ target make up của repo. Target này từ chối khởi động khi cổng 3000 hoặc 8888 đang bị chiếm. Tìm process sở hữu cổng bằng lsof -iTCP:3000 -sTCP:LISTEN.

FAQ

Tôi có còn cần Neo4j để chạy mem0 với graph memory không?

Không. Thuật toán memory mới, được phát hành vào tháng 4 năm 2026, đã xóa các configuration key graph_store và enable_graph khỏi SDK mã nguồn mở. Việc trích xuất entity hiện chạy trong lúc add bình thường và ghi vào một collection pgvector thứ hai có tên <collection_name>_entities. Vì vậy không cần graph database bên ngoài, container bổ sung hoặc bước migration. Đổi lại, field relations không còn xuất hiện trong kết quả search. Entity hiện chỉ làm tăng thứ hạng của memory thay vì cung cấp các edge để duyệt. Do đó, ứng dụng từng duyệt qua các relationship này cần có graph store riêng bên ngoài mem0.

VPS nhỏ nhất có thể chạy mem0 server tự host là loại nào?

Nếu language model được host ở nơi khác, 2 GB RAM và khoảng 4 GB disk trống là đủ cho API container, Postgres và dashboard. Giai đoạn dễ thiếu tài nguyên nhất là lần build đầu tiên, vì biên dịch dashboard Next.js dùng nhiều memory hơn lúc chạy. Máy 1 GB thường bị kill tiến trình build với exit code 137. Nếu Ollama chạy trên cùng server, hãy chọn cấu hình dựa trên model: model 8B ở mức quantisation 4-bit tự nó cần khoảng 6 GB, nên nên dùng 8 GB.

Tôi có thể chạy mem0 mà không cần OpenAI API key không?

Có, thông qua endpoint tương thích với OpenAI của Ollama. Thiết lập "provider": "ollama" sẽ fail vì server image chỉ tích hợp các library openai, anthropic và gemini, rồi trả về HTTP 400. Thay vào đó, giữ "provider": "openai" và đặt "openai_base_url": "http://ollama:11434/v1" bằng bất kỳ api_key không rỗng nào cho cả llm và embedder. Ollama bỏ qua key này. Kiểm tra provider đi kèm vẫn pass vì provider thực tế là openai.

Tại sao mem0 không trả về kết quả sau khi tôi chuyển sang local embedding model?

Vì pgvector table được tạo với độ rộng cố định. embedding_model_dims mặc định là 1536, còn nomic-embed-text trả về 768, nên Postgres từ chối insert với expected 1536 dimensions, not 768. mem0 tạo table bằng CREATE TABLE IF NOT EXISTS, vì vậy chỉ thay đổi con số này sẽ không tác động đến collection hiện có. Đặt embedding_model_dims theo đúng độ rộng thực của model, xác nhận độ rộng đó bằng cách gọi /v1/embeddings và đếm các giá trị được trả về, đồng thời tạo collection_name mới cho vector store.