Claude
Skills
Sign in
Back

nowledge-mem-docker

Included with Lifetime
$97 forever

Install, check on, or upgrade a self-hosted Nowledge Mem server (the headless Docker deployment) using the `nmemctl` lifecycle controller. Use this whenever the user mentions running their own Nowledge Mem instance, self-hosting Mem on a NAS, VPS, homelab, or server, deploying `nowledgelabs/mem` from Docker Hub, troubleshooting their Mem container, or upgrading a Mem server to a newer version. Trigger even when the user says "my Mem server", "self-hosted Mem", "the docker version of Mem", "memory server on my Synology / Proxmox / Raspberry Pi", or just describes a container that's at `docker.io/nowledgelabs/mem` without naming the product. Do NOT trigger for the Mem desktop app, Mem Cloud, or anything that doesn't touch the operator's own server.

Cloud & DevOps

What this skill does


# Nowledge Mem — self-hosted Docker maintenance

You are helping the user run **their own** Nowledge Mem server — the headless Docker deployment that lives behind the desktop app and Mem clients. The server image is `docker.io/nowledgelabs/mem`. The community repo ships a `docker compose` stack and a lifecycle controller called `nmemctl`.

Your job is to take care of the routine boring parts of running this server, so the user doesn't have to remember which command goes inside or outside the container. **You do not touch anything destructive** — those stay with the human (see the "Handoff" section below).

## Mental model — the two CLIs

There are two command-line tools, and they look similar on purpose:

- **`./nmemctl`** — outside the container, in the deploy directory. Controls the **container lifecycle** (up, status, logs, upgrade). Same idea as `systemctl` or `kubectl`. **This is the one you'll use most.**
- **`nmem`** — inside the container, runs the Python application CLI. Handles data operations. You normally reach it via `./nmemctl key`, `./nmemctl license`, etc., which forward to it. You rarely need to `docker compose exec` it directly.

If you find yourself wanting to write `docker compose exec mem nmem ...`, stop — there's almost certainly an `./nmemctl` verb for what you want.

## Triage — where is the user?

Before doing anything, figure out which of these three situations applies:

### Path A — Fresh install (no deploy exists yet)

The user wants to set Mem up on this server for the first time. Signals: they haven't mentioned a clone before; `community/docker/compose.yaml` isn't on disk; `docker ps` shows no `nowledge-mem` container.

Run this exact sequence:

```bash
# 1. Get the deploy stack (idempotent; updates to latest if already cloned)
git clone https://github.com/nowledge-co/community.git ~/nowledge-mem || \
  git -C ~/nowledge-mem pull --ff-only

# 2. Bring it up — nmemctl will wait for /livez, print the API key, and show the URL
cd ~/nowledge-mem/community/docker
./nmemctl up
```

That's the whole install. `nmemctl up` is idempotent — safe to rerun.

The output includes:
- An **Access Anywhere API key** (a long string starting with `nmem_`). The user pastes this into the desktop / web client when prompted.
- The **URL** to open (typically `http://<host-ip>:14242/app`).
- License status (will say "Free / 0 / 20 memories" until activated).

Hand the API key to the user verbatim. **Do not save it anywhere** — it lives in a `mode 0600` file at `./config/co.nowledge.mem.desktop/remote-access.json` next to the compose file on the host (mounted at `/etc/nowledge-mem/co.nowledge.mem.desktop/remote-access.json` inside the container). The user only needs to see it once.

If the user wants TLS (a real domain with HTTPS), do **not** improvise. Tell them:

> "I can enable TLS by overlaying `compose.tls.yaml` once you've pointed a DNS record at this host. Want me to walk you through that? You'll need a domain name, ports 80/443 open, and Docker Compose v2.24.4+."

Then read `community/docker/README.md` (in the cloned repo) for the TLS section and follow it.

### Path B — Maintenance (server already running)

The user wants to know how their server is doing, see logs, look up the API key again, etc. Signals: `docker ps` shows `nowledge-mem` already running; the user says things like "is my server healthy", "what's running", "show me the logs".

**Finding the deploy directory.** Different operators install in different places. Path A uses `~/nowledge-mem/` as a sensible default, but on existing installs you'll need to discover the actual location. In order of cost:

```bash
# 1. Cheapest: ask docker where the compose project lives
docker inspect nowledge-mem --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'

# 2. Fall back: find a compose.yaml that defines a 'mem' service
find ~ -maxdepth 5 -name compose.yaml -path '*/community/docker/*' 2>/dev/null

# 3. Worst case: ask the user "where did you clone community/docker/?"
```

Once located, `cd` there and run:

```bash
./nmemctl status
```

`status` prints the container state, `/livez` health, license tier, image version, the current API key, and the web URL. Most "is everything ok?" questions are answered by this one command.

Other safe operations:

```bash
./nmemctl logs --tail 100      # recent server logs
./nmemctl logs -f              # live tail; use --no-color in scripts
./nmemctl key                  # print the current API key (read-only)
./nmemctl license              # show license tier + activation status
./nmemctl version              # image tag + binary version
./nmemctl restart              # restart the container, data preserved
```

`./nmemctl restart` IS safe to run autonomously — it preserves all data and is the standard recovery move for "the server feels stuck". Always print the `status` output afterwards to confirm `/livez` is back.

**Reading version output.** `./nmemctl version` prints two lines:

```
image:   docker.io/nowledgelabs/mem:0.8.4-rc5
binary:  0.8.4
```

These can legitimately differ. `image:` is the docker tag the operator pulled; `binary:` is what the Python application reports for itself (from `pyproject.toml`). A pre-release tag like `0.8.4-rc1` corresponds to the same base `binary: 0.8.4` because the suffix isn't baked into the application version. When you see a divergence:

- `image` and `binary` share a base (`0.8.4-rc5` vs `0.8.4`) → fine; the operator is on a pre-release for an upcoming stable. Mention it neutrally; do not push them to "upgrade" unsolicited.
- `image` is bare semver but `binary` differs → something has gone wrong (e.g. an old container against a new image). Surface the discrepancy to the user.

### Path C — Upgrade (move to a newer version)

The user wants to move from the current version to a newer one. Signals: "is there a new version", "upgrade Mem to X", "I saw 0.8.5 in the release notes".

```bash
./nmemctl version                       # confirm current image tag first
./nmemctl upgrade 0.8.5                 # pull + bump compose.yaml + recreate
```

`nmemctl upgrade` **refuses pre-release tags** by default (anything with a `-` suffix like `0.8.5-rc1` is blocked). This is intentional. Do not set `NMEM_ALLOW_PRERELEASE=1` to work around it unless the user explicitly asks for a release candidate. Stable semver only.

The backend runs schema migrations on startup. **There is no downgrade path** — once a newer image opens the database, an older image will refuse to. Mention this before upgrading if the user seems uncertain.

If `upgrade` fails midway, the previous image is still pulled locally. `./nmemctl restart` brings the container back up on the version listed in `compose.yaml`; check `./nmemctl version` to see which side of the bump it landed on.

### Path C-bis — Click-to-update from the web UI (opt-in)

Some operators don't want to SSH every release. The headless deploy has an opt-in companion that lets them click Update from the web UI in Settings → About → Self-hosted server.

How to guide the operator through enabling it (only after they ask for it, and after you surface the trust trade-off):

```bash
./nmemctl auto-update enable    # generates token, adds sidecar, recreates stack
./nmemctl auto-update status    # state + last pull + retained snapshots
./nmemctl auto-update disable   # remove the sidecar
./nmemctl auto-update rotate    # rotate the token (recreates sidecar + mem)
./nmemctl auto-update upgrade   # bump just the updater image
```

**Trust trade-off worth saying out loud:** auto-update adds a small sidecar container that holds `/var/run/docker.sock`. Docker socket access is host-root-equivalent for that container. The Mem container itself does NOT mount the socket; only the sidecar does. Per-deploy random token in `.env` (mode 0600). The sidecar is not exposed beyond the compose-internal network.
`enable` also sets `NOWLEDGE_ADMIN_REMOTE_OPS=1`, because browser-triggered i

Related in Cloud & DevOps