
Fredy Acuna / May 4, 2026 / 12 min read
Engram es un sistema de memoria persistente para agentes de IA que se conectan vía MCP (Claude Code, Cursor, Codex, etc.). Localmente guarda todo en SQLite, y opcionalmente podés sincronizar tu memoria con un servidor cloud para acceder a ella desde cualquier maquina o compartirla entre proyectos.
En esta guía vamos a montar Engram Cloud en tu propio VPS usando Dokploy, con autenticación real (no modo inseguro), HTTPS automático y dashboard web. Esta guía nace de hacerlo en producción y resolver TODOS los errores que aparecieron en el camino.
Antes de empezar, asegurate de tener:
engram.tudominio.com)Engram es un binario Go agnóstico al agente. Tiene varios modos:
~/.engram/ (SQLite)mem_save, mem_search, etc.) a tu agente de IALo importante: el SQLite local es siempre la fuente de verdad. Cloud actúa como índice replicado, NO como almacenamiento primario. Si tu cloud se cae, seguís trabajando localmente sin perder nada.
Cada proyecto en Engram es un namespace totalmente aislado. Si trabajás en 10 proyectos, cada uno tiene su propia memoria, sus propias observations, sus propias sessions. No comparten nada.
El nombre del proyecto se resuelve desde el cwd del MCP server, no desde lo que el LLM diga. Si abrís Claude Code en ~/projects/foo, ese es el proyecto foo. Si abrís en ~/projects/bar, es bar. Memoria separada.
En vez de meter el docker-compose.yml directo en Dokploy, vamos a hacer un repo privado en GitHub que contenga la configuración. Dokploy lo va a clonar y deployar.
Crea un directorio nuevo:
mkdir engram-deploy && cd engram-deploy
Crea el docker-compose.yml:
services:
postgres:
image: postgres:16-alpine
container_name: engram-cloud-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 3s
retries: 10
volumes:
- engram-cloud-pg:/var/lib/postgresql/data
cloud:
image: ghcr.io/gentleman-programming/engram:${ENGRAM_VERSION}
container_name: engram-cloud
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
ENGRAM_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable
ENGRAM_JWT_SECRET: ${ENGRAM_JWT_SECRET}
ENGRAM_CLOUD_TOKEN: ${ENGRAM_CLOUD_TOKEN}
ENGRAM_CLOUD_INSECURE_NO_AUTH: '0'
ENGRAM_CLOUD_ALLOWED_PROJECTS: ${ENGRAM_CLOUD_ALLOWED_PROJECTS}
ENGRAM_CLOUD_HOST: 0.0.0.0
ENGRAM_PORT: '18080'
expose:
- '18080'
command: ['cloud', 'serve']
volumes:
engram-cloud-pg:
Y un .gitignore para no commitear secretos:
.env
.env.local
*.local
Decisiones del compose explicadas:
- Imagen oficial pre-publicada:
ghcr.io/gentleman-programming/engramya viene con el comandocloud servecomo CMD por defecto. No buildeamos nada — Dokploy hacedocker pully arranca en segundos.- Versión pineada en
ENGRAM_VERSION: nunca useslatesten producción. Si bumpeás manualmente, sabés exactamente qué cambia.- Postgres NO expuesto al host: sacamos el
ports:mapping del compose original. La base solo es accesible desde la red interna del compose. Más seguro.expose: 18080(sin host port): dejamos que Traefik (que viene con Dokploy) agarre el servicio por la red interna y le pegue HTTPS. No exponemos puertos públicos directamente al VPS.ENGRAM_CLOUD_INSECURE_NO_AUTH: '0': el compose oficial viene en'1'para desarrollo local. Asegurate de que esté en'0'para producción.
Pushea el repo a GitHub (privado):
git init -b main
git add -A
git commit -m "feat: initial engram cloud deploy compose"
gh repo create <tu-usuario>/engram-deploy --private --source=. --push
Engram Cloud necesita tres secretos distintos. Cada uno con su rol:
| Variable | Rol | Quién lo ve |
|---|---|---|
POSTGRES_PASSWORD | Password del usuario postgres | Solo el server |
ENGRAM_JWT_SECRET | Clave HMAC para firmar tokens internos | Solo el server |
ENGRAM_CLOUD_TOKEN | Bearer token compartido (cliente lo manda en headers) | Server Y cliente |
Importante: cada uno tiene que tener un valor distinto. Reusar el mismo secreto para dos cosas distintas es una práctica horrible.
# POSTGRES_PASSWORD — DEBE ser URL-safe (va dentro de una URL postgres://)
openssl rand -hex 32
# ENGRAM_JWT_SECRET — solo va en env vars, base64 OK
openssl rand -base64 48
# ENGRAM_CLOUD_TOKEN — solo va en headers, base64 OK
openssl rand -base64 48
¿Por qué hex para postgres?
El password se inserta dentro de
ENGRAM_DATABASE_URL. Si tiene caracteres reservados de URL (:,/,@,?,#,+), rompe el parser y vas a ver errores cripticos comoinvalid portque NO tienen nada que ver con el puerto real. Hex ([0-9a-f]) es totalmente URL-safe y evita ese problema desde el inicio.
engram-cloud)maindocker-compose.ymlEn la pestaña Environment de tu servicio, pegá esto reemplazando los <...> con los valores que generaste:
ENGRAM_VERSION=v1.15.7
POSTGRES_USER=engram
POSTGRES_DB=engram_cloud
POSTGRES_PASSWORD=<output de openssl rand -hex 32>
ENGRAM_JWT_SECRET=<output de openssl rand -base64 48>
ENGRAM_CLOUD_TOKEN=<output de openssl rand -base64 48>
ENGRAM_CLOUD_ALLOWED_PROJECTS=personal
Sobre ENGRAM_CLOUD_ALLOWED_PROJECTS: esto es una whitelist a nivel server de qué proyectos puede aceptar el cloud. Empezá con personal o el nombre del proyecto donde vas a usar Engram. Si después querés agregar más, los sumás separados por coma y hacés redeploy:
ENGRAM_CLOUD_ALLOWED_PROJECTS=personal,blog,trabajo,experimentos
Importante: el "redeploy" en Dokploy con imagen publicada NO es buildeo + downtime largo. Solo hace
docker pull(que ya está cached) y restartea el container con las env vars nuevas. 2 a 5 segundos de downtime, durante los cuales tu cliente sigue funcionando con el SQLite local sin perder nada. El sync se reanuda automáticamente cuando vuelve.
En la pestaña Domains de tu servicio:
cloudengram.tudominio.com18080/Antes del deploy: asegurate de que el DNS de
engram.tudominio.comya esté apuntando a la IP de tu VPS. Si Let's Encrypt no puede validar el dominio, el deploy va a salir pero sin TLS.
Dale Deploy. En menos de un minuto deberías ver:
engram-cloud-postgres: Up (healthy)engram-cloud: UpMirá los logs del container engram-cloud. Si arranca limpio vas a ver algo tipo:
cloud serve listening on 0.0.0.0:18080
Abrí https://engram.tudominio.com/dashboard/login en el browser. Pegá tu ENGRAM_CLOUD_TOKEN. Login. Listo, estás dentro del dashboard.
Ahora hay que apuntar tu cliente Engram local al server.
engram version
Necesitás mínimo v1.15.x (las versiones anteriores no tienen comandos cloud). Si te dice engram dev o engram vdev, es una development build sin features de cloud. Reinstalá con un tag específico:
go install github.com/Gentleman-Programming/engram/cmd/engram@v1.15.7
El detalle clave: el
@v1.15.7es OBLIGATORIO. Sin tag, Go te buildea desde main como dev build sin versión. Con tag te genera el binario con la versión correcta.
Verificá que ahora sí salga la versión:
engram version
# debe decir: engram 1.15.7
engram --help | grep cloud
# debe listar el subcomando 'cloud'
Agregá a tu ~/.bashrc o ~/.zshrc (lo que uses — verificalo con echo $SHELL):
export ENGRAM_CLOUD_TOKEN='<el mismo token que pusiste en Dokploy>'
Recargá la shell (source ~/.bashrc o nueva terminal).
engram cloud config --server https://engram.tudominio.com
engram cloud status
Deberías ver:
Cloud status: configured (target=cloud)
Server: https://engram.tudominio.com
Auth status: ready (token provided via runtime cloud config)
Sync readiness: ready for explicit --project sync (project must be enrolled)
Si dice Auth status: token not configured, el shell no está leyendo la env var. Verificá echo "${ENGRAM_CLOUD_TOKEN:0:6}..." (debe imprimir los primeros 6 chars).
cd ~/path/al/proyecto-que-querés-sincronizar
engram cloud enroll personal # solo la primera vez
engram sync --cloud --project personal
Recargá el dashboard en https://engram.tudominio.com/dashboard. Los 0 / 0 / 0 ahora muestran números reales: el proyecto, vos como contributor, y el total de chunks sincronizados.
Engram Cloud trae un dashboard completo. Sin necesidad de admin token, vas a poder ver:
| Path | Para qué sirve |
|---|---|
/dashboard | Landing |
/dashboard/stats | Métricas generales |
/dashboard/activity | Actividad reciente |
/dashboard/projects | Lista de tus proyectos |
/dashboard/projects/{name} | Detalle de un proyecto: observations, sessions, prompts |
/dashboard/browser/observations | Navegador de TODAS tus observations |
/dashboard/browser/sessions | Navegador de sesiones |
/dashboard/browser/prompts | Historial de prompts |
/dashboard/contributors | Quién contribuyó qué (útil para equipos) |
Si querés features admin (pausar/reanudar sync por proyecto, audit log), generá otro token con openssl rand -base64 48 y agregá ENGRAM_CLOUD_ADMIN=<token> como env var en Dokploy. Después accedés vía /dashboard/admin/*.
Estos son los errores que te vas a topar en el orden exacto que aparecen. Yo los tuve todos.
cloud auth token is required: set ENGRAM_CLOUD_TOKENCausa: te falta ENGRAM_CLOUD_TOKEN en las env vars del server. El compose oficial trae ENGRAM_CLOUD_INSECURE_NO_AUTH=1 por defecto (modo dev sin auth) — al pasar a producción tenés que agregar el token.
Fix: agregar ENGRAM_CLOUD_TOKEN en Environment de Dokploy y redeploy.
cannot parse ... invalid port ":XXXXX" after hostCausa: tu POSTGRES_PASSWORD tiene caracteres reservados de URL (:, /, +, @). El parser de la URL postgres se confunde y cree que parte del password es un puerto.
Fix: regenerar el password con openssl rand -hex 32 (alfabeto [0-9a-f], totalmente URL-safe).
password authentication failed for user "engram" (después de cambiar el password)Causa: cambiaste POSTGRES_PASSWORD en Dokploy pero el volumen postgres ya estaba inicializado con el password anterior. La imagen oficial de postgres solo aplica POSTGRES_PASSWORD la PRIMERA vez que crea el data dir. Cambiar el env var después no actualiza al usuario existente.
Fix: borrar el volumen postgres y redeploy. Como el cloud nunca llegó a guardar datos tuyos, no perdés nada.
volume is in use al intentar docker volume rmCausa: hiciste "Stop" en Dokploy, pero "Stop" solo detiene los containers, no los elimina. Containers stopped siguen reteniendo referencias a sus volúmenes.
Fix:
# Encontrar el container parado
docker ps -a | grep engram
# Eliminarlo (no solo stoppear)
docker rm <container-id>
# Ahora sí
docker volume rm <nombre-real-del-volumen>
Nota: el nombre real del volumen incluye un prefijo de Dokploy. Hacé
docker volume ls | grep engrampara ver el nombre exacto, algo tipo<projectid>_engram-cloud-pg.
Si te resulta más simple: borrar la app entera en Dokploy y recrearla. Limpia todo de un saque.
engram dev (cliente sin comandos cloud)Causa: instalaste el binario con go install ...@latest o sin tag. Eso te buildea desde main como development build, que NO incluye los comandos cloud si tu Go cache tomó un commit anterior al feature.
Fix:
go install github.com/Gentleman-Programming/engram/cmd/engram@v1.15.7
goenv rehash # solo si usás goenv
engram version # debe decir 1.15.7, NO dev
Si seteaste ENGRAM_CLOUD_TOKEN en ~/.zshrc pero engram cloud status sigue diciendo token not configured, verificá tu shell:
echo $SHELL
Si dice /bin/bash, .zshrc nunca se carga. Tenés que poner el export en ~/.bashrc.
Para despliegues serios:
ENGRAM_CLOUD_TOKEN se filtró, regenerá ambos (server y client) y redeployá. Cualquiera con ese token tiene acceso de lectura/escritura a TODOS los proyectos whitelisted.engram-cloud-pg (Dokploy tiene integración con S3-compatible storage).ENGRAM_CLOUD_ALLOWED_PROJECTS como defensa adicional: incluso si te leakean el token, solo los proyectos en la whitelist pueden ser sincronizados. Mantené la lista corta y específica..gitignore con .env es obligatorio. Los secretos viven solo en Dokploy → Environment.Ahora tenés tu propia infraestructura de memoria persistente para agentes de IA corriendo en tu VPS:
Y lo más importante: tu memoria es tuya. No depende de un servicio cloud externo. Si Engram desaparece mañana, vos seguís teniendo todo localmente en SQLite y un postgres replica en tu VPS.