Files
2026-05-09 11:10:17 -05:00

12 KiB
Raw Permalink Blame History

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. 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:

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:

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

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 DevicePath:

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
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:

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):

# 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:

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:

uv tool install mcp-proxy
# or: pip install mcp-proxy

Claude Code

~/.claude.json (or project .mcp.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

[mcp_servers.mempalace]
command = "mcp-proxy"
args = ["http://<container-ip>:8765/sse"]

Antigravity / Windsurf

Settings → AI → MCP Servers, or ~/.antigravity/mcp.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):

[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, 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 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:

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:

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

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.