MarkDB

Self-hosting

Run MarkDB yourself with Docker Compose, and the environment matrix to configure it.

MarkDB is open source. One Go binary runs every role (via a mode flag), backed by three services: PostgreSQL (pgvector), Redis, and Meilisearch.

Run it locally

git clone https://github.com/markdb-cloud/markdb
cd markdb
cp .env.example .env
docker compose up

The root docker-compose.yml brings up:

ServicePortImage
markdb (serve)8080built from Dockerfile
markdb-proxy8090same image, -mode proxy
markdb-workersame image, -mode worker
postgres5437→5432pgvector/pgvector:pg17
redis6379redis:8-alpine
meilisearch7700getmeili/meilisearch

Add the dashboard and a local Caddy with docker compose --profile fullstack up.

Binary modes

The mode is set by MARKDB_MODE (or -mode):

ModeRole
serveHTTP API + MCP server (the data plane and control plane).
proxyThe LLM capture proxy.
workerEnrichment + indexing background worker.
stdio-mcpMCP over stdio (for local/desktop clients).
migrateApply migrations and exit.

Migrations are applied automatically on startup in every mode (a custom, forward-only, filename-ordered runner over db/migrations); there are no down migrations.

Backing services

  • PostgreSQL + pgvector (pgvector/pgvector:pg17) -- the system of record; requires the vector, pgcrypto, and citext extensions (all in that image). A shm_size of at least 1g is recommended.
  • Redis (redis:8-alpine) -- cache, rate limiting, and chat-activity tracking. If Redis is unavailable, serve falls back to an in-memory cache and rate limiting is disabled.
  • Meilisearch (cloud pins v1.43.0) -- keyword search. If it's unavailable, search falls back to Postgres keyword search.

Environment matrix

The key MARKDB_* variables (plus DATABASE_URL and REDIS_URL), with defaults:

Core

VarDefault
MARKDB_MODEserve
MARKDB_HTTP_ADDR:8080
MARKDB_PROXY_HTTP_ADDR:8090
MARKDB_MCP_PATH/mcp
MARKDB_LOG_LEVELinfo
MARKDB_MIGRATIONS_DIRdb/migrations

Data stores

VarDefault
DATABASE_URLpostgres://markdb:markdb@postgres:5432/markdb?sslmode=disable
MARKDB_DB_POOL_SIZE16
REDIS_URLredis://redis:6379/0
MARKDB_REDIS_ENABLEDtrue

Search

VarDefault
MARKDB_SEARCH_ENABLEDtrue
MARKDB_SEARCH_DIMENSIONS3072
MARKDB_MEILISEARCH_URLhttp://meilisearch:7700
MARKDB_MEILISEARCH_API_KEY
MARKDB_MEILISEARCH_INDEXmarkdb_pages

Proxy & worker

VarDefault
MARKDB_PROXY_USE_NATIVE_SDK"" (empty = all)
MARKDB_PROXY_INJECT_CONTEXTtrue
MARKDB_PROXY_CHAT_IDLE_TIMEOUT3h
MARKDB_ENRICHMENT_ENABLEDfalse (worker compose sets true)
MARKDB_WORKER_PROXY_URL
MARKDB_WORKER_SERVICE_TOKEN
ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY / XAI_API_KEY

Auth & keys

VarDefault
MARKDB_AUTH_MODEheaders (headers, bearer, disabled, jwt, jwt+bearer, service)
MARKDB_MCP_READ_ONLYfalse
MARKDB_JWT_SECRET— (≥32 chars for JWT modes)
MARKDB_GITHUB_CLIENT_ID / _SECRET
MARKDB_GOOGLE_CLIENT_ID / _SECRET
MARKDB_LOCAL_KEK_BASE64— (base64 of 32 bytes; see Security)
MARKDB_RATE_LIMIT_PERSONAL_PER_MINUTE60
MARKDB_RATE_LIMIT_TEAM_PER_MINUTE600

Public URLs (used to build links and set cookie/OAuth redirects): MARKDB_PUBLIC_API_URL, MARKDB_PUBLIC_DASHBOARD_URL, MARKDB_PUBLIC_PROXY_URL, MARKDB_PUBLIC_MCP_URL, MARKDB_PUBLIC_BASE_URL.

Embedding dimensions

.env.example ships MARKDB_SEARCH_DIMENSIONS=1536, but the default embedder path (gemini-embedding-001) and both compose files use 3072. Whatever you pick, index-time and query-time dimensions must match, so set it once and rebuild the index if you change it.

Single-VM production

deploy/cloud/ contains a production reference: a docker-compose.yml with Caddy terminating TLS (auto Let's Encrypt) for the api, mcp, proxy, and dashboard hosts, plus Terraform (deploy/cloud/terraform/) that provisions a GCP VM, static IP, Artifact Registry, a backups bucket, a KMS key, and Secret Manager entries. DNS A records are pointed at the VM manually. See deploy/cloud/terraform/README.md in the repo.