Skip to content
Menu

Product

Solutions

Integrations

Developers

Language

Developers

Advanced setup and infrastructure

For when the server leaves your laptop. Each topic is a diagram or a table, the shortest correct config, and a link to the full document on GitHub.

Four places it can run

The binary is the same in all four. What changes is the bind address, and with it how much auth and encryption you owe.

Four deployment topologies side by side: laptop only with the server inside the laptop, a homelab box that two laptops connect to, a LAN server with a TLS shield in front of it, and a server reached through an outbound tunnel to a cloud edge.
Moving right adds reach, and every step to the right adds a requirement. There is no replication between servers in any of them: many machines reach one server.
TopologyBindAuthTLSRuns as
Laptop only127.0.0.1:49374None neededNosystemd user unit, launchd agent, or a container
Homelab box0.0.0.0:49374Bearer token and host allowlist, both requiredRecommendedDocker with a healthcheck, or the AUR system service
LAN with TLS0.0.0.0:49374 behind a proxyRoot token, plus a user and an API key per personYes. Caddy with its internal CA works without a domainDocker Compose with a Caddy sidecar
TunnelNo host port at allSame as the LAN serverYes, at Cloudflare’s edgeDocker Compose with a cloudflared sidecar

A laptop can skip HTTP entirely with ai-memory serve --transport stdio. For a homelab, the repository ships a bin/deploy script with compose and env templates. Follow the homelab deploy walkthrough.

TLS through a reverse proxy

ai-memory does not terminate TLS itself, by design. A bearer token authenticates a request. It does not encrypt it.

You can skip TLS when

  • the agent talks to it over stdio
  • the server is loopback-only, for one user, and nobody opens /web from another machine
  • it is local development or a one-off experiment

You need TLS when

  • accounts exist, because aim_ keys then cross the network
  • the server is bound beyond loopback
  • you open /web from a different machine
  • the server is reachable beyond the LAN
Two laptops connect over HTTPS to a reverse proxy. The proxy forwards plain HTTP to ai-memory. The proxy and ai-memory sit on the same host.
The proxy owns the certificate. ai-memory stays on plain HTTP behind it, reachable only over the Docker network or loopback.

For a public domain with ports 80 and 443 reachable. Caddy issues and renews the Let’s Encrypt certificate on its own. The repository has the full compose file as docker/compose.tls.caddy.yml.

Caddyfile
memory.example.com {
    reverse_proxy ai-memory:49374
}

Then tell the server

The allowlist must include the public hostname, or the DNS rebinding guard rejects the proxy’s requests. The secure cookie setting is required once people log in through a listener beyond loopback.

.env.production
AI_MEMORY_AUTH_TOKEN=...long-random-token-from-generate-auth-token...
AI_MEMORY_AUTH__SECURE_COOKIE=true
AI_MEMORY_ALLOWED_HOSTS=memory.example.com,localhost,127.0.0.1
AI_MEMORY_BIND=0.0.0.0:49374

And repoint the clients

The MCP URL ends in /mcp. The hook URL is the bare origin.

Terminal
ai-memory install-mcp   --client claude-code --apply \
    --server-url "https://memory.example.com/mcp" --auth-token "$AI_MEMORY_AUTH_TOKEN"
ai-memory install-hooks --agent  claude-code --apply \
    --server-url "https://memory.example.com" --auth-token "$AI_MEMORY_AUTH_TOKEN"

Read the full HTTPS guide, including subpaths and proxy timeouts for long bootstrap runs

Keep the server running

ai-memory does not restart itself. The service manager on each operating system does that.

For a single-user workstation. The AUR packages install the unit. It needs no sudo and keeps state in ~/.local/share/ai-memory. A user unit stops at logout unless you enable lingering.

Terminal
systemctl --user daemon-reload
systemctl --user enable --now ai-memory.service
systemctl --user status ai-memory.service
journalctl --user -u ai-memory.service -f

# keep it running after you log out
loginctl enable-linger "$USER"

systemd units in the install guidelaunchd in the macOS guideWinSW in the Windows guide

The data directory and backups

The wiki is the truth. It is plain markdown in a git repository, and the search index is built from it.

Reindex rebuilds pages, links and full-text search from the wiki. It does not bring back sessions, observations, handoffs, users, keys, audit rows or embeddings, so the database still belongs in your backup.

DirectoryHoldsKindBack it up?
wiki/Every page as markdown, in one git repositoryTruthYes. It is in the backup tarball, and you can also rsync it or git push it
raw/Immutable, sanitized transcript segments from managed launchesTruthYes, if you use ai-memory run. Copy it separately
db/memory.sqlite: full-text index, entities, embeddings, sessions, users, audit rowsMostly derivedYes. Pages, links and search rebuild from the wiki. Sessions, handoffs, users and keys live only here
models/The local embedding model, about 87 MBRebuildableNo. It downloads again, or you place the files yourself
logs/Rolling trace outputDisposableNo

Defaults: ~/.local/share/ai-memory on Linux, ~/Library/Application Support/ai-memory on macOS, %LOCALAPPDATA%\ai-memory on Windows, /data in the container. Override with AI_MEMORY_DATA_DIR.

Backup and restore

Backup takes a tar.gz archive from a running server. Restore goes from the archive into a stopped server, which is then started again.
Backup runs against a live server. Restore works directly on disk and refuses while any ai-memory process is alive.

Back up

It uses SQLite’s online backup API, so writes during the snapshot stay coherent. The tarball holds the wiki tree, the database snapshot and config.toml.

Terminal
# safe while the server runs
ai-memory backup --to /tmp/ai-memory-backup.tar.gz

Restore

--data-dir is the host-side path of the volume. The paths here are the ones from the deploy guide. Use yours.

Terminal
# Stop the server first.
docker compose -f ~/deploy/ai-memory/docker-compose.yml down
# Restore (sysinfo refuses if the container is still running).
ai-memory restore --from /tmp/ai-memory-backup.tar.gz --data-dir /var/opt/docker/utils/ai-memory/data --force
# Start back up.
docker compose -f ~/deploy/ai-memory/docker-compose.yml up -d

Read lifecycle operations: purge, rename, move, restore a page, reset

Routing and identity

A shared server has to answer two questions: which project does this session belong to, and who is asking.

The marker file

By default the project is the name of the current directory, in a workspace called default. Drop a .ai-memory.toml in any ancestor directory to change that. Hooks walk up from the working directory and use the first marker they find.

Work and personal

One marker per parent directory. Every repository below it lands in that workspace, with the directory name as the project.

.ai-memory.toml
# ~/projects/movvia/.ai-memory.toml
workspace = "movvia"

# ~/personal/.ai-memory.toml
workspace = "personal"

Mono-repo

A marker with a project pins every subdirectory to that one project. The closest marker wins.

.ai-memory.toml
# ~/projects/movvia/pe-portais/.ai-memory.toml
workspace = "movvia"
project = "pe-portais"

Git worktrees

Linked worktrees and subdirectories resolve to the main repository, even when the worktree lives outside it.

.ai-memory.toml
# ~/projects/.ai-memory.toml
workspace = "oss"
project_strategy = "repo-root"

The same file holds [capture] rules with ignore_paths, and ai-memory install-hooks --apply --capture-mode allowlist turns the marker into an opt-in: a repository without one emits no events. Native hooks enforce both. The Docker wrapper’s shell hooks do not. Read the marker file reference.

Auto-scope modes

An MCP call without an explicit project resolves through a “current project” pointer. The mode decides who shares that pointer. The server logs the live mode at startup.

ModeWhen to use it
per_actorThe default. Isolates parallel harnesses and separate people on one server.
per_sessionFor session-aware clients that forward the hook session id on every MCP request.
singleThe behaviour before v1.39. One slot for the whole process, last write wins. Unsafe on a shared server.
config.toml
[auto_scope]
mode = "per_actor"        # "per_actor" (default since v1.39) | "per_session" | "single"
session_ttl_secs = 3600   # TTL for per-key entries (default 1 h)
max_entries = 4096        # hard cap; oldest insertions evicted first

Read how auto-scope isolation works

SSO and OIDC

Each developer logs in once with an OIDC device flow against any standards-compliant issuer, such as Keycloak, Okta or Entra ID. The token then authenticates native lifecycle hooks and CLI commands when no static bearer token is set.

Terminal
ai-memory auth login oidc-device \
  --issuer "https://issuer.example.com/realms/team" \
  --client-id "ai-memory-cli"

Read the SSO and enterprise identity page

Offline, limits and upgrades

What a security review, a capacity plan and a maintenance window each need to know.

Air-gapped install

Binaries

Every release asset has a .sha256 file. Download on a connected machine, verify, carry it in. Releases carry checksums only, with no SLSA provenance or artifact attestation.

Source builds

SQLite is bundled and libgit2 is vendored, so the build needs a C toolchain and your crates mirror. cargo vendor works.

The embedding model

A default install fetches the model from Hugging Face on first start. For no outbound request at all, place model.safetensors, tokenizer.json and config.json in <data_dir>/models/all-MiniLM-L6-v2/ beforehand. Checksums are pinned in source.

The binary has no telemetry. The Docker wrapper checks Docker Hub for a newer image at most once every 24 hours, and AI_MEMORY_NO_VERSION_CHECK=1 turns that off. Updates in an air gap are manual, the same way as the install. Read the offline install page.

Capacity

Every write goes through one writer. The project measured where that stops, with cargo test -p ai-memory-store --test writer_throughput -- --ignored --nocapture.

Concurrent writersThroughputMean latency
142/s23.9 ms
8295/s3.4 ms
32698/s1.43 ms
128700/s1.43 ms
  • The ceiling is about 700 writes per second, reached near 32 writers.
  • The write queue is bounded at 1024. Past that, producers slow down and every write still lands.
  • Shell hooks give up on the server after 200 ms and spool the event locally, so a slow server does not stall your agent.
  • AI_MEMORY_HOOK_RATE_PER_SEC and AI_MEMORY_HOOK_RATE_BURST add an optional rate limit per actor and session. It is off by default.

Read the capacity section of the deploy guide

Upgrading

Terminal
ai-memory upgrade
  • With the Docker wrapper, this verifies and replaces the wrapper, pulls the image and restages hook scripts. A server on another host is upgraded separately.
  • Schema and wiki migrations run on start. They are forward-only: an older binary refuses to open a migrated directory, so run ai-memory backup first if you might roll back.

Read the 2.0 migration guide, including how to go back

Questions and answers

Does ai-memory need TLS?

Not on a single-user laptop bound to 127.0.0.1, and not over stdio. Add a TLS proxy when accounts exist, when the server is bound beyond loopback, when you open the web UI from another machine, or when it is reachable beyond the LAN. ai-memory does not terminate TLS itself.

What do I need to back up?

Run ai-memory backup, which is safe while the server runs. The tarball holds the wiki tree, a consistent SQLite snapshot and config.toml. The wiki is the source of truth. Pages, links and search can be rebuilt from it with ai-memory reindex, but sessions, handoffs, users and keys exist only in the database.

Does ai-memory support SSO?

It supports OIDC device auth for lifecycle hooks and CLI commands against any standards-compliant issuer. The server does not validate OIDC tokens itself, so putting the server API behind your identity provider requires an OIDC-aware gateway in front of it.

Can two servers share one data directory?

No. Run one server per data directory. Since 2.0 the server takes an exclusive lock on .serve.lock and a second server refuses to start.

Running it somewhere unusual?

Open an issue and describe your setup. The source, the docs and the tracker are all public.