Docker Deployment
Run Nowledge Mem as a headless server on a VPS, NAS, cloud VM, or homelab host using the official Docker image
Not sure if you need this? Most people use Nowledge Mem as a desktop app on macOS, Windows, or Linux — install once, the data lives locally, no Linux or Docker knowledge required. See Installation.
This page is for the specific case where you want Mem to run as a headless server on a host you administer — a VPS, a home NAS, a cloud VM, a homelab box — so the web app and nmem CLI on your other devices can reach it over your network.
The official nowledgelabs/mem image runs the same backend as the desktop app, serves the web app at /app, and keeps all your data in three plain bind-mounted directories on the host (./data, ./config, ./cache). You back them up with whatever you already use — rsync, restic, tar, ZFS snapshots.
It is in preview: shipping and verified end-to-end on real hosts, but the operator surface will keep evolving while we round it out.
Is this path for you?
Pick Docker when all of these are true:
- You have a long-running host you administer — a VPS, a home NAS box, a cloud VM, a homelab server, or similar — and you want Mem to live there as a service.
- You already manage other long-running services with Docker on that host, or you're comfortable enough with a shell to run a few commands and edit a
compose.yaml. - You want one centralized Mem server that the web app and the
nmemCLI on your laptop / phone / other machines can connect to over your network.
If none of those fit, install the desktop app instead — that is the normal path for almost every user, and your data still lives entirely on your machine.
Supported architectures
The image is published as a multi-arch manifest, so docker pull nowledgelabs/mem:<version> resolves correctly on the two architectures you'll typically run a server on:
| Architecture | Where it usually runs |
|---|---|
linux/amd64 | Standard x86_64 VPS, cloud VMs, x86 NAS boxes (Synology DSx+, QNAP TS-x73AU, etc.), homelab servers |
linux/arm64 | Ampere / AWS Graviton VPS, arm64 NAS boxes, Raspberry Pi 5 class boards |
You don't pick the arch — Docker pulls the right one for the host. If you happen to run Docker Desktop on an Apple Silicon Mac for development, it'll pull arm64 and work, but that isn't the recommended path: on a personal Mac, the desktop app is simpler.
Quick start
The drop-in stack lives in the community repository:
git clone https://github.com/nowledge-co/community.git
cd community/docker
./nmemctl upnmemctl is the lifecycle controller for this deploy. It starts the container, waits for /livez, prints your API key, and tells you the URL to open. If you already have a license code:
./nmemctl license activate <BASE64-LICENSE-CODE>Other commands: ./nmemctl status (health + key + URL), ./nmemctl logs -f, ./nmemctl upgrade <version>, ./nmemctl wipe (factory reset), ./nmemctl help for the full list.
By hand, the same thing in three lines:
docker compose up -d
docker compose exec -T mem nmem key # print API key
docker compose exec -T mem nmem license activate <BASE64-CODE> # optionalThen open http://<your-host>:14242/app and paste the API key when asked.
For your agent
Hand this URL to Claude / Codex / Cursor / any agent, and it will know how to install, monitor, and upgrade your Mem server on its own. Destructive operations (wipe, key rotation, license activation) always stay with you.
https://raw.githubusercontent.com/nowledge-co/community/main/skills/nowledge-mem-docker/SKILL.mdThat's the raw file — curl it and you get the markdown directly. The rest of this page is the human-readable version of the same workflow.
What you get
- One container running the same Mem backend as the desktop app, with the web app available at
/app. - A non-root user, a read-only image filesystem, all data in three local directories next to your
compose.yaml. No docker-volume idioms to learn — back them up withrsync,restic,tar, or ZFS snapshots. - A small image (~1.3 GB compressed) built straight from source. Same Python bundle as the desktop release.
- Multi-arch: one tag works on both
amd64andarm64hosts.
Where is my API key?
Mem generates an API key on first start. If you missed it, lost it, or want to rotate it, any of these works:
./nmemctl status # reprints key, license, URL, health
./nmemctl key # just the current key
./nmemctl key --rotate # rotate to a new value
./nmemctl logs | grep -A 1 "API Key" # peek at the first-run bannerThe key lives in ./config/co.nowledge.mem.desktop/remote-access.json on the host (mounted into the container at /etc/nowledge-mem/...), so it survives image upgrades. Backing up ./config backs up the key. If you rotate, every existing client — the web UI, MCP clients, the nmem CLI on remote machines — needs the new value pasted back in.
Where your data lives
Three local directories sit next to compose.yaml. You own them — standard tools work directly:
| Directory | Holds | Tier |
|---|---|---|
./data | Your graph, your conversations, your files | Irreplaceable — back this up |
./config | Settings, license, plugin choices, your API key, your device identity | Valuable — back this up |
./cache | Embedding models, search index projection | Rebuildable — safe to wipe |
Files are owned by UID 10001 inside the container; ./nmemctl up chowns the directories for you on first run via a one-shot helper container, so you never type chown or sudo yourself.
When you upgrade with ./nmemctl upgrade <version> (or docker compose pull && docker compose up -d by hand), all three directories stay in place. Your license stays activated, your data and conversations stay put, and the embedding model in cache means searches don't need to re-download anything. The only operation that resets you to a fresh install is ./nmemctl wipe, which is the deliberate "wipe and start over" flow.
SELinux operators (RHEL / Fedora / Rocky): append :Z to each bind mount in compose.yaml so SELinux relabels the host directories for container access. The label is opt-in because it's destructive on first apply.
Memory and growing data
The default mem_limit: 4g is sized for passive use of Mem — graph, web app, idle search. If you intend to use AI Now or Feed Agent actively on the server, raise it. As a rule of thumb:
| Your graph size | Recommended mem_limit |
|---|---|
| Under 200 MB | 2 GB |
| Under 1 GB | 4 GB (compose default) |
| 1 to 4 GB | 8 GB |
| 4 to 16 GB | 16 GB |
| Over 16 GB | Tune explicitly (see community/docker/README.md) |
If a file upload or an agent query ever leaves the UI hanging on 500s, the safe recovery is docker compose restart mem. The image escalates its internal memory budget on the next start automatically, so the same workload should fit afterward. If it keeps happening, raise mem_limit per the table above.
On arm64 SBCs (Raspberry Pi 5, Orange Pi 5, etc.), the same table applies, but be aware these boards usually share host RAM with the GPU and other accelerators. Leave headroom for the OS.
Verify the image came from us
Each release is published with a Sigstore attestation. If you want to confirm the image you're about to pull came from our build pipeline (and not from somewhere that happens to have the same tag), run:
cosign verify docker.io/nowledgelabs/mem:0.8.6 \
--certificate-identity-regexp='https://github.com/nowledge-co/mem/.github/workflows/release-docker.yml@.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com'A passing run shows the build's GitHub Actions identity. A failure means the image you pulled was not produced by our pipeline — do not trust it.
The attestation is attached to the manifest, so the same cosign verify works for both amd64 and arm64.
TLS on a public host
If the server is reachable from the internet and you want a real certificate, the same community/docker/ directory ships a Caddy sidecar:
export NOWLEDGE_DOMAIN=mem.example.com
export NOWLEDGE_LE_EMAIL=you@example.com
docker compose -f compose.yaml -f compose.tls.yaml up -dYou need a DNS record pointing at the host, ports 80 and 443 open for the Let's Encrypt challenge, and Docker Compose v2.24.4 or newer (the overlay uses a YAML merge tag that earlier versions silently ignore). Caddy renews the certificate automatically.
Backups and migration
Two layers, pick the one that matches your move:
Volume-level snapshot (same image version on both ends, fastest restore):
./nmemctl export # stop, tar ./data ./config ./cache, restart
./nmemctl export --no-cache # smaller archive, skip rebuildable cache
./nmemctl import mem-export-<host>-<ts>.tar.gz # restore on the new hostApplication-level dump (cross-version, or migrating from a .deb / desktop install — uses the same portable JSONL format the desktop app uses for "Export to file"):
./nmemctl backup-app # produces mem-app-export-<host>-<ts>.zip
./nmemctl restore-app mem-app-export-<host>-<ts>.zipFor the app-side guide to this portable export format, read Back Up, Export, and Import.
Both flows leave the machine_id behind on purpose: the destination gets a fresh device identity and re-activates the license on first launch (consumes one seat). Retire the source server after a successful migration — running both side-by-side diverges state.
You can also use rsync, restic, borg, ZFS snapshots, or plain tar directly against ./data and ./config — they're just host directories. Stop the container first (./nmemctl down) for a guaranteed-consistent snapshot, then ./nmemctl up afterward.
Application-level dump is also how you migrate between architectures (e.g. moving from an amd64 VPS to an arm64 NAS). Volume-level snapshots are tied to the source architecture's data layout for some on-disk projections, so cross-arch moves should go through backup-app / restore-app.
Upgrade from the web UI (optional)
By default, upgrading a self-hosted Mem server means SSH-ing in once per release and running ./nmemctl upgrade <version>. If you'd rather upgrade from the same web app you use Mem in, turn auto-update on once:
./nmemctl auto-update enableThat generates a per-deploy token, adds a small companion container that handles the upgrade work, and allows Install to be triggered from the browser. After this:
- Title-bar badge lights up when a newer Mem image is published, the same way the desktop app signals updates.
- Settings → Server card shows the current version, the latest published version, a Download button (background pull, no downtime), and an Install button (about 30 seconds of downtime, with a snapshot taken before the container is recreated).
- Skipping versions works. If you're on 0.8.4 and 0.8.6 is the latest, Install moves you directly to 0.8.6. Forward-only schema migrations run in order on the new image's first boot.
Before turning it on:
- The companion container mounts
/var/run/docker.sock, which is why this stays opt-in. That container is root-equivalent on the host. The Mem container itself never gets socket access. - Remote Install is a deliberate opt-in. Checking for updates always works from the web UI, but Download and Install are loopback-only until
NOWLEDGE_ADMIN_REMOTE_OPS=1is set.auto-update enablesets that flag for you, because clicking Install from a browser is a server-side state change. Only enable this on a trusted network. - Every Install takes a snapshot of
./dataand./configinto./cache/_pre-upgrade-<timestamp>.tar.gzbefore recreating the container. The last three snapshots stay on disk. If the new image fails to come up, the web UI shows the snapshot path so you can SSH in and run./nmemctl import <path> --forceto restore the previous state.
./nmemctl auto-update status # current state, last pull, retained snapshots
./nmemctl auto-update rotate # rotate the updater token
./nmemctl auto-update upgrade # bump the companion container's image
./nmemctl auto-update disable # remove the companion container; keep the snapshotsFull operator notes
For the complete operator contract — every environment variable, security hardening details, the device-identity invariant, troubleshooting recipes — see community/docker/README.md in the community repository.
Related
- Installation — The desktop app path, which is what most users want.
- Linux Server Deployment —
.debinstall with a systemd unit, for Linux servers where you'd prefer apt-managed updates over containers. - Back Up, Export, and Import — Application-level export format and restore flow.
- Remote Access — Access Anywhere keys, API access, multi-device sync. Applies identically to the Docker deployment.
- LLM Providers — Required for background intelligence (daily briefings, insights, graph enrichment).
- Troubleshooting — Common operator issues, including the headless container path.