Alojar mem0 en un VPS: RAM, Compose y TLS
Aloja mem0 en tu VPS con Compose y localhost: conoce el mínimo real de RAM, configura TLS ante la API y usa Ollama de forma totalmente local.
Cuánta RAM consume realmente alojar mem0 en un VPS
Alojar mem0 por cuenta propia implica ejecutar tres contenedores: el servidor de memoria FastAPI, Postgres con la extensión pgvector y un dashboard de Next.js. mem0 es una capa de memoria para agentes. Se le envía una conversación, un modelo de lenguaje extrae los datos persistentes de esa conversación y esos datos se almacenan como vectores para que una consulta posterior pueda recuperar los relevantes.
Reserve aproximadamente 1 GB de memoria residente para los tres contenedores y entre 3 y 4 GB de disco una vez creadas las imágenes. Un VPS de 2 GB ejecuta esta configuración sin problemas cuando el modelo de lenguaje está en otro lugar. Cuando el modelo se ejecuta en el mismo equipo mediante Ollama, el modelo consume mucho más que el resto: un modelo 8B cuantizado a 4 bits necesita aproximadamente 6 GB por sí solo, por lo que una instalación completamente local requiere al menos 8 GB.
No tome esas cifras de una publicación de blog, incluida esta. Mida la pila que realmente ha creado.
docker compose ps
docker stats --no-stream
docker system df -vdocker stats muestra la memoria residente por contenedor. docker system df -v muestra el espacio de disco que ocupa cada imagen y cada volumen.
El estado estable no representa el pico de consumo. docker compose up -d --build compila el dashboard de Next.js y esa compilación de Node es el momento de mayor consumo de toda la instalación. En un VPS de 1 GB, el asesino de falta de memoria del kernel detiene el proceso y la compilación termina con exit code 137. Confirme la causa antes de buscar un error de Docker:
dmesg -T | grep -i "killed process"Si un servidor le parece una infraestructura excesiva para sus necesidades, existen opciones más pequeñas. un almacén local de memoria para agentes sin ningún servidor y la memoria integrada en Claude Code evitan la base de datos. Vuelva aquí cuando varios agentes o varios equipos necesiten leer las mismas memorias.
¿Necesito Neo4j para la memoria de grafos de mem0?
No. Si una guía indica que debe añadir un contenedor de Neo4j, esa guía es anterior al código actual.
La memoria de grafos en mem0 antes significaba una base de datos de grafos externa, configurada mediante una clave graph_store con enable_graph establecido en true. El nuevo algoritmo de memoria, publicado en abril de 2026, eliminó ambas claves del SDK de código abierto. La extracción de entidades ahora se ejecuta dentro de la ruta normal de add, y las entidades se escriben en una segunda colección de pgvector cuyo nombre se forma a partir del de la colección principal con _entities añadido. No es necesario ejecutar ninguna migración. La vinculación de entidades integrada empieza a funcionar en la siguiente llamada a add.
Eliminar el almacén de grafos evita un contenedor JVM, su heap y varios cientos de megabytes de imagen. En un VPS de 2 GB, esa diferencia determina si el sistema funciona o empieza a usar swap.
Esto es lo que pierde, explicado de forma directa. Antes, los resultados de búsqueda incluían un campo relations con la lista de relaciones entre entidades. Ese campo ya no existe. Las coincidencias de entidades ahora aumentan la posición de una memoria en la puntuación combinada, y ya no hay una estructura que se pueda recorrer. Si su aplicación recorría esas relaciones, mem0 ya no las conserva. En ese caso, debe mantener su propia base de datos de grafos fuera de mem0 y alimentarla con su propio código.
El archivo de Compose del repositorio es un archivo de Compose para desarrollo
server/docker-compose.yaml declara name: mem0-dev y eso es exactamente lo que hace. Léalo antes de ejecutarlo, porque contiene cinco elementos incorrectos para un servidor.
- Compila desde
server/dev.Dockerfiley monta el checkout sobre la imagen con.:/app. Por tanto, el contenedor ejecuta lo que haya en ese directorio en lugar de lo que compiló. - Su comando es
rm -rf /app/packages && pip install -q --force-reinstall --no-deps mem0ai && alembic upgrade head && uvicorn main:app --reload. Esto reinstalamem0aidesde PyPI en cada inicio. Por tanto, la versión que ejecuta el servidor puede cambiar durante un reinicio que no pretendía que fuera una actualización. - El mismo paso de pip hace que un reinicio sin red saliente falle antes de que se ejecute uvicorn. En ese caso, el servidor de memoria queda detenido porque no se puede acceder a PyPI.
--reloadinicia el observador de archivos de uvicorn. Su función es reiniciar el proceso cuando edita el código. En producción consume memoria y mantiene un segundo proceso sin aportar utilidad. ElDockerfilede producción también incluye--reloaden suCMD, por lo que debe sobrescribir el comando en cualquier caso.- Los puertos publicados son
"8888:8000","8432:5432"y"3000:3000". Un puerto publicado sin una dirección delante se enlaza a0.0.0.0. Por tanto, Postgres queda accesible desde Internet en el puerto 8432 en cuanto se inicia la pila.
Este último punto requiere una advertencia específica. Docker publica un puerto escribiendo sus propias reglas delante de la cadena que administra ufw, por lo que ufw deny 8432 no cierra un puerto de un contenedor publicado. Publicación de puertos de Docker directamente fuera de ufw explica las reglas implicadas.
Un archivo de Compose para un servidor real
Trabaje dentro de server/, mantenga init-db.sh donde está y reemplace docker-compose.yaml con esto.
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: bridgeAquí importan cinco cambios, y cada uno tiene una razón.
Cada entrada ports comienza con 127.0.0.1, por lo que el kernel acepta esas conexiones sólo desde el propio servidor. Todo lo externo llega a través del reverse proxy, que es el único componente que contiene un certificado.
Postgres no tiene ningún bloque ports. El contenedor mem0 accede a él mediante mem0_network usando el nombre del servicio, por lo que publicar 8432 no aporta nada y deja un puerto abierto. Use docker compose exec postgres psql -U postgres cuando necesite un shell.
El historial pasa del bind mount ./history a un volumen con nombre. Un bind mount vincula los datos a una ruta y un uid concretos de este host, mientras que un volumen con nombre es un objeto que Docker puede capturar y mover. Volúmenes con nombre frente a bind mounts explica cuándo conviene usar cada uno.
El comando elimina --reload y conserva alembic upgrade head. Mantenga ese paso de migración. Sin él, la aplicación se inicia contra una base de datos sin tablas y cada petición falla en la primera consulta.
NEXT_PUBLIC_API_URL es la URL que utiliza el navegador, por lo que debe ser la dirección HTTPS pública y no http://mem0:8000. Next.js inserta cada valor de NEXT_PUBLIC_ durante la compilación, por lo que cambiarlo requiere docker compose up -d --build mem0-dashboard. Un simple reinicio conserva el valor anterior integrado en JavaScript y el dashboard realiza las llamadas al host incorrecto.
Los secretos se guardan en .env, y .env no debe exponerse 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 .envEstablezca POSTGRES_PASSWORD, JWT_SECRET y ADMIN_API_KEY. Deje AUTH_DISABLED=false sin establecer. El nombre describe exactamente lo que hace esa opción: cuando está activada, el servidor entrega toda la memoria que contiene a cualquiera que pueda acceder al puerto. Establezca MEM0_TELEMETRY=false si no quiere que el evento de incorporación se envíe al sistema ascendente.
ADMIN_API_KEY se compara con la cabecera X-API-Key mediante secrets.compare_digest, y una coincidencia omite todas las búsquedas en la base de datos. Es una credencial de root para toda la API. Trátela como tal: no la deje en el historial del shell, no la incluya en git ni la pegue en un prompt. Archivos de entorno de Compose y cómo se filtran los secretos y Cómo evitar que las claves de API lleguen al contexto de un agente se aplican directamente, porque los clientes de este servidor son agentes.
Los valores cargados desde env_file quedan en el entorno del contenedor, y docker inspect los muestra completos. Cualquiera que pertenezca al grupo docker puede leerlos, y cualquiera que pertenezca al grupo docker tiene, en la práctica, privilegios de root en el host.
Coloque TLS delante de la API en lugar de abrir 8888
La API responde en 127.0.0.1:8888 y el dashboard en 127.0.0.1:3000. nginx termina TLS (seguridad de la capa de transporte) en 443 y reenvía las peticiones a ambos servicios.
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 es más importante de lo que parece. Una llamada add se bloquea mientras el modelo de lenguaje lee la conversación y extrae los datos. Un modelo local 8B ejecutándose en la CPU tarda con frecuencia más que los 60 segundos predeterminados de nginx. Entonces el cliente recibe 504 Gateway Time-out mientras el modelo sigue trabajando y la memoria todavía se escribe. El resultado es una memoria que se informó como fallida, aunque se haya guardado.
Cierre el resto con una política ufw de denegación predeterminada, y deje abiertos 22 y 443. Emita el certificado con certbot en Ubuntu 24.04 detrás de nginx. Si el servidor ya expone otras aplicaciones mediante Traefik para enrutar varias aplicaciones de Compose, añada mem0 a ese router en lugar de instalar un segundo proxy.
Prueba básica: añadir una memoria y leerla de nuevo
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 respuesta correcta es un objeto JSON con una lista results. Cada entrada contiene un id, el texto memory extraído y "event": "ADD". El algoritmo actual sólo devuelve eventos ADD. Los eventos UPDATE y DELETE se eliminaron, por lo que su ausencia no indica un error.
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}'El dato sobre Postgres 17 debe devolverse con una puntuación. Pase el identificador dentro de filters, como se muestra. Un user_id de nivel superior sigue funcionando, y el servidor registra Top-level user_id in /search is deprecated. Use filters={...} instead. cada vez que lo utiliza.
Limpie los datos de prueba para que no contaminen las búsquedas reales:
curl -sS -X DELETE "http://127.0.0.1:8888/memories?user_id=smoke" \
-H "X-API-Key: $MEM0_KEY"Si la búsqueda devuelve menos filas de las esperadas, compruebe los valores predeterminados antes de atribuirlo al sistema de recuperación. En la versión actual, top_k tiene un valor predeterminado de 20, frente a 100, y threshold tiene un valor predeterminado de 0.1 en lugar de ninguno, por lo que los resultados débiles se filtran automáticamente. Cuando esto funcione mediante curl, esos mismos endpoints son los que debe conectar a un agente, ya sea directamente o mediante un servidor MCP que se ejecute en el mismo VPS.
Ejecutar mem0 sin ninguna clave de OpenAI
Empiece por el bloqueo, porque aparecerá durante los primeros cinco minutos. La imagen del servidor incluye un conjunto fijo de bibliotecas de proveedores, y /configure rechaza todo lo que no esté incluido en ellas:
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.No es necesario recompilar nada. Ollama ofrece una API compatible con OpenAI en /v1, compatible con /v1/chat/completions y /v1/embeddings, y el proveedor openai de mem0 acepta un openai_base_url. Apunte esa clave a Ollama y la comprobación incluida se supera, porque el proveedor realmente es openai. Sólo cambia la dirección.
Añada Ollama al mismo proyecto de Compose:
ollama:
image: ollama/ollama
restart: unless-stopped
networks: [mem0_network]
ports:
- "127.0.0.1:11434:11434"
volumes:
- ollama_models:/root/.ollamaAñada ollama_models: bajo la clave de nivel superior volumes: y descargue un modelo de chat y otro de embeddings:
docker compose up -d ollama
docker compose exec ollama ollama pull llama3.1:8b
docker compose exec ollama ollama pull nomic-embed-textSi Ollama ya se ejecuta en el host como una unidad de systemd, como se explica en ejecutar Ollama directamente en un VPS, no apunte el contenedor a 127.0.0.1:11434. Dentro del contenedor de mem0, 127.0.0.1 es el contenedor de mem0. Asigne al servicio de mem0 extra_hosts: ["host.docker.internal:host-gateway"], configure Environment="OLLAMA_HOST=0.0.0.0:11434" en un drop-in de systemd para que Ollama escuche en una dirección accesible desde el bridge y mantenga 11434 bloqueado en el firewall.
Consulte al modelo la dimensión de sus embeddings antes de configurar nada
Este paso determina si la recuperación funcionará.
El almacén pgvector de mem0 crea su tabla con un ancho de vector fijo, vector vector(1536), porque embedding_model_dims tiene 1536 por defecto, el ancho de text-embedding-3-small de OpenAI. nomic-embed-text devuelve 768 valores. mem0 no compara esos dos números, por lo que la incompatibilidad aparece en Postgres durante la primera inserción:
expected 1536 dimensions, not 768Tampoco confíe en el número de este párrafo. Consulte al modelo:
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']))"Esto muestra el ancho que debe usar la colección. Escriba la configuración en un archivo, porque pegar una contraseña de Postgres mediante comillas del shell es una forma de introducir errores tipográficos en producción.
{
"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 segunda llamada vuelve a leer la configuración. Esa es la comprobación de que la escritura se realizó correctamente. Después, repita la prueba de humo anterior.
Hay cuatro detalles de ese JSON que no son evidentes. Cada uno rompe algo si se configura incorrectamente.
api_key es la cadena ollama, y Ollama ignora su valor. No puede estar vacía, porque la biblioteca cliente de OpenAI genera un error antes de enviar ninguna petición cuando no se establece una clave. Cualquier cadena no vacía funciona.
embedding_model_dims se configura en el almacén vectorial, y deliberadamente no hay ningún embedding_dims en el embedder. mem0 sólo envía el parámetro dimensions de OpenAI cuando se establece embedding_dims, y los backends que no implementan el truncamiento Matryoshka rechazan directamente ese parámetro. Configure el ancho al crear la tabla y deje el embedder sin cambios.
collection_name es nuevo. mem0 crea su tabla con CREATE TABLE IF NOT EXISTS, por lo que apuntar un ancho diferente a una colección existente no cambia nada: la columna vector(1536) antigua permanece y todas las inserciones fallan. Un cambio de ancho requiere un nombre de colección nuevo o eliminar manualmente la tabla antigua.
El host de openai_base_url es el nombre del servicio de Compose ollama, no localhost. Los contenedores se resuelven entre sí mediante el nombre del servicio en su red compartida.
Coste de ejecutar todo de forma local
Sea realista sobre la calidad. Las puntuaciones de las pruebas de referencia publicadas por mem0 se midieron con modelos avanzados realizando la extracción. Considérelas un límite superior, no una previsión para un modelo 8B en su VPS. Un modelo pequeño genera hechos más vagos y, en ocasiones, devuelve texto cuando se solicitó JSON. Esto aparece como una llamada add que devuelve una lista results vacía sin ningún error.
La velocidad es el otro coste. La extracción usando sólo CPU tarda varios segundos por llamada add, y cada mensaje almacenado tiene ese coste. Un modelo que se extiende más allá del JSON solicitado empeora la situación. Por eso, limitar la respuesta con num_predict establece un límite sobre la duración de cada llamada add. Si esa latencia es importante, un VPS con una GPU conectada es la solución adecuada. Añadir más núcleos de CPU a un modelo 8B ayuda mucho menos de lo que suele esperarse. Cambiar el modelo es una opción más económica que cambiar la máquina, y Nemotron 3.5 Lightning en un VPS proporciona la etiqueta que debe descargar, la RAM que necesita y una indicación de si el uso exclusivo de CPU es suficientemente rápido.
Se aplica una regla independientemente de la opción elegida: no mezcle modelos de embeddings dentro de una misma colección. Dos modelos diferentes que casualmente tengan el mismo ancho producen vectores no comparables. La inserción se completa, la búsqueda devuelve filas y las filas son incorrectas, sin que ningún componente informe de un error.
Backups: hay dos bases de datos, no una
El error más común al hacer una copia de seguridad de mem0 es volcar una sola base de datos. init-db.sh crea mem0_app junto a la base de datos predeterminada postgres, y cada una contiene datos diferentes. La base de datos postgres contiene las colecciones de pgvector, que son las memorias. mem0_app contiene usuarios, sesiones, claves de API y registros de solicitudes. Cada aplicación autohospedada divide su estado de una forma distinta. Por eso, dos servidores de fotos que hacen lo mismo siguen necesitando comandos de copia de seguridad diferentes. Consulte qué datos almacena su aplicación antes de confiar en un volcado. En el otro extremo está algo como una biblioteca de Jellyfin reconstruida como un videoclub de los años 90, que obtiene todo su catálogo de otro servicio y, por tanto, sólo necesita copiar su propia configuración. mem0, en cambio, necesita ambas bases de datos; de lo contrario, la restauración no sirve.
Si restaura sólo postgres, las memorias volverán, pero se perderán todas las cuentas y claves de API. Nada podrá autenticarse para leerlas. Vuelque ambas bases de datos y los roles con un solo comando:
docker compose exec -T postgres pg_dumpall -U postgres --clean \
| gzip > "mem0-$(date +%F).sql.gz"El volumen del historial es independiente de Postgres y necesita su propia copia:
docker run --rm -v mem0_mem0_history:/data -v "$PWD:/backup" \
alpine tar czf /backup/mem0-history.tgz -C /data .Docker antepone el nombre del proyecto a los nombres de los volúmenes. Confirme el suyo con docker volume ls antes de dar por hecho que es mem0_mem0_history.
Restaure la copia en un contenedor temporal y compruebe el número de filas antes de considerarla válida:
gunzip -c mem0-2026-08-03.sql.gz \
| docker compose exec -T postgres psql -U postgres -d postgresUna copia de seguridad que nunca se ha restaurado es sólo una suposición. Cuando los volcados sean correctos, envíelos fuera del servidor con instantáneas de restic en almacenamiento externo, porque una copia de seguridad que permanece en el servidor que protege no protege nada.
Modos de fallo y las cadenas exactas que verá
{"detail":"Authentication required. Provide a Bearer token or X-API-Key header."} significa que falta la cabecera o que está mal escrita. El nombre es X-API-Key y curl envía los nombres de las cabeceras literalmente.
{"detail":"At least one identifier (user_id, agent_id, run_id) is required."} al añadir una memoria significa que la petición no incluía ninguna de esas cabeceras. Una memoria debe estar asociada a un ámbito, porque la búsqueda filtra exactamente por esos campos.
LLM provider 'ollama' is not bundled in this image con HTTP 400 significa que envió "provider": "ollama". Use "provider": "openai" con openai_base_url apuntado a Ollama.
expected 1536 dimensions, not 768 de Postgres significa que la colección se creó con una dimensión y que el embedder devuelve otra. Establezca embedding_model_dims en el almacén vectorial y use un collection_name nuevo.
La búsqueda devuelve filas que no tienen sentido después de cambiar de modelo, sin ningún error. La dimensión sigue coincidiendo, por lo que la base de datos funciona correctamente, pero dos modelos sitúan la misma frase en posiciones distintas. Cree una colección nueva y vuelva a añadir los datos.
Connection refused en los logs de mem0 al conectar con Ollama normalmente significa 127.0.0.1 en openai_base_url. Dentro del contenedor, esa dirección corresponde al propio contenedor. Use el nombre del servicio o la puerta de enlace del host cuando Ollama se ejecute en el host.
504 Gateway Time-out de nginx al añadir una memoria significa que el modelo tardó más de proxy_read_timeout. Auméntelo y compruebe si la memoria se escribió antes de volver a intentar la petición.
exit code 137 durante docker compose up --build es el terminador de procesos por falta de memoria, que detiene la compilación del dashboard. Añada swap o compile la imagen en una máquina con más recursos y súbala a un registro.
error: port 3000 is already in use procede del destino make up del repositorio, que no puede iniciarse cuando los puertos 3000 o 8888 están ocupados. Busque el proceso propietario con lsof -iTCP:3000 -sTCP:LISTEN.
FAQ
¿Sigo necesitando Neo4j para ejecutar mem0 con memoria de grafos?
No. El nuevo algoritmo de memoria, publicado en abril de 2026, eliminó las claves de configuración graph_store y enable_graph del SDK de código abierto. La extracción de entidades se ejecuta ahora durante una operación normal de adición y escribe en una segunda colección de pgvector llamada <collection_name>_entities, por lo que no se necesita una base de datos de grafos externa, un contenedor adicional ni un paso de migración. La contrapartida es que el campo relations ya no existe en los resultados de búsqueda. Las entidades ahora aumentan la clasificación de una memoria en lugar de proporcionar aristas que se puedan recorrer. Por tanto, una aplicación que recorría esas relaciones necesita su propio almacén de grafos fuera de mem0.
¿Cuál es el VPS más pequeño que puede ejecutar un servidor de mem0 autohospedado?
Si el modelo de lenguaje está alojado en otro lugar, 2 GB de RAM y unos 4 GB de espacio libre en disco bastan para el contenedor de la API, Postgres y el dashboard. El momento de mayor consumo es la primera compilación, porque compilar el dashboard de Next.js usa más memoria que ejecutarlo, y un equipo con 1 GB termina el proceso de compilación con exit code 137. Si Ollama se ejecuta en el mismo servidor, dimensione el sistema según el modelo: un modelo 8B con cuantización de 4 bits necesita aproximadamente 6 GB por sí solo, así que debe planificar 8 GB.
¿Puedo ejecutar mem0 sin una clave de API de OpenAI?
Sí, mediante el endpoint compatible con OpenAI de Ollama. Configurar "provider": "ollama" falla porque la imagen del servidor sólo incluye las bibliotecas openai, anthropic y gemini, y devuelve HTTP 400. En su lugar, conserve "provider": "openai" y configure "openai_base_url": "http://ollama:11434/v1" con cualquier api_key no vacío, tanto para el llm como para el embedder. Ollama ignora la clave y la comprobación del proveedor incluida funciona porque el proveedor realmente es openai.
¿Por qué mem0 no devuelve resultados después de cambiar a un modelo local de embeddings?
Porque la tabla de pgvector se creó con un ancho fijo. embedding_model_dims tiene el valor predeterminado 1536, nomic-embed-text devuelve 768 y Postgres rechaza la inserción con expected 1536 dimensions, not 768. mem0 crea la tabla con CREATE TABLE IF NOT EXISTS, por lo que cambiar sólo el número no modifica una colección existente. Configure embedding_model_dims con el ancho real de su modelo, confirme ese ancho llamando a /v1/embeddings y contando los valores que devuelve, y asigne al mismo tiempo un nuevo collection_name al almacén de vectores.