SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-28

VPS-এ agent memory-এর জন্য mem0 self-host করুন

নিজের VPS-এ mem0 memory server চালানোর বাস্তব RAM মাপ, localhost bind করা Compose file, API-র সামনে TLS এবং Ollama-সহ সম্পূর্ণ local setup দেখুন।

VPS-এ mem0 self-hosting-এর প্রকৃত RAM খরচ

mem0 self-hosting বলতে তিনটি container চালানো বোঝায়: FastAPI memory server, pgvector extension-সহ Postgres এবং Next.js dashboard। mem0 agent-দের জন্য একটি memory layer। আপনি এতে একটি conversation পাঠান, language model সেই conversation থেকে স্থায়ী তথ্য বের করে, এবং সেই তথ্য vector হিসেবে সংরক্ষিত হয়, যাতে পরবর্তী query প্রাসঙ্গিক তথ্যগুলো আবার খুঁজে আনতে পারে।

তিনটি container-এর জন্য মোটামুটি 1 GB resident memory এবং image build হওয়ার পরে 3 থেকে 4 GB disk বরাদ্দ রাখুন। Language model অন্য কোথাও চললে 2 GB VPS-এ এটি স্বাচ্ছন্দ্যে চালানো যায়। একই machine-এ Ollama-এর মাধ্যমে model চালালে বাকি সবকিছুর তুলনায় model-ই সবচেয়ে বেশি resource ব্যবহার করে: 4 bits-এ quantised একটি 8B model-এর জন্য একাই প্রায় 6 GB প্রয়োজন হয়। তাই সম্পূর্ণ local setup-এর শুরুতেই 8 GB দরকার।

এই সংখ্যাগুলো কোনো blog post থেকে নেবেন না, এটিও নয়। আপনি যে stack তৈরি করেছেন, সেটিই মাপুন।

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

docker stats প্রতিটি container-এর resident memory দেখায়। docker system df -v প্রতিটি image এবং volume কত disk ব্যবহার করছে তা দেখায়।

Steady state সর্বোচ্চ ব্যবহার নয়। docker compose up -d --build Next.js dashboard compile করে, এবং পুরো installation-এর মধ্যে সেই Node build-এই সবচেয়ে বেশি memory লাগে। 1 GB VPS-এ kernel-এর out-of-memory killer এটি বন্ধ করে দেয়, এবং build exit code 137 দিয়ে শেষ হয়। Docker-এর bug খোঁজার আগে কারণটি নিশ্চিত করুন:

dmesg -T | grep -i "killed process"

আপনার প্রয়োজনের তুলনায় কোনো server বেশি জটিল মনে হলে ছোট বিকল্পগুলো ব্যবহারযোগ্য। কোনো server ছাড়াই local agent memory store এবং Claude Code-এর ভেতরেই থাকা memory—উভয়ই database ব্যবহার করে না। একাধিক agent বা একাধিক machine-এর একই memory পড়ার প্রয়োজন হলে এখানে ফিরে আসুন।

mem0 graph memory-এর জন্য কি Neo4j দরকার?

না। কোনো guide-এ যদি Neo4j container যোগ করতে বলা হয়, তাহলে সেই guide-টি বর্তমান code-এর চেয়ে পুরোনো।

mem0-এ graph memory বলতে আগে একটি external graph database বোঝাত। এটি graph_store key-এর অধীনে configure করা হতো এবং enable_graph-এর মান true সেট করতে হতো। April 2026-এ released নতুন memory algorithm open source SDK থেকে উভয় key সরিয়ে দিয়েছে। Entity extraction এখন সাধারণ add path-এর ভেতরেই চলে, এবং entity-গুলো আপনার প্রধান collection-এর নামে, তার শেষে _entities যোগ করে তৈরি করা দ্বিতীয় pgvector collection-এ লেখা হয়। কোনো migration চালানোর প্রয়োজন নেই। Built-in entity linking পরবর্তী add call থেকেই কাজ শুরু করে।

Graph store বাদ দিলে একটি JVM container, তার heap এবং image-এর কয়েকশ megabyte সাশ্রয় হয়। 2 GB VPS-এ এটিই running থাকা এবং swapping শুরু হওয়ার মধ্যে পার্থক্য তৈরি করতে পারে।

আপনি কী হারাচ্ছেন, তা সরাসরি বলা যাক। আগে search result-এ relations field থাকত, যেখানে entity-গুলোর মধ্যকার edge তালিকাভুক্ত থাকত। সেই field আর নেই। এখন entity match combined score-এ কোনো memory-এর অবস্থান বাড়ায়, কিন্তু traverse করার মতো কোনো structure নেই। আপনার application যদি ওই relationship-গুলো ধরে এগোত, mem0 এখন সেগুলো সংরক্ষণ করে না। সেক্ষেত্রে mem0-এর বাইরে আপনার নিজস্ব graph database রাখতে হবে এবং নিজের code দিয়ে তাতে data দিতে হবে।

রিপোজিটরির compose ফাইলটি development-এর জন্য ব্যবহৃত

server/docker-compose.yaml-এ name: mem0-dev ঘোষণা করা হয়েছে, এবং সেটিই কার্যকর হয়। চালানোর আগে এটি পড়ে নিন, কারণ সার্ভারের জন্য এতে পাঁচটি সমস্যা আছে।

  • এটি server/dev.Dockerfile থেকে build করে এবং .:/app দিয়ে আপনার checkout-কে image-এর ওপর mount করে। ফলে container-এ আপনি যে build তৈরি করেছেন সেটি নয়, ওই directory-তে থাকা ফাইলই চলে।
  • এর command হলো rm -rf /app/packages && pip install -q --force-reinstall --no-deps mem0ai && alembic upgrade head && uvicorn main:app --reload। এটি প্রতিবার start-এর সময় PyPI থেকে mem0ai পুনরায় install করে। তাই upgrade করার কথা না থাকলেও restart-এর সময় server-এ চলা version বদলে যেতে পারে।
  • একই pip ধাপের কারণে outbound network ছাড়া restart করলে uvicorn চালু হওয়ার আগেই ব্যর্থ হয়। তখন PyPI-তে পৌঁছানো যায় না বলে আপনার memory server বন্ধ থাকে।
  • --reload uvicorn-এর file watcher চালু করে। কোড edit করলে process restart করার জন্য এটি ব্যবহৃত হয়। Production-এ এটি কোনো কার্যকর কাজ না করেই memory এবং অতিরিক্ত একটি process ব্যবহার করে। Production-এর Dockerfile-এও তার CMD-এর মধ্যে --reload রয়েছে, তাই উভয় ক্ষেত্রেই command override করতে হবে।
  • প্রকাশিত port-গুলো হলো "8888:8000", "8432:5432" এবং "3000:3000"। কোনো address উল্লেখ না করে port publish করলে সেটি 0.0.0.0-এ bind হয়। ফলে stack চালু হওয়ার সঙ্গে সঙ্গেই Postgres 8432 port-এ public Internet থেকে সংযোগ গ্রহণ করে।

শেষের বিষয়টির জন্য আলাদা সতর্কতা প্রয়োজন। Docker নিজস্ব rule লিখে ufw পরিচালিত chain-এর আগে সেগুলো প্রয়োগ করে। তাই ufw deny 8432 published container port বন্ধ করে না। Docker কীভাবে ufw নিয়ম পাশ কাটিয়ে port publish করে-এ সংশ্লিষ্ট নিয়মগুলো ব্যাখ্যা করা হয়েছে।

একটি বাস্তব সার্ভারের জন্য Compose file

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

এখানে পাঁচটি পরিবর্তন গুরুত্বপূর্ণ। প্রতিটির একটি নির্দিষ্ট কারণ আছে।

প্রতিটি ports entry 127.0.0.1 দিয়ে শুরু হয়। তাই kernel এই connection-গুলো শুধু একই host থেকে গ্রহণ করে। বাইরের সব traffic reverse proxy-এর মাধ্যমে আসে। Certificate শুধু reverse proxy-তেই থাকে।

Postgres-এর কোনো ports block নেই। mem0 container service name ব্যবহার করে mem0_network-এর মাধ্যমে এতে পৌঁছায়। তাই 8432 publish করলে কোনো সুবিধা হয় না, বরং একটি অতিরিক্ত open port তৈরি হয়। Shell প্রয়োজন হলে docker compose exec postgres psql -U postgres ব্যবহার করুন।

History এখন ./history bind mount-এর পরিবর্তে একটি named volume ব্যবহার করে। Bind mount data-কে এই host-এর একটি path এবং একটি uid-এর সঙ্গে যুক্ত করে। Named volume হলো এমন একটি object, যার snapshot নেওয়া এবং স্থানান্তর করা Docker-এর পক্ষে সম্ভব। কোন পরিস্থিতিতে কোনটি উপযুক্ত, তা Named volume ও bind mount-এর তুলনা-এ ব্যাখ্যা করা হয়েছে।

Command-টি --reload বাদ দেয় এবং alembic upgrade head রেখে দেয়। এই migration step রাখুন। এটি না থাকলে app এমন একটি database-এর বিরুদ্ধে চালু হবে, যেখানে কোনো table নেই। ফলে প্রথম query-তেই প্রতিটি request ব্যর্থ হবে।

NEXT_PUBLIC_API_URL হলো browser যে URL-এ request পাঠায়। তাই এটি public HTTPS address হতে হবে, http://mem0:8000 নয়। Next.js build time-এ প্রতিটি NEXT_PUBLIC_ value JavaScript-এর মধ্যে স্থায়ীভাবে যুক্ত করে। তাই এটি পরিবর্তন করতে docker compose up -d --build mem0-dashboard প্রয়োজন। শুধু restart করলে JavaScript-এর মধ্যে পুরোনো value থেকে যায় এবং dashboard ভুল host-এ call পাঠায়।

Secrets .env ফাইলে থাকে, আর .env 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

POSTGRES_PASSWORD, JWT_SECRET এবং ADMIN_API_KEY সেট করুন। AUTH_DISABLED=false অপরিবর্তিত রাখুন। ওই flag কী করে, নামটি তা স্পষ্টভাবেই জানায়: এটি চালু থাকলে, port-এ পৌঁছাতে পারে এমন যেকেউ server-এর ধারণ করা সব memory পেয়ে যায়। onboarding event upstream-এ পাঠাতে না চাইলে MEM0_TELEMETRY=false সেট করুন।

ADMIN_API_KEY-এর মান X-API-Key header-এর সঙ্গে secrets.compare_digest ব্যবহার করে তুলনা করা হয়, এবং মিল পাওয়া গেলে সব database lookup বাদ যায়। এটি পুরো API-এর জন্য একটি root credential। তাই এটিকে root credential হিসেবেই সুরক্ষিত রাখুন: shell history-তে নয়, git-এ নয়, এবং prompt-এ paste করবেন না। Compose env file এবং সেগুলো থেকে secret কীভাবে ফাঁস হয় এবং agent-এর context-এর বাইরে API key রাখা—দুটিই সরাসরি প্রযোজ্য, কারণ এই server-এর caller হলো agent।

env_file থেকে load করা value container environment-এ থাকে, আর docker inspect সেগুলো সম্পূর্ণভাবে দেখায়। docker group-এর যেকেউ সেগুলো পড়তে পারে, এবং docker group-এর যেকেউ host-এ কার্যত root।

API-এর সামনে TLS দিন, 8888 খুলবেন না

API 127.0.0.1:8888-এ এবং dashboard 127.0.0.1:3000-এ সাড়া দেয়। nginx 443 port-এ TLS (transport layer security) termination করে এবং উভয় service-এ অনুরোধ পাঠায়।

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 দেখতে যতটা গুরুত্বপূর্ণ মনে হয়, তার চেয়েও বেশি গুরুত্বপূর্ণ। Language model conversation পড়ে তথ্য বের করার সময় add call অপেক্ষা করে। CPU-তে চলা স্থানীয় 8B model-এর প্রক্রিয়া প্রায়ই nginx-এর 60 second default সময়সীমার চেয়ে বেশি সময় নেয়। তখন model কাজ চালিয়ে যাওয়া এবং memory লেখা সম্পন্ন হওয়া সত্ত্বেও caller 504 Gateway Time-out দেখতে পায়। ফলে এমন একটি memory তৈরি হয়, যেটি ব্যর্থ হয়েছে বলে আপনাকে জানানো হয়েছিল।

বাকি সংযোগগুলো একটি default deny ufw policy দিয়ে বন্ধ করুন। শুধু 22 এবং 443 খোলা রাখুন। nginx-এর পেছনে Ubuntu 24.04-এ certbot ব্যবহার করে certificate জারি করুন। সার্ভারটি যদি আগে থেকেই Traefik দিয়ে একাধিক Compose app route করে থাকে, তাহলে দ্বিতীয় proxy ইনস্টল না করে সেই router-এ mem0 যোগ করুন।

একটি memory যোগ করে আবার পড়ার smoke test

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

সুস্থ response হলো একটি JSON object, যার মধ্যে results list থাকে। প্রতিটি entry-তে id, বের করা memory text এবং "event": "ADD" থাকে। বর্তমান algorithm শুধু ADD event ফেরত দেয়। UPDATE এবং DELETE event সরিয়ে দেওয়া হয়েছে, তাই এগুলো না থাকা কোনো bug নয়।

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 সম্পর্কে fact-টি একটি score-সহ ফেরত আসা উচিত। দেখানো নিয়মে identifier-টি filters-এর মধ্যে পাঠান। top-level user_id এখনও কাজ করে, এবং এটি ব্যবহার করলে server প্রতিবার Top-level user_id in /search is deprecated. Use filters={...} instead. log করে।

Test data যেন বাস্তব search-কে প্রভাবিত না করে, তাই কাজ শেষে তা পরিষ্কার করুন:

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

Search-এ প্রত্যাশার চেয়ে কম row এলে retrieval-কে দোষ দেওয়ার আগে default-গুলো পরীক্ষা করুন। বর্তমান release-এ top_k-এর default 100 থেকে কমে 20 হয়েছে, আর threshold-এর default none-এর বদলে 0.1। ফলে দুর্বল match-গুলো এখন স্বয়ংক্রিয়ভাবে বাদ পড়ে। curl-এর মাধ্যমে এটি কাজ করলে একই endpoint-গুলো agent-এ যুক্ত করবেন, সরাসরি অথবা একই VPS-এ চলা একটি MCP server-এর মাধ্যমে।

Ollama ব্যবহার করে কোনো OpenAI key ছাড়াই mem0 চালান

প্রথমেই বাধাটি দেখুন, কারণ প্রথম পাঁচ মিনিটেই এটি সামনে আসবে। Server image-এ provider library-গুলোর একটি নির্দিষ্ট সেট থাকে, এবং /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.

কিছুই rebuild করতে হবে না। Ollama /v1-এ একটি OpenAI-compatible API দেয়, যা /v1/chat/completions এবং /v1/embeddings সমর্থন করে, এবং mem0-এর openai provider একটি openai_base_url গ্রহণ করে। সেই key-কে Ollama-এর দিকে নির্দেশ করুন। তাহলে bundled check পাস করবে, কারণ provider সত্যিই openai। শুধু address পরিবর্তন হয়।

একই Compose project-এ Ollama যোগ করুন:

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

top-level volumes: key-এর অধীনে ollama_models: যোগ করুন। এরপর একটি chat model এবং একটি embedding model pull করুন:

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

Ollama যদি host-এ systemd unit হিসেবে আগে থেকেই চলে, যেমন VPS-এ Ollama সরাসরি চালানো, তাহলে container-কে 127.0.0.1:11434-এর দিকে নির্দেশ করবেন না। mem0 container-এর ভিতরে 127.0.0.1 হলো mem0 container নিজেই। mem0 service-কে extra_hosts: ["host.docker.internal:host-gateway"] দিন। একটি systemd drop-in-এ Environment="OLLAMA_HOST=0.0.0.0:11434" সেট করুন, যাতে Ollama এমন একটি address-এ listen করে যেটি bridge থেকে পৌঁছানো যায়। Firewall-এ 11434 বন্ধ রাখুন।

কিছু configure করার আগে model-কে তার embedding dimension জিজ্ঞাসা করুন

এই একটি ধাপেই retrieval আদৌ কাজ করবে কি না তা নির্ধারিত হয়।

mem0-এর pgvector store একটি নির্দিষ্ট vector width, vector vector(1536), দিয়ে table তৈরি করে, কারণ embedding_model_dims-এর default হলো 1536, যা OpenAI-এর text-embedding-3-small-এর width। nomic-embed-text 768টি value ফেরত দেয়। mem0-এর ভিতরে এই দুটি সংখ্যার তুলনা হয় না। তাই প্রথম insert-এ Postgres থেকে mismatch-এর error আসে:

expected 1536 dimensions, not 768

এই paragraph-এর সংখ্যাটিও যাচাই না করে বিশ্বাস করবেন না। 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']))"

এতে আপনার collection-এ যে width ব্যবহার করতে হবে তা দেখাবে। Configuration একটি file-এ লিখুন। Shell quoting-এর মাধ্যমে Postgres password paste করলেই production-এ typo ঢোকার ঝুঁকি থাকে।

{
  "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-টি configuration আবার পড়ে। এতে write সঠিকভাবে হয়েছে কি না যাচাই হয়। এরপর উপরের smoke test আবার চালান।

এই JSON-এ চারটি বিষয় সরাসরি বোঝা যায় না। প্রতিটি ভুল হলে ভিন্ন সমস্যা হবে।

api_key হলো ollama string, এবং Ollama এর value উপেক্ষা করে। এটি empty হতে পারবে না। কোনো key সেট না থাকলে request process ছাড়ার আগেই OpenAI client library error দেয়। যেকোনো non-empty string কাজ করবে।

embedding_model_dims vector store-এ দিতে হবে। Embedder-এ ইচ্ছাকৃতভাবে কোনো embedding_dims নেই। আপনি embedding_dims সেট করলেই mem0 OpenAI-এর dimensions parameter পাঠায়। Matryoshka truncation বাস্তবায়ন না করা backend-গুলো এই parameter সরাসরি reject করে। Table তৈরির সময় width সেট করুন এবং embedder অপরিবর্তিত রাখুন।

collection_name নতুন। mem0 CREATE TABLE IF NOT EXISTS দিয়ে table তৈরি করে। তাই কোনো existing collection-এ ভিন্ন width নির্দেশ করলে কিছুই পরিবর্তন হবে না। পুরোনো vector(1536) column থেকেই যাবে এবং প্রতিটি insert ব্যর্থ হবে। Width পরিবর্তন করতে হলে নতুন collection name ব্যবহার করুন, অথবা হাতে পুরোনো table drop করুন।

openai_base_url-এর host হলো Compose service name ollama, localhost নয়। Shared network-এ container-গুলো service name ব্যবহার করে একে অপরকে resolve করে।

সম্পূর্ণ local পদ্ধতির খরচ

Quality সম্পর্কে নিজের কাছে সৎ থাকুন। mem0-এর প্রকাশিত benchmark score-গুলো extraction-এর জন্য frontier model ব্যবহার করে মাপা হয়েছে। তাই 8B model-সহ VPS-এর জন্য এগুলোকে forecast নয়, ceiling হিসেবে ধরুন। ছোট model আরও অস্পষ্ট fact লেখে। কখনো JSON চাওয়া হলেও prose ফেরত দেয়। এর ফলে কোনো error ছাড়াই add call থেকে empty results list ফেরত আসতে পারে।

Speed হলো আরেকটি খরচ। শুধু CPU ব্যবহার করে extraction করতে প্রতিটি add call-এ কয়েক সেকেন্ড লাগে। আপনি যে প্রতিটি message store করেন, তার জন্য এই খরচ দিতে হয়। যে model-কে দেওয়া JSON-এর বাইরে দীর্ঘ উত্তর দিতে থাকে, সে সমস্যা আরও বাড়ায়। তাই num_predict দিয়ে reply-এর সীমা নির্ধারণ করলে একটি add call কতক্ষণ চলতে পারবে তার upper limit থাকে। এই latency গুরুত্বপূর্ণ হলে GPU-সহ একটি VPS হলো বাস্তবসম্মত সমাধান। 8B model-এ আরও CPU core যোগ করলে মানুষের প্রত্যাশার তুলনায় অনেক কম উন্নতি হয়। Machine পরিবর্তনের চেয়ে model পরিবর্তন সাশ্রয়ী পদক্ষেপ। VPS-এ Nemotron 3.5 Lightning কোন tag pull করতে হবে, কত RAM প্রয়োজন এবং CPU-only ব্যবহার সহনীয় গতিতে চলে কি না—এসব জানায়।

আপনি যেটিই বেছে নিন, একটি নিয়ম সব ক্ষেত্রেই প্রযোজ্য: একই collection-এর মধ্যে কখনো embedding model মেশাবেন না। একই width রয়েছে এমন দুটি ভিন্ন model থেকেও এমন vector তৈরি হয় যেগুলো পরস্পরের সঙ্গে তুলনাযোগ্য নয়। Insert সফল হয়, search row ফেরত দেয়, কিন্তু row-গুলো ভুল হয়। কোথাও কোনো error দেখায় না।

Backup: একটি নয়, দুটি database

সবচেয়ে সাধারণ mem0 backup ভুল হলো শুধু একটি database dump করা। init-db.sh ডিফল্ট postgres database-এর পাশাপাশি mem0_app তৈরি করে, এবং দুটিতে ভিন্ন ধরনের তথ্য থাকে। postgres database-এ pgvector collection থাকে, যেগুলোই memories। mem0_app-এ users, sessions, API keys এবং request logs থাকে। প্রতিটি self-hosted app নিজস্ব পদ্ধতিতে state ভাগ করে রাখে। তাই একই কাজ করা দুটি photo server-এরও আলাদা backup command প্রয়োজন হয়। কোনো dump-এর ওপর নির্ভর করার আগে আপনার app কী সংরক্ষণ করে তা পড়ে নিন। এই পরিসরের অন্য প্রান্তে রয়েছে 90s-এর video store হিসেবে পুনর্নির্মিত একটি Jellyfin library। এটি অন্য একটি service থেকে সম্পূর্ণ catalogue পড়ে। তাই সাধারণত শুধু নিজের configuration কপি করলেই চলে। কিন্তু mem0-এর ক্ষেত্রে উভয় database প্রয়োজন। তা না হলে restore কার্যকর হবে না।

শুধু postgres restore করলে memory ফিরে আসবে, কিন্তু সব account এবং API key হারিয়ে যাবে। ফলে কোনো কিছুই authenticate করে সেগুলো পড়তে পারবে না। এক command-এ উভয় database এবং role dump করুন:

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

history volume Postgres থেকে আলাদা এবং এর নিজস্ব copy প্রয়োজন:

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

Docker project name দিয়ে volume name-এর শুরুতে prefix যোগ করে। তাই mem0_mem0_history ধরে নেওয়ার আগে docker volume ls দিয়ে আপনার volume name নিশ্চিত করুন।

কোনো backup-কে নির্ভরযোগ্য মনে করার আগে সেটি একটি অস্থায়ী container-এ restore করে row count পরীক্ষা করুন:

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

যে backup কখনো restore করে পরীক্ষা করা হয়নি, সেটি কেবল অনুমান। dump সঠিক হলে off-site storage-এ restic snapshot পাঠান। যে server-কে সুরক্ষিত করার কথা, সেই server-এ থাকা backup কোনো সুরক্ষা দেয় না।

ব্যর্থতার ধরন এবং যে সঠিক string আপনি দেখতে পাবেন

{"detail":"Authentication required. Provide a Bearer token or X-API-Key header."} মানে header অনুপস্থিত অথবা ভুল বানানে লেখা হয়েছে। নামটি X-API-Key, এবং curl header-এর নাম হুবহু পাঠায়।

কোনো add operation-এ {"detail":"At least one identifier (user_id, agent_id, run_id) is required."} দেখা গেলে বুঝবেন, request-এ সেগুলোর একটিও ছিল না। একটি memory-কে কোনো কিছুর scope-এর মধ্যে রাখতে হয়, কারণ search ঠিক ওই field-গুলোর ওপর filter প্রয়োগ করে।

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 এলে বুঝবেন collection এক width-এ তৈরি হয়েছিল, কিন্তু embedder অন্য width ফেরত দিচ্ছে। vector store-এ embedding_model_dims সেট করুন এবং নতুন collection_name ব্যবহার করুন।

model পরিবর্তনের পরে, কোথাও কোনো error না থাকলেও Search returns rows that make no sense। width একই থাকায় database এটিকে গ্রহণ করছে, কিন্তু দুটি model একই sentence-কে ভিন্ন position-এ স্থাপন করে। নতুন collection শুরু করে memory আবার যোগ করুন।

Ollama-তে সংযোগ করার সময় mem0 log-এ Connection refused দেখা গেলে সাধারণত openai_base_url-এ 127.0.0.1 বোঝায়। container-এর ভেতরে ওই address-টি container-কেই নির্দেশ করে। service name ব্যবহার করুন, অথবা Ollama host machine-এ চললে host gateway ব্যবহার করুন।

কোনো add operation-এ nginx থেকে 504 Gateway Time-out এলে বুঝবেন model-এর কাজ proxy_read_timeout-এর বেশি সময় নিয়েছে। timeout বাড়ান। request আবার পাঠানোর আগে memory লেখা হয়েছিল কি না পরীক্ষা করুন।

docker compose up --build চলাকালে exit code 137 হলো out-of-memory killer dashboard build বন্ধ করে দিয়েছে। swap যোগ করুন, অথবা বড় machine-এ image build করে registry-তে push করুন।

error: port 3000 is already in use repo-র make up target থেকে আসে। 3000 অথবা 8888 port ব্যবহৃত থাকলে এটি start হতে অস্বীকৃতি জানায়। owner খুঁজতে lsof -iTCP:3000 -sTCP:LISTEN ব্যবহার করুন।

FAQ

graph memory দিয়ে mem0 চালাতে কি এখনও Neo4j দরকার?

না। April 2026-এ প্রকাশিত নতুন memory algorithm open source SDK থেকে graph_store এবং enable_graph configuration key সরিয়ে দিয়েছে। এখন সাধারণ add operation চলাকালীন entity extraction হয় এবং <collection_name>_entities নামের দ্বিতীয় pgvector collection-এ ডেটা লেখা হয়। তাই external graph database, অতিরিক্ত container বা migration step-এর প্রয়োজন নেই। এর বিনিময়ে search result-এ relations field আর থাকে না। এখন entity memory-র ranking বাড়ায়, traverse করার জন্য edge দেয় না। তাই কোনো application যদি ওই relationship ধরে কাজ করত, তাহলে mem0-এর বাইরে নিজস্ব graph store রাখতে হবে।

self-hosted mem0 server চালানোর জন্য সবচেয়ে ছোট VPS কতটা হওয়া দরকার?

Language model অন্যত্র host করা থাকলে API container, Postgres এবং dashboard চালানোর জন্য 2 GB RAM এবং প্রায় 4 GB free disk যথেষ্ট। বেশি resource দরকার হয় প্রথম build-এর সময়। কারণ Next.js dashboard compile করতে এটি চালানোর চেয়ে বেশি memory ব্যবহার করে। 1 GB-এর server-এ build process exit code 137-সহ বন্ধ হয়ে যায়। Ollama একই server-এ চালালে model অনুযায়ী sizing করুন। 4-bit quantisation-এ 8B model-এর জন্য একাই প্রায় 6 GB দরকার হয়। তাই 8 GB RAM পরিকল্পনা করুন।

OpenAI API key ছাড়া কি mem0 চালানো যায়?

হ্যাঁ, Ollama-এর OpenAI-compatible endpoint ব্যবহার করে চালানো যায়। "provider": "ollama" সেট করলে কাজ হবে না, কারণ server image-এ শুধু openai, anthropic এবং gemini library bundled থাকে এবং HTTP 400 ফেরত দেয়। এর পরিবর্তে "provider": "openai" অপরিবর্তিত রাখুন এবং llm ও embedder—উভয়ের জন্য যেকোনো non-empty api_key সহ "openai_base_url": "http://ollama:11434/v1" সেট করুন। Ollama key-টি উপেক্ষা করে। Bundled provider check-ও পাস করে, কারণ provider সত্যিই openai।

local embedding model-এ পরিবর্তন করার পরে mem0 কোনো result দেয় না কেন?

কারণ pgvector table-টি নির্দিষ্ট width দিয়ে তৈরি হয়েছিল। embedding_model_dims-এর default মান 1536, কিন্তু nomic-embed-text 768 ফেরত দেয়। ফলে Postgres expected 1536 dimensions, not 768-সহ insert প্রত্যাখ্যান করে। mem0 CREATE TABLE IF NOT EXISTS ব্যবহার করে table তৈরি করে। তাই শুধু সংখ্যা পরিবর্তন করলে existing collection-এ কোনো প্রভাব পড়ে না। আপনার model-এর প্রকৃত width অনুযায়ী embedding_model_dims সেট করুন। /v1/embeddings call করে এবং সেটি ফেরত দেওয়া value গুনে width নিশ্চিত করুন। একই সময়ে vector store-এর জন্য নতুন collection_name দিন।