Self-host mem0 trên VPS: RAM, Docker Compose và Ollama
Tự chạy mem0 trên VPS với mức RAM thực tế, file Compose bind localhost, TLS trước API và Ollama local. VPS 2 GB đủ nếu model chạy máy khác; local cần ít nhất 8 GB.
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à memory layer cho agent. Bạn gửi một cuộc hội thoại vào đó, language model trích xuất các dữ kiện lâu dài từ cuộc hội thoại, rồi lưu các dữ kiện đó dưới dạng vector để truy vấn sau này có thể lấy lại những dữ kiện 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 thông qua Ollama, model sẽ chiếm phần lớn tài nguyên: model 8B được quantise xuống 4 bit cần khoảng 6 GB riêng, nên bản triển khai hoàn toàn local cần ít nhất 8 GB.
Không nên lấy các con số đó 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 -vdocker 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.
Trạng thái ổn định không phải mức đỉnh. docker compose up -d --build compile dashboard Next.js, và quá trình build Node đó là thời điểm ngốn tài nguyên RAM 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 tiến 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, các lựa chọn nhỏ hơn là hoàn toàn khả thi. local agent memory store không cần server và memory nằm ngay trong Claude Code đều không cần database. Hãy quay lại đây khi nhiều agent hoặc nhiều máy cần đọc cùng một memory.
Bạn 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 container Neo4j, hướng dẫn đó cũ hơn code hiện tại.
Trước đây, graph memory trong mem0 có nghĩa là dùng một graph database bên ngoài, được cấu hình trong 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 tháng 4 năm 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. Tên collection này là tên collection chính của bạn với _entities nối vào cuối. Không cần chạy migration. Tính năng 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 container JVM, phần heap của nó và vài trăm megabyte image. Trên một VPS 2 GB, đó là khác biệt giữa chạy ổn định và phải dùng swap.
Dưới đây là những gì bạn mất, nói thẳng. 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ị loại bỏ. 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ự vận hành một graph database bên ngoài mem0 và dùng code của mình để ghi dữ liệu vào đó.
File compose trong repo là file compose dành cho môi trường development
server/docker-compose.yaml khai báo name: mem0-dev, và nó thực sự chạy theo cách đó. Hãy đọc file trước khi chạy, vì có năm điểm không phù hợp với server.
- File build từ
server/dev.Dockerfilevà mount thư mục checkout của bạn đè lên image bằng.:/app, nên container chạy nội dung hiện có trong thư mục đó thay vì nội dung bạn đã build. - Command của file 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ạimem0aitừ PyPI mỗi lần khởi động, nên phiên bản server chạy có thể thay đổi trong một lần restart mà bạn không nghĩ là đang nâng cấp. - Cùng bước pip đó cũng khiến restart thất bại trước khi uvicorn chạy nếu không có network outbound. Khi đó memory server bị down vì không thể truy cập PyPI.
--reloadkhởi động file watcher của uvicorn. Tính năng này dùng để restart tiến trình khi bạn sửa code, nhưng trong production nó chỉ tốn memory và thêm một tiến trình mà không có tác dụng.Dockerfilecho production cũng chứa--reloadtrongCMD, 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ỉ đứng trước sẽ bind vào0.0.0.0, nên 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 đặt rule riêng lên trước chain do ufw quản lý, nên ufw deny 8432 không đóng được port của container đã publish. Docker publish port trực tiếp qua ufw giải thích các rule liên quan.
Một file compose cho máy chủ thực
Làm việc trong server/, giữ nguyên init-db.sh tại 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: bridgeCó 5 thay đổi quan trọng ở đây, và mỗi thay đổi đều có lý do.
Mỗi mục 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 đều đi qua reverse proxy, là thành phần duy nhất quản lý certificate.
Postgres hoàn toàn không có block ports. Container mem0 kết nối đến nó qua mem0_network bằng tên service, 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.
Dữ liệu history được chuyển từ bind mount ./history sang named volume. Bind mount gắn dữ liệu với một đường dẫn và một uid cụ thể 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.
Lệnh này bỏ --reload và giữ lại alembic upgrade head. Giữ nguyên bước migration đó. Nếu bỏ bước này, app sẽ khởi động với một database chưa có table, và mọi request đều fail ngay ở query đầu tiên.
NEXT_PUBLIC_API_URL là URL mà trình duyệt gọi đến, 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. Chỉ restart sẽ giữ nguyên giá trị cũ đã được nhúng vào JavaScript, khiến dashboard gọi sai 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 .envThiết lập POSTGRES_PASSWORD, JWT_SECRET và ADMIN_API_KEY. Giữ nguyên AUTH_DISABLED=false. Tên của flag này phản ánh đúng chức năng: khi bật, server gửi toàn bộ dữ liệu đang giữ trong memory cho bất kỳ ai có thể truy cập cổng đó. Thiết lập 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. File env của Compose và nơi secrets 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 nạp từ env_file nằm trong environment của container, còn docker inspect sẽ in toàn bộ các giá trị đó. Bất kỳ ai thuộc group docker đều có thể đọc chúng, và bất kỳ ai thuộc group docker về cơ bản 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, còn dashboard chạy 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 dịch vụ.
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 cuộc hội thoại và trích xuất các fact. Model local 8B chạy trên CPU thường mất nhiều thời gian hơn giá trị mặc định 60 giây của nginx. Khi đó caller nhận 504 Gateway Time-out, dù 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 là thao tác đã thất bại.
Đóng các cổng còn lại bằng policy ufw mặc định deny, chỉ để mở 22 và 443. Cấp certificate bằng certbot trên Ubuntu 24.04 chạy phía sau nginx. Nếu máy chủ đã dùng Traefik để định tuyến nhiều ứng dụng Compose cho các app khác, hãy thêm mem0 vào router đó thay vì cài thêm một proxy.
Kiểm tra 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 đối tượng JSON có danh sách results. Mỗi phần tử chứa id, văn bản memory đã trích xuất và "event": "ADD". Thuật toán 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}'Thông tin 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 sau khi kiểm tra để dữ liệu thử nghiệm không làm nhiễu các tìm kiếm 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 cho rằng retrieval có vấn đề. Trong bản 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 đặt giá trị, nên các kết quả khớp yếu hiện được tự động lọc. Khi đã chạy được qua curl, bạn có thể kết nối các endpoint này vào agent theo cách 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
Hãy 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ộ này:
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 build lại 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 provider openai 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ỉ đị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/.ollamaThêm ollama_models: bên dưới key cấp cao nhất volumes:, rồi 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-textNế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. Gán extra_hosts: ["host.docker.internal:host-gateway"] cho mem0 service, đặ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, đồng thời giữ cổng 11434 bị chặn trên 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, tức width của text-embedding-3-small của OpenAI. nomic-embed-text trả về 768 giá trị. Không có phần nào bên trong mem0 so sánh hai con số này, nên mismatch sẽ xuất hiện từ Postgres ở lần insert đầu tiên:
expected 1536 dimensions, not 768Cũ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"Call thứ hai đọc lại cấu hình. Đây là bước kiểm tra việc ghi đã thành công. Sau đó lặp lại smoke test ở trên.
Có 4 chi tiết trong JSON này không rõ ràng, và mỗi chi tiết đều sẽ làm hỏng một phần nếu bạn đặt sai.
api_key là chuỗi ollama, còn Ollama bỏ qua giá trị của nó. Giá trị này không được để trống, vì OpenAI client library sẽ raise error trước khi request rời khỏi process nếu chưa set key. Mọi chuỗi không rỗng đều dùng được.
embedding_model_dims nằm trên vector store, và cố ý không có embedding_dims trên embedder. mem0 chỉ gửi parameter dimensions của OpenAI khi bạn set embedding_dims, còn những backend không implement Matryoshka truncation sẽ từ chối parameter này ngay lập tức. Đặt width tại nơi tạo table và không cấu hình gì thêm cho embedder.
collection_name là tùy chọn 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 làm thay đổi gì: cột vector(1536) cũ vẫn giữ nguyên và mọi insert đều fail. Khi đổi width, bạn cần dùng tên collection mới hoặc tự xóa table cũ.
Host trong openai_base_url là tên Compose service ollama, không phải localhost. Các container phân giải 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 điểm benchmark được công bố của mem0 được đo bằng frontier model thực hiện việc extraction, nên hãy xem đó là mức trần thay vì dự báo cho model 8B chạy trên VPS của bạn. Model nhỏ thường tạo ra các fact mơ hồ hơn và đôi khi trả về prose trong khi được yêu cầu JSON. Điều này thể hiện qua việc add call trả về danh sách results rỗng mà không có error.
Tốc độ là chi phí thứ hai. Extraction chỉ dù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 độ trễ này. Nếu độ trễ đó quan trọng, VPS có gắn GPU là giải pháp thực tế. Tăng thêm CPU core cho model 8B hiệu quả thấp hơn nhiều so với mọi người thường nghĩ.
Dù chọn phương án nào, vẫn phải tuân thủ một nguyên 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ề các row, nhưng các row đó sai và không có thành phần nào báo error.
Backup: có 2 database, không phải 1
Sai lầm phổ biến nhất khi backup mem0 là dump chỉ 1 database. init-db.sh tạo mem0_app bên cạnh database mặc định postgres, và mỗi database chứa dữ liệu khác nhau. Database postgres chứa các collection pgvector, tức là các memory. mem0_app chứa user, session, API key và request log.
Nếu chỉ restore postgres, các memory sẽ quay lại nhưng toàn bộ account và API key sẽ mất, nên không có gì có thể authenticate để đọc chúng. Dump cả 2 database cùng với role bằng một command:
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, vì vậy 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 có thể dùng được:
gunzip -c mem0-2026-08-03.sql.gz \
| docker compose exec -T postgres psql -U postgres -d postgresMột backup chưa từng được restore chỉ là phỏng đoán. Khi các dump đã chính xác, hãy đẩy chúng ra khỏi máy bằng restic snapshot đến storage ngoài site, vì backup nằm trên chính server mà nó bảo vệ thì không bảo vệ được gì.
Các dạng lỗi và chuỗi chính xác 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ó các trường đó. Memory phải được gắn scope vào một đối tượng nào đó, vì bộ lọc tìm kiếm chỉ lọc đúng trên các trường này.
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 độ rộng, còn embedder trả về độ rộng khác. Đặt embedding_model_dims trên vector store và dùng một collection_name mới.
Kết quả tìm kiếm trả về các row vô lý sau khi đổi model nhưng không có lỗi ở đâu. Độ rộng 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 và add lại dữ liệu.
Connection refused trong log 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ỉ đó là chính container. Dùng tên service 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 và kiểm tra xem memory đã được ghi hay 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 lên registry.
error: port 3000 is already in use xuất phát từ target make up của repo. Target này không khởi động được khi cổng 3000 hoặc 8888 đã bị chiếm. Tìm tiến trình đang dùng 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 khóa cấu hình graph_store và enable_graph khỏi SDK mã nguồn mở. Việc trích xuất entity giờ chạy trong lúc add thông thường và ghi vào một collection pgvector thứ hai có tên <collection_name>_entities, nên không cần database graph 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 giờ chỉ tăng ranking của memory thay vì cung cấp các edge để traverse. Vì vậy, ứng dụng từng traverse các relationship đó phải dùng graph store riêng bên ngoài mem0.
VPS nhỏ nhất có thể chạy server mem0 tự host là bao nhiêu?
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 cần nhiều tài nguyên nhất là lần build đầu tiên, vì compile 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 theo model: model 8B với quantisation 4-bit cần khoảng 6 GB riêng cho model, nên dự trù 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ỉ đóng gói 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" với bất kỳ api_key không rỗng nào cho cả llm và embedder. Ollama bỏ qua key này, còn bước kiểm tra provider vẫn pass vì provider thực sự là openai.
Vì sao mem0 không trả về kết quả sau khi tôi chuyển sang local embedding model?
Vì bảng pgvector được tạo với chiều cố định. embedding_model_dims mặc định là 1536, nomic-embed-text trả về 768, nên Postgres từ chối insert với expected 1536 dimensions, not 768. mem0 tạo bảng bằng CREATE TABLE IF NOT EXISTS, vì vậy chỉ thay đổi con số không có tác dụng với collection hiện có. Đặt embedding_model_dims theo đúng chiều thực của model, xác nhận chiều đó bằng cách gọi /v1/embeddings rồi đếm số giá trị được trả về, đồng thời tạo collection_name mới cho vector store.