SSD Nodes Learn 🎉 VPS da $4.99/mese
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-07

Self-hosting di mem0 su VPS: RAM e setup locale

Scopri quanta RAM serve davvero per mem0 su VPS: circa 1 GB senza modello locale, almeno 8 GB con Ollama, più Compose, localhost e TLS.

Quanto costa realmente in RAM il self-hosting di mem0 su un VPS

Il self-hosting di mem0 richiede l'esecuzione di tre container: il server FastAPI per la memoria, Postgres con l'estensione pgvector e una dashboard Next.js. mem0 è un livello di memoria per gli agenti. Si invia una conversazione al servizio, un modello linguistico estrae da quella conversazione i fatti persistenti e tali fatti vengono memorizzati come vettori, in modo che una query successiva possa recuperare quelli pertinenti.

Prevedi circa 1 GB di memoria residente per i tre container e da 3 a 4 GB di spazio su disco dopo la creazione delle immagini. Un VPS da 2 GB esegue questa configurazione senza problemi quando il modello linguistico si trova altrove. Se il modello viene eseguito sullo stesso server tramite Ollama, il modello richiede molte più risorse degli altri componenti: un modello 8B quantizzato a 4 bit richiede circa 6 GB da solo, quindi una configurazione completamente locale parte da 8 GB.

Non prendere questi valori da un post di un blog, incluso questo. Misura lo stack che hai effettivamente creato.

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

docker stats stampa la memoria residente per container. docker system df -v stampa lo spazio su disco occupato da ogni immagine e da ogni volume.

L'utilizzo a regime non corrisponde al picco. docker compose up -d --build compila la dashboard Next.js e la compilazione Node è il momento con il maggiore consumo di memoria durante l'intera installazione. Su un VPS da 1 GB, il kernel interrompe il processo tramite l'out-of-memory killer e la compilazione termina con exit code 137. Conferma la causa prima di cercare un bug in Docker:

dmesg -T | grep -i "killed process"

Se un server ti sembra un'infrastruttura eccessiva rispetto a ciò che ti serve, esistono alternative più semplici. un archivio locale della memoria degli agenti senza alcun server e una memoria che risiede direttamente in Claude Code evitano entrambi il database. Torna a questa soluzione quando più agenti o più macchine devono leggere le stesse memorie.

È necessario Neo4j per la memoria a grafo di mem0?

No. Se una guida indica di aggiungere un container Neo4j, quella guida è precedente al codice attuale.

In passato, la memoria a grafo in mem0 indicava un database a grafo esterno, configurato con una chiave graph_store e con enable_graph impostato su true. Il nuovo algoritmo di memoria, rilasciato ad aprile 2026, ha rimosso entrambe le chiavi dall'SDK open source. L'estrazione delle entità ora viene eseguita durante il normale percorso di aggiunta e le entità vengono scritte in una seconda raccolta pgvector, denominata come quella principale con l'aggiunta di _entities. Non è necessaria alcuna migrazione. Il collegamento integrato delle entità diventa operativo alla chiamata di aggiunta successiva.

Rimuovere l'archivio a grafo consente di evitare un container JVM, il relativo heap e diverse centinaia di megabyte di immagine. Su un VPS da 2 GB, può fare la differenza tra un sistema operativo e uno che usa lo swap.

Ecco cosa si perde, in modo diretto. In precedenza, i risultati di ricerca includevano un campo relations che elencava gli archi tra le entità. Questo campo non esiste più. Le corrispondenze tra entità ora aumentano la posizione di una memoria nel punteggio combinato e non esiste più una struttura attraversabile. Se l'applicazione percorreva queste relazioni, mem0 non le conserva più. È quindi necessario mantenere un database a grafo autonomo all'esterno di mem0 e alimentarlo con codice proprietario.

Il file Compose nel repository è una configurazione Compose per lo sviluppo

server/docker-compose.yaml dichiara name: mem0-dev e lo fa realmente. Leggilo prima di eseguirlo, perché contiene cinque impostazioni errate per un server.

  • Esegue la build a partire da server/dev.Dockerfile e monta il checkout sopra l'immagine con .:/app. Il container esegue quindi il contenuto presente in quella directory, non quello incluso nell'immagine compilata.
  • Il comando è rm -rf /app/packages && pip install -q --force-reinstall --no-deps mem0ai && alembic upgrade head && uvicorn main:app --reload. A ogni avvio reinstalla mem0ai da PyPI. La versione eseguita dal server può quindi cambiare durante un riavvio che non consideravi un aggiornamento.
  • Lo stesso passaggio con pip fa fallire il riavvio senza rete in uscita prima ancora dell'avvio di uvicorn. Il server della memoria resta quindi inattivo perché PyPI non era raggiungibile.
  • --reload avvia il rilevatore di modifiche ai file di uvicorn. Serve a riavviare il processo quando modifichi il codice, ma in produzione consuma memoria e avvia un secondo processo senza svolgere alcuna funzione utile. Il Dockerfile di produzione include già --reload nel proprio CMD, quindi devi sovrascrivere il comando in entrambi i casi.
  • Le porte pubblicate sono "8888:8000", "8432:5432" e "3000:3000". Una porta pubblicata senza un indirizzo davanti viene associata a 0.0.0.0. Di conseguenza, Postgres è raggiungibile da Internet sulla porta 8432 non appena lo stack viene avviato.

Quest'ultimo punto richiede un avviso separato. Docker pubblica una porta inserendo le proprie regole prima della catena gestita da ufw. Per questo ufw deny 8432 non chiude una porta pubblicata dal container. La pubblicazione delle porte Docker aggira direttamente ufw illustra le regole coinvolte.

Un file Compose per un server reale

Lavorare all'interno di server/, mantenere init-db.sh nella posizione attuale e sostituire docker-compose.yaml con questo contenuto.

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

Qui contano cinque modifiche, ognuna con una motivazione.

Ogni voce ports inizia con 127.0.0.1, quindi il kernel accetta queste connessioni soltanto dal server stesso. Tutto il traffico esterno arriva tramite il reverse proxy, che è l'unico componente che gestisce un certificato.

Postgres non ha alcun blocco ports. Il container mem0 lo raggiunge tramite mem0_network usando il nome del servizio, quindi pubblicare la porta 8432 non offre alcun vantaggio e aggiunge una porta esposta. Usare docker compose exec postgres psql -U postgres quando serve una shell.

La cronologia passa dal bind mount ./history a un volume denominato. Un bind mount lega i dati a un percorso e a uno uid specifici su questo host, mentre un volume denominato è un oggetto che Docker può sottoporre a snapshot e spostare. Volumi denominati e bind mount spiega quando usare ciascuna soluzione.

Il comando rimuove --reload e mantiene alembic upgrade head. Mantenere questo passaggio della migrazione. Senza di esso, l'applicazione si avvia con un database privo di tabelle e ogni richiesta fallisce alla prima query.

NEXT_PUBLIC_API_URL è l'URL chiamato dal browser, quindi deve essere l'indirizzo HTTPS pubblico e non http://mem0:8000. Next.js incorpora ogni valore NEXT_PUBLIC_ al momento della build, quindi per modificarlo serve docker compose up -d --build mem0-dashboard. Un semplice riavvio mantiene il vecchio valore incorporato nel codice JavaScript e la dashboard chiama l'host errato.

I secret sono in .env, che non deve essere esposto a 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

Imposta POSTGRES_PASSWORD, JWT_SECRET e ADMIN_API_KEY. Lascia AUTH_DISABLED=false. Il nome descrive con precisione il comportamento del flag: quando è attivo, il server consegna tutta la memoria che contiene a chiunque possa raggiungere la porta. Imposta MEM0_TELEMETRY=false se non vuoi che l'evento di onboarding venga inviato a monte.

ADMIN_API_KEY viene confrontato con l'header X-API-Key tramite secrets.compare_digest e, in caso di corrispondenza, vengono saltate tutte le query al database. È una credenziale root per l'intera API. Trattala come tale: niente cronologia della shell, niente git e niente incolla in un prompt. Si applicano direttamente anche File env di Compose e dove possono fuoriuscire i secret e come tenere le chiavi API fuori dal contesto di un agent, perché i client di questo server sono agent.

I valori caricati da env_file si trovano nell'ambiente del container e docker inspect li stampa per intero. Chiunque appartenga al gruppo docker può leggerli, mentre chiunque appartenga al gruppo docker dispone di fatto dei privilegi root sull'host.

Metti TLS davanti all'API invece di esporre la porta 8888

L'API risponde su 127.0.0.1:8888 e la dashboard su 127.0.0.1:3000. nginx termina TLS (transport layer security) sulla porta 443 e inoltra le richieste a entrambi.

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 è più importante di quanto sembri. Una chiamata add resta bloccata mentre il language model legge la conversazione ed estrae i fatti. Un modello locale da 8B eseguito sulla CPU impiega regolarmente più del timeout predefinito di 60 secondi di nginx. Il chiamante riceve quindi 504 Gateway Time-out mentre il modello sta ancora elaborando e la memoria viene comunque scritta. Di conseguenza, ti ritrovi con una memoria che il sistema ha segnalato come non riuscita.

Chiudi il resto con una policy ufw deny predefinita, lasciando aperte le porte 22 e 443. Genera il certificato con certbot su Ubuntu 24.04 dietro nginx. Se il server espone già altre applicazioni tramite Traefik per il routing di più applicazioni Compose, aggiungi mem0 a quel router invece di installare un secondo proxy.

Test smoke: aggiunta e rilettura di una memoria

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

Una risposta corretta è un oggetto JSON con un elenco results. Ogni elemento contiene un id, il testo memory estratto e "event": "ADD". L'algoritmo attuale restituisce solo eventi ADD. Gli eventi UPDATE e DELETE sono stati rimossi, quindi la loro assenza non indica un problema.

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

Il fatto su Postgres 17 dovrebbe essere restituito con un punteggio. Passa l'identificatore all'interno di filters, come mostrato. È ancora possibile usare un user_id di primo livello e il server registra Top-level user_id in /search is deprecated. Use filters={...} instead. ogni volta che lo usi.

Pulisci l'ambiente al termine, in modo che i dati di test non influenzino le ricerche reali:

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

Se la ricerca restituisce meno righe del previsto, controlla i valori predefiniti prima di attribuire il problema al recupero. Nella release attuale top_k ha il valore predefinito 20, invece di 100, mentre threshold ha il valore predefinito 0.1 anziché nessun limite; di conseguenza, le corrispondenze deboli vengono ora filtrate automaticamente. Dopo aver verificato il funzionamento con curl, questi stessi endpoint sono quelli da collegare a un agent, direttamente oppure tramite un server MCP in esecuzione sullo stesso VPS.

Eseguire mem0 senza alcuna chiave OpenAI

Iniziate dal blocco principale, perché lo incontrerete nei primi cinque minuti. L'immagine del server include un insieme fisso di librerie dei provider e /configure rifiuta qualsiasi elemento che non appartenga a quell'insieme:

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.

Non è necessario ricompilare nulla. Ollama espone un'API compatibile con OpenAI su /v1, che supporta /v1/chat/completions e /v1/embeddings, mentre il provider openai di mem0 accetta un openai_base_url. Indicate Ollama con quella chiave e il controllo incluso va a buon fine, perché il provider è realmente openai. Cambia soltanto l'indirizzo.

Aggiungete Ollama allo stesso progetto Compose:

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

Aggiungete ollama_models: sotto la chiave di primo livello volumes:, quindi scaricate un modello di chat e un modello di embedding:

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

Se Ollama è già in esecuzione sull'host come unità systemd, come descritto in eseguire Ollama direttamente su un VPS, non indirizzate il container a 127.0.0.1:11434. All'interno del container mem0, 127.0.0.1 è il container mem0. Assegnate al servizio mem0 extra_hosts: ["host.docker.internal:host-gateway"], impostate Environment="OLLAMA_HOST=0.0.0.0:11434" in un drop-in di systemd in modo che Ollama sia in ascolto su un indirizzo raggiungibile dalla rete bridge e mantenete chiusa la porta 11434 nel firewall.

Chiedete al modello la dimensione degli embedding prima di configurare qualsiasi cosa

Questo passaggio determina se il recupero funzionerà.

Il vector store pgvector di mem0 crea la tabella con una dimensione fissa del vettore, vector vector(1536), perché embedding_model_dims ha come valore predefinito 1536, la dimensione di text-embedding-3-small di OpenAI. nomic-embed-text restituisce 768 valori. mem0 non confronta questi due numeri, quindi la discrepanza viene segnalata da Postgres al primo inserimento:

expected 1536 dimensions, not 768

Non fidatevi nemmeno del numero riportato in questo paragrafo. Chiedetelo al modello:

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']))"

Questo comando stampa la dimensione che la collection deve usare. Scrivete la configurazione in un file, perché inserire una password Postgres usando le virgolette della shell è un modo comune per portare errori di battitura in produzione.

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

La seconda chiamata rilegge la configurazione e verifica che la scrittura sia andata a buon fine. Ripetete quindi lo smoke test precedente.

Nel JSON ci sono quattro dettagli non ovvi. Se li impostate in modo errato, qualcosa si rompe.

api_key è la stringa ollama e Ollama ignora il suo valore. Non può essere vuota, perché la libreria client OpenAI genera un errore prima che qualsiasi richiesta lasci il processo quando non è impostata alcuna chiave. È sufficiente una stringa non vuota qualsiasi.

embedding_model_dims va impostato sul vector store e deliberatamente non esiste alcun embedding_dims sull'embedder. mem0 invia il parametro OpenAI dimensions solo quando impostate embedding_dims, mentre i backend che non implementano il troncamento Matryoshka rifiutano direttamente quel parametro. Impostate la dimensione al momento della creazione della tabella e lasciate invariato l'embedder.

collection_name è nuovo. mem0 crea la tabella con CREATE TABLE IF NOT EXISTS, quindi indicare una dimensione diversa per una collection esistente non produce alcun effetto: la vecchia colonna vector(1536) rimane e ogni inserimento fallisce. Per cambiare dimensione serve un nuovo nome di collection oppure è necessario eliminare manualmente la vecchia tabella.

L'host in openai_base_url è il nome del servizio Compose ollama, non localhost. I container si risolvono a vicenda tramite il nome del servizio sulla rete condivisa.

Il costo del percorso completamente locale

Siate realistici sulla qualità. I punteggi pubblicati nei benchmark di mem0 sono stati misurati con modelli di frontiera per l'estrazione. Considerateli quindi un limite superiore, non una previsione per un modello 8B su un VPS. Un modello più piccolo produce fatti più vaghi e talvolta restituisce testo discorsivo quando è richiesto JSON. Questo si manifesta come una chiamata add che restituisce un elenco results vuoto senza errori.

Anche la velocità ha un costo. L'estrazione eseguita soltanto sulla CPU richiede secondi per ogni chiamata add e ogni messaggio archiviato deve sostenere questo costo. Se la latenza è importante, un VPS con una GPU collegata è la soluzione corretta. Aggiungere core CPU a un modello 8B aiuta molto meno di quanto ci si aspetti.

Una regola vale in ogni caso: non mescolate mai modelli di embedding nella stessa collection. Due modelli diversi che per caso condividono la stessa dimensione producono vettori non confrontabili. L'inserimento riesce, la ricerca restituisce righe e le righe sono errate, senza che venga segnalato alcun errore.

Backup: ci sono due database, non uno

L’errore più comune nei backup di mem0 consiste nell’eseguire il dump di un solo database. init-db.sh crea mem0_app insieme al database predefinito postgres e i due database contengono dati diversi. Il database postgres contiene le raccolte pgvector, cioè i ricordi. mem0_app contiene utenti, sessioni, chiavi API e log delle richieste.

Se ripristini solo postgres, i ricordi tornano disponibili, ma tutti gli account e le chiavi API vengono persi. Di conseguenza, nessuno può autenticarsi per leggerli. Esegui il dump di entrambi i database e dei ruoli con un unico comando:

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

Il volume della cronologia è separato da Postgres e richiede una copia propria:

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

Docker antepone ai nomi dei volumi il nome del progetto. Verifica il nome effettivo con docker volume ls prima di dare per scontato mem0_mem0_history.

Ripristina i dati in un container temporaneo e controlla il numero di righe prima di considerarli verificati:

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

Un backup che non hai mai ripristinato è solo un’ipotesi. Dopo aver verificato i dump, trasferiscili fuori dal server con snapshot restic su storage esterno, perché un backup conservato sul server che dovrebbe proteggere non protegge nulla.

Modalità di errore e stringhe esatte visualizzate

{"detail":"Authentication required. Provide a Bearer token or X-API-Key header."} indica che l'header manca o contiene un errore di ortografia. Il nome è X-API-Key e curl invia i nomi degli header alla lettera.

{"detail":"At least one identifier (user_id, agent_id, run_id) is required."} durante un'operazione di aggiunta indica che la richiesta non ne conteneva alcuno. Una memoria deve essere associata a qualcosa, perché la ricerca applica i filtri esattamente a questi campi.

LLM provider 'ollama' is not bundled in this image con HTTP 400 indica che è stato inviato "provider": "ollama". Usa "provider": "openai" con openai_base_url configurato per puntare a Ollama.

expected 1536 dimensions, not 768 da Postgres indica che la collection è stata creata con una dimensione e l'embedder ne restituisce un'altra. Imposta embedding_model_dims sul vector store e usa un nuovo collection_name.

La ricerca restituisce righe prive di senso dopo una modifica del modello, senza errori visibili. La dimensione continua a corrispondere, quindi il database non rileva problemi, ma due modelli collocano la stessa frase in posizioni diverse. Crea una nuova collection e aggiungi nuovamente i dati.

Connection refused nei log di mem0 durante la connessione a Ollama indica in genere 127.0.0.1 in openai_base_url. All'interno del container, quell'indirizzo identifica il container stesso. Usa il nome del servizio oppure il gateway dell'host quando Ollama viene eseguito sull'host.

504 Gateway Time-out da nginx durante un'operazione di aggiunta indica che il modello ha impiegato più di proxy_read_timeout. Aumenta questo valore e verifica se la memoria è stata comunque scritta prima di ripetere la richiesta.

exit code 137 durante docker compose up --build indica che l'out-of-memory killer ha interrotto la build della dashboard. Aggiungi swap oppure crea l'immagine su una macchina più grande e inviala a un registry.

error: port 3000 is already in use deriva dal target make up del repository, che rifiuta di avviarsi quando le porte 3000 o 8888 sono già occupate. Trova il processo che le usa con lsof -iTCP:3000 -sTCP:LISTEN.

FAQ

Devo ancora usare Neo4j per eseguire mem0 con la memoria a grafo?

No. Il nuovo algoritmo di memoria, rilasciato ad aprile 2026, ha rimosso le chiavi di configurazione graph_store e enable_graph dall'SDK open source. L'estrazione delle entità ora viene eseguita durante una normale operazione di add e scrive in una seconda raccolta pgvector denominata <collection_name>_entities, quindi non sono necessari un database a grafo esterno, un container aggiuntivo o una procedura di migrazione. Il compromesso è che il campo relations non è più presente nei risultati di ricerca. Le entità ora aumentano il ranking di una memoria invece di fornire archi da attraversare; un'applicazione che percorreva queste relazioni deve quindi usare un proprio graph store esterno a mem0.

Qual è il VPS più piccolo su cui eseguire un server mem0 self-hosted?

Se il modello linguistico è ospitato altrove, 2 GB di RAM e circa 4 GB di spazio libero su disco sono sufficienti per il container API, Postgres e la dashboard. Il momento più impegnativo è la prima build, perché la compilazione della dashboard Next.js usa più memoria rispetto alla sua esecuzione e su una macchina da 1 GB il processo di build viene terminato con exit code 137. Se Ollama viene eseguito sullo stesso server, dimensiona il server in base al modello: un modello 8B con quantizzazione a 4 bit richiede da solo circa 6 GB, quindi pianifica 8 GB.

Posso eseguire mem0 senza una chiave API OpenAI?

Sì, tramite l'endpoint compatibile con OpenAI di Ollama. L'impostazione di "provider": "ollama" non funziona, perché l'immagine del server include solo le librerie openai, anthropic e gemini e restituisce HTTP 400. Mantieni invece "provider": "openai" e imposta "openai_base_url": "http://ollama:11434/v1" con un valore api_key non vuoto qualsiasi, sia per llm sia per embedder. Ollama ignora la chiave e il controllo del provider integrato viene superato perché il provider è effettivamente openai.

Perché mem0 non restituisce risultati dopo il passaggio a un modello di embedding locale?

Perché la tabella pgvector è stata creata con una dimensione fissa. embedding_model_dims ha come valore predefinito 1536, nomic-embed-text restituisce 768 e Postgres rifiuta l'inserimento con expected 1536 dimensions, not 768. mem0 crea la tabella usando CREATE TABLE IF NOT EXISTS, quindi modificare soltanto il numero non produce alcun effetto su una raccolta esistente. Imposta embedding_model_dims sulla dimensione effettiva del modello, verifica tale dimensione chiamando /v1/embeddings e contando i valori restituiti, quindi assegna contemporaneamente al vector store un nuovo collection_name.

#mem0#agent-memory#docker#self-hosting#ollama