# Deployment

> Deploy Memoria with Docker Compose or Kubernetes. Environment variables, multi-instance setup, security. Use when deploying or configuring Memoria.

- Skill: `matrixorigin/deployment` (Agent Skill)
- Install (CLI): `npx skillmds@latest add matrixorigin/deployment`
- Raw SKILL.md: https://api.skillmd.com/api/skills/matrixorigin/deployment/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: matrixorigin (https://skillmd.com/u/matrixorigin)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/matrixorigin/deployment

---


## Docker Compose (Single Instance)

```bash
cd Memoria
cp .env.example .env   # Set MEMORIA_MASTER_KEY, MEMORIA_EMBEDDING_API_KEY
docker compose up -d
```

Services: API on `:8100`, MatrixOne on `:6001`. Verify: `curl http://localhost:8100/health`

## Environment Variables

### Required

| Variable | Description |
|----------|-------------|
| `MEMORIA_MASTER_KEY` | Admin API key (min 16 chars) |

### Database

| Variable | Default | Description |
|----------|---------|-------------|
| `MEMORIA_DB_HOST` | `matrixone` | MatrixOne host |
| `MEMORIA_DB_PORT` | `6001` | MatrixOne port |
| `MEMORIA_DB_USER` | `root` | Database user |
| `MEMORIA_DB_PASSWORD` | `111` | Database password |
| `MEMORIA_DB_NAME` | `memoria` | Database name |

### Embedding

| Variable | Default | Description |
|----------|---------|-------------|
| `MEMORIA_EMBEDDING_PROVIDER` | `local` | `local` or `openai` |
| `MEMORIA_EMBEDDING_MODEL` | `all-MiniLM-L6-v2` | Model name |
| `MEMORIA_EMBEDDING_API_KEY` | — | Required if provider is `openai` (single-backend) |
| `MEMORIA_EMBEDDING_BASE_URL` | — | Custom endpoint, OpenAI-compatible (single-backend) |
| `MEMORIA_EMBEDDING_ENDPOINTS` | — | JSON array for multi-backend round-robin. When set, supersedes `BASE_URL`/`API_KEY`. Format: `[{"url":"https://api1.example.com/v1","api_key":"sk-1"},{"url":"https://api2.example.com/v1","api_key":"sk-2"}]`. All endpoints must serve the same model. Requests rotate round-robin; failed/rate-limited endpoints are skipped automatically. |
| `MEMORIA_EMBEDDING_DIM` | `0` (auto) | Embedding dimension |

### Distributed

| Variable | Default | Description |
|----------|---------|-------------|
| `MEMORIA_INSTANCE_ID` | Random UUID | Unique instance ID. Set to Pod name in K8s |
| `MEMORIA_LOCK_TTL_SECS` | `120` | Distributed lock TTL. Heartbeat renews every TTL/3 |

### Governance

| Variable | Default | Description |
|----------|---------|-------------|
| `MEMORIA_GOVERNANCE_ENABLED` | `false` | Enable background governance scheduler |
| `MEMORIA_GOVERNANCE_PLUGIN_BINDING` | `default` | Repository binding name |
| `MEMORIA_GOVERNANCE_PLUGIN_SUBJECT` | `system` | Subject key for binding resolution |
| `MEMORIA_GOVERNANCE_PLUGIN_DIR` | — | Local plugin dir (dev mode, skips signature) |

### Security

| Variable | Default | Description |
|----------|---------|-------------|
| `MEMORIA_API_KEY_SECRET` | — | HMAC secret for key hashing. Set independently of MASTER_KEY |

### LLM (for reflect/extract-entities/episodic)

| Variable | Description |
|----------|-------------|
| `LLM_API_KEY` | OpenAI-compatible API key |
| `LLM_BASE_URL` | API base URL |
| `LLM_MODEL` | Model name |

## External MatrixOne

Use an existing MatrixOne instead of the bundled one:

```bash
MEMORIA_DB_HOST=your-host MEMORIA_DB_PORT=6001 docker compose up -d api
```

Tables are auto-created on first startup.

## Multi-Instance (K8s)

All coordination uses the shared MatrixOne database — zero external dependencies.

How it works:
- **Governance scheduler**: Each instance acquires a DB lock before each task. Only one wins; others skip. Heartbeat renews every TTL/3.
- **Async tasks**: Stored in `mem_async_tasks` table, visible cross-instance.
- **Lock expiry**: Crashed instance's locks expire after TTL, another takes over.
- **Single-instance**: `NoopDistributedLock` — zero overhead, identical behavior.

### K8s Manifest

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: memoria-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: memoria-api
  template:
    metadata:
      labels:
        app: memoria-api
    spec:
      containers:
        - name: memoria
          image: matrixorigin/memoria:latest
          ports:
            - containerPort: 8100
          env:
            - name: MEMORIA_INSTANCE_ID
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: MEMORIA_MASTER_KEY
              valueFrom:
                secretKeyRef:
                  name: memoria-secrets
                  key: master-key
            - name: MEMORIA_DB_HOST
              value: "matrixone.default.svc.cluster.local"
            - name: MEMORIA_DB_PORT
              value: "6001"
            - name: MEMORIA_EMBEDDING_PROVIDER
              value: "openai"
            - name: MEMORIA_EMBEDDING_API_KEY
              valueFrom:
                secretKeyRef:
                  name: memoria-secrets
                  key: embedding-api-key
            # Optional: multi-backend round-robin (supersedes API_KEY + MODEL single-backend)
            # - name: MEMORIA_EMBEDDING_ENDPOINTS
            #   value: '[{"url":"https://api1.example.com/v1","api_key":"sk-1"},{"url":"https://api2.example.com/v1","api_key":"sk-2"}]'
            - name: MEMORIA_EMBEDDING_MODEL
              value: "BAAI/bge-m3"
            - name: MEMORIA_EMBEDDING_DIM
              value: "1024"
            - name: MEMORIA_GOVERNANCE_ENABLED
              value: "true"
            - name: MEMORIA_LOCK_TTL_SECS
              value: "120"
          livenessProbe:
            httpGet:
              path: /health
              port: 8100
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health/instance
              port: 8100
            initialDelaySeconds: 5
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: memoria-api
spec:
  selector:
    app: memoria-api
  ports:
    - port: 8100
      targetPort: 8100
```

Key points:
- `MEMORIA_INSTANCE_ID` → `fieldRef: metadata.name` (Pod name)
- All replicas share one MatrixOne
- Leader election is automatic
- Liveness: `/health`, Readiness: `/health/instance`

## Data Persistence

MatrixOne data bind-mounted to `./data/matrixone`. Survives restarts. Change: `MATRIXONE_DATA_DIR=/your/path`

## Security Notes

- API keys are HMAC-SHA256 hashed at rest
- Set `MEMORIA_API_KEY_SECRET` independently of `MASTER_KEY` for key rotation
- All queries scoped to authenticated `user_id`
- Snapshot names are regex-validated before SQL
- Rate limiting per API key (in-memory sliding window)
- Run behind reverse proxy with TLS in production

## Rate Limits

Configurable via env: `MEMORIA_RATE_LIMIT_AUTH_KEYS=1000,60` (format: `max_requests,window_seconds`)

