Files
mempalace/deploy/unraid/UNRAID_GUI_BR0.md
T
2026-05-09 11:10:17 -05:00

412 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MemPalace on Unraid — GUI install with `br0` networking
This walks through installing MemPalace as a single Docker container on
Unraid, using the **dockerMan WebUI** (no compose, no SSH-driven YAML),
attached to **`br0`** so the container has its own IP address on your
LAN — separate from Unraid's IP.
If you'd rather do an SSH/compose install with TLS + bearer auth via a
Caddy sidecar, see [`README.md`](README.md). This guide is the
GUI-friendly path.
---
## Why `br0`?
`br0` (macvlan-style bridge to your physical network) gives the
container its **own LAN IP**. That means:
* No port conflicts with Unraid itself (you can keep `8765`/`8766`
instead of remapping).
* Clients connect to the container directly — `http://<container-ip>:8765/sse`
rather than `http://<unraid-ip>:8765/sse`.
* The container can be reached by every machine on your LAN without
punching holes in the Unraid host.
The trade-off, classic macvlan:
* By default the **Unraid host itself can't talk to the container**.
If you only ever connect from other machines, this is fine. If you
also want to test from Unraid's own shell, enable **Host access to
custom networks** (see Step 1).
* The container is exposed bare to the LAN. Anyone on the LAN can hit
the MCP endpoint — it has no auth in this mode. See "Security
notes" at the bottom.
---
## Prerequisites
* Unraid 6.12+ with Docker enabled.
* The repo synced to a path on Unraid (e.g. `/mnt/user/system/build/mempalace`).
You can clone over SSH or just copy the folder via SMB.
* `/mnt/user/appdata` available (default on every Unraid).
* A free LAN IP for the container, ideally with a DHCP reservation on
your router so it doesn't drift.
---
## Step 1 — Enable br0 in Unraid
Skip this if you already use `br0` for other containers.
1. **Stop the Docker service** so settings are editable: *Settings →
Docker → Enable Docker = No → Apply*.
2. *Settings → Docker → Advanced View*.
3. **Host access to custom networks** = **Enabled**. (Lets the Unraid
shell reach `br0` containers — needed for `curl` health checks
from Unraid itself. Skip if you don't need that.)
4. **Docker custom network type** = **macvlan** (default) or
**ipvlan** if your switch/router doesn't like macvlan MAC churn.
5. Re-enable Docker: *Enable Docker = Yes → Apply*.
6. Confirm `br0` shows up under *Settings → Docker → Docker custom
networks* (or implicitly — every Unraid with a bridge has it).
---
## Step 2 — Build the image on Unraid
There's no public registry image — the container is built locally from
the repo's root `Dockerfile`. SSH into Unraid (or open the WebUI
terminal) and run:
```bash
cd /mnt/user/system/build/mempalace # wherever you put the repo
docker build -t mempalace-server:latest .
```
First build pulls `python:3.13-slim` and pip-installs `mempalace` +
`mcp-proxy` (~35 minutes). When it finishes:
```bash
docker images mempalace-server
# REPOSITORY TAG IMAGE ID CREATED SIZE
# mempalace-server latest <hash> <time> ~600 MB
```
You only repeat this step when you `git pull` an update.
---
## Step 3 — Create appdata directories
```bash
mkdir -p /mnt/user/appdata/mempalace
chown -R 99:100 /mnt/user/appdata/mempalace
```
The container runs as UID/GID **99:100** (`nobody:users` — the Unraid
appdata convention). Wrong ownership here is the #1 cause of "permission
denied" on first start.
---
## Step 4 — Add the container in the WebUI
1. *Docker → Add Container* (don't pick a template — fill it in
manually).
2. Fill the top section:
| Field | Value |
|---|---|
| **Name** | `MemPalace` |
| **Repository** | `mempalace-server:latest` |
| **Network Type** | **Custom: br0** |
| **Fixed IP address** | *(your chosen LAN IP, e.g. `192.168.1.50`)* |
| **Console shell command** | `Bash` |
| **Privileged** | `Off` |
| **Extra Parameters** | `--user 99:100` |
Use a fixed IP. macvlan + DHCP can race the DHCP server on
container restart and end up unreachable.
3. Leave **WebUI** blank (there isn't one). **Icon URL** optional:
`https://raw.githubusercontent.com/MemPalace/mempalace/develop/assets/mempalace_logo.png`
---
## Step 5 — Add the path mapping
Click **Add another Path, Port, Variable, Label or Device****Path**:
| Field | Value |
|---|---|
| **Name** | `Appdata` |
| **Container Path** | `/data` |
| **Host Path** | `/mnt/user/appdata/mempalace` |
| **Access Mode** | `Read/Write` |
This holds ChromaDB, the knowledge graph, the embedding-model cache,
and the inbox for ingested transcripts. Lose it = lose all memory.
---
## Step 6 — Add the ports
In `br0` mode the "host port" field is just a label — the container's
ports are published on **the container's own IP**, not Unraid's. Set
both to match the container port.
Click **Add another …****Port** twice:
| Name | Container Port | Host Port | Connection Type |
|---|---|---|---|
| `MCP SSE` | `8765` | `8765` | `TCP` |
| `Ingest HTTP` | `8766` | `8766` | `TCP` |
Skip the ingest port if you only use this from a single machine and
won't push transcripts in via HTTP hooks.
---
## Step 7 — Add environment variables
Click **Add another …****Variable** for each:
### Required
| Name | Key | Value |
|---|---|---|
| Palace path | `MEMPALACE_PALACE_PATH` | `/data/palace` |
### Recommended (enables transcript ingest)
| Name | Key | Value |
|---|---|---|
| Ingest port | `MEMPALACE_INGEST_PORT` | `8766` |
| Ingest bind | `MEMPALACE_INGEST_HOST` | `0.0.0.0` |
| Ingest token | `MEMPALACE_INGEST_TOKEN` | *a long random string* |
`MEMPALACE_INGEST_TOKEN` is optional but **strongly recommended in
`br0` mode** — without it, anyone on the LAN can POST transcripts
into your palace. Generate one with:
```bash
openssl rand -hex 32
```
…and save it; you'll set the same value as `MEMPAL_REMOTE_TOKEN` on
each client.
### Optional
| Name | Key | Default | Notes |
|---|---|---|---|
| Embedding device | `MEMPALACE_EMBEDDING_DEVICE` | *(auto)* | `cpu`, `cuda`, `dml`, or `coreml`. CUDA needs a rebuild with the `[gpu]` extra and the NVIDIA driver plugin. |
| Entity languages | `MEMPALACE_ENTITY_LANGUAGES` | `en` | Comma-separated, e.g. `en,es,de`. |
---
## Step 8 — Apply and verify
Click **Apply**. Unraid will start the container; first start takes
~10 s while ChromaDB initializes.
From any machine on your LAN (or from Unraid itself if you enabled
*Host access to custom networks*):
```bash
# Unauth liveness check on the ingest port:
curl http://<container-ip>:8766/healthz
# → {"status":"ok","version":"3.3.x"}
# Confirm token gating works (only relevant if you set MEMPALACE_INGEST_TOKEN):
curl -i http://<container-ip>:8766/ingest/transcript
# → HTTP/1.0 401 Unauthorized
```
For the MCP SSE endpoint:
```bash
curl -i http://<container-ip>:8765/sse
# Should hang open with `Content-Type: text/event-stream` —
# that's healthy. Ctrl-C to bail.
```
If both work, you're done. Container logs are under *Docker → MemPalace
→ Logs* in the WebUI, or `docker logs MemPalace` from the shell.
---
## Connect AI tools
Each client needs `mcp-proxy`:
```bash
uv tool install mcp-proxy
# or: pip install mcp-proxy
```
### Claude Code
`~/.claude.json` (or project `.mcp.json`):
```json
{
"mcpServers": {
"mempalace": {
"command": "mcp-proxy",
"args": ["http://<container-ip>:8765/sse"]
}
}
}
```
No bearer header on this path — the MCP endpoint is unauthenticated
in `br0` mode.
### Codex CLI — `~/.codex/config.toml`
```toml
[mcp_servers.mempalace]
command = "mcp-proxy"
args = ["http://<container-ip>:8765/sse"]
```
### Antigravity / Windsurf
Settings → AI → MCP Servers, or `~/.antigravity/mcp.json`:
```json
{
"mempalace": {
"command": "mcp-proxy",
"args": ["http://<container-ip>:8765/sse"]
}
}
```
### Smoke test
Start a session in any of those clients and ask:
> "Use mempalace_status to show palace stats."
You should get back a JSON blob with wing/room/drawer counts. A
connection error means wrong IP or the container isn't on `br0`.
---
## Set up auto-save hooks (optional)
Enables transcripts to flow into the palace automatically as you work.
Each client needs the env vars:
**Windows (PowerShell)**:
```powershell
[Environment]::SetEnvironmentVariable("MEMPAL_REMOTE_URL", "http://<container-ip>:8766", "User")
[Environment]::SetEnvironmentVariable("MEMPAL_REMOTE_TOKEN", "<the-ingest-token>", "User")
```
**macOS/Linux**: same exports in `~/.zshrc` / `~/.bashrc`.
Then wire `hooks/mempal_save_hook_remote.sh` and
`hooks/mempal_precompact_hook_remote.sh` into your client's hook
config — see the "Set up auto-save hooks" section in
[`README.md`](README.md), it's identical from this point on.
Note: these hooks talk **HTTP**, not HTTPS, since this guide doesn't
use Caddy. If `MEMPAL_REMOTE_URL` starts with `http://`, no TLS env
vars are needed.
---
## Security notes
`br0` puts the container on your LAN with no auth in front of MCP.
That's fine if and only if:
* You trust every device on your LAN (no untrusted IoT, no guest
network bridged in, no roommates).
* You've set `MEMPALACE_INGEST_TOKEN` so nobody can drop transcripts
in unprompted.
If those don't both hold, do one of these instead:
* **Run on a Tailscale subnet** rather than `br0` so only your
tailnet devices can reach it.
* **Use the Caddy compose path** in [`README.md`](README.md) to add
TLS + bearer-token auth on the MCP side too.
* **Front it with SWAG / Nginx Proxy Manager** if you already run
one — drop the bare `8765` exposure, route through the proxy with
bearer-token auth and SSE pass-through configured.
---
## Troubleshooting
### Container starts then dies immediately
99% of the time this is `/data` ownership. From Unraid shell:
```bash
ls -ld /mnt/user/appdata/mempalace
# Want: owner 99 (nobody), group 100 (users)
chown -R 99:100 /mnt/user/appdata/mempalace
```
Then *Docker → MemPalace → Restart*.
### "Address already in use" on start
Another container on `br0` is using the same IP. Pick a different
**Fixed IP address** in Step 4. (Port conflicts are not possible in
`br0` mode since each container has its own IP.)
### Unraid shell can't reach the container, but other machines can
Default macvlan behavior. *Settings → Docker → Host access to custom
networks → Enabled* (Step 1).
### Clients see SSE connect but tool calls hang
Network path is fine; this is usually a stale `mcp-proxy` install on
the client. `uv tool upgrade mcp-proxy` or `pip install -U mcp-proxy`.
### Embedding model download stalls on first request
The ~80 MB MiniLM ONNX model downloads from HuggingFace on first use.
Pre-warm it from the Unraid shell:
```bash
docker exec MemPalace python -c \
"from chromadb.utils.embedding_functions import ONNXMiniLM_L6_V2; ONNXMiniLM_L6_V2()(['warmup'])"
```
Subsequent uses load from cache (~50 ms).
### Port 8765 / 8766 already used by something else
In `br0` mode the container has its own IP, so this only happens if
you literally run a second mempalace on the same IP. Pick a different
fixed IP and restart.
---
## Updating
```bash
cd /mnt/user/system/build/mempalace
git pull
docker build -t mempalace-server:latest .
```
Then in the WebUI: *Docker → MemPalace → Force Update* (or just
*Restart*). Your palace data persists because it's on the
`/mnt/user/appdata/mempalace` volume, outside the container.
---
## Backups
Add `/mnt/user/appdata/mempalace/` to **CA Backup / Appdata Backup**.
Three subdirectories matter:
* `palace/` — ChromaDB vectors + SQLite metadata
* `kg/` — knowledge graph
* `inbox/` — uploaded transcripts (kept for re-mining)
Losing this directory = losing all memory. The image itself is
disposable — you can rebuild it from the repo any time.