Skip to content

Architecture

A self-hosted Engram instance is five processes:

┌─────────────────────────────────────────────────────────┐
│ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ Engram app │◄─►│ Postgres │ │
│ │ (Elixir) │ │ (notes, │ │
│ │ │ │ metadata) │ │
│ │ - REST API │ └───────────────┘ │
│ │ - WebSocket │ ┌───────────────┐ │
│ │ - MCP │◄─►│ Qdrant │ │
│ │ - Web SPA │ │ (vectors) │ │
│ │ │ └───────────────┘ │
│ │ │ ┌───────────────┐ │
│ │ │◄─►│ Ollama │ (embeddings) │
│ │ │ └───────────────┘ │
│ │ │ ┌───────────────┐ │
│ │ │◄─►│ MinIO │ (attachments) │
│ └───────────────┘ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
  • Engram app — the Elixir/Phoenix monolith. Serves the REST API, WebSocket sync, MCP endpoint, and the React web SPA from one process. Stateless beyond its database/cache connections.
  • Postgres — notes, folders, metadata, users, billing. Uses Row-Level Security for multi-tenancy. Also hosts Oban (job queue).
  • Qdrant — vector store for semantic search. Holds embeddings with binary quantization + rescore.
  • Ollama (or BYO embedding service) — generates embeddings on every note upsert. Default model: nomic-embed-text (768 dims).
  • MinIO (or any S3-compatible) — stores attachments (images/PDFs/binary files). Notes themselves live in Postgres.
  • Elixir/OTP — concurrent sync fan-out, fault tolerance, no GIL bottleneck on the WebSocket fan-out path
  • Postgres — boring is good; RLS handles multi-tenancy without custom plumbing
  • Qdrant — fast HNSW, binary quantization keeps memory tight
  • Ollama — easiest path to local embeddings
  • MinIO — S3-compatible self-host; you can swap in real S3, R2, or any S3-compatible bucket

The app listens on port 4000 by default. A reverse proxy (Caddy, Nginx, NPM) terminates TLS in front of it. Postgres, Qdrant, Ollama, and MinIO are on a private Docker network — only the app talks to them.

For TLS guidance see Quickstart.