Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ docker run --rm -p 8080:8080 \
ghcr.io/savioruz/memayu:latest
```

For a persistent, restart-on-failure deployment with a data volume and a
readiness healthcheck, see [`contrib/docker-compose.yml`](contrib/docker-compose.yml):

```bash
docker compose up -d
docker compose logs -f
```

The server exposes an unauthenticated readiness endpoint at
`GET /api/health`:

Expand Down Expand Up @@ -88,6 +96,42 @@ ExecStartPost=/bin/sh -c 'for i in $(seq 1 30); do \
sleep 1; done; exit 1'
```

### Running as a service (systemd)

To keep `memayu serve` running persistently (and restart it automatically after
a reboot or crash), ship the example unit file:

```bash
sudo cp contrib/systemd/memayu.service /etc/systemd/system/memayu.service
```

Read the comments in that file first — it runs as an unprivileged `memayu` user
and expects a `config.toml` in that user's config directory. Create the service
user, data directory, and a valid `config.toml` (e.g. from `memayu setup` run as
that user), then:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now memayu
```

Useful commands:

```bash
systemctl status memayu # status plus the last few log lines
journalctl -u memayu -f # follow the service logs
systemctl restart memayu # apply changes after editing config.toml
systemctl disable memayu # stop auto-starting on boot
```

The service reads the same on-disk `config.toml` that `memayu serve` uses
(`$MEMAYU_CONFIG`, else `$XDG_CONFIG_HOME/memayu/config.toml`, else
`~/.config/memayu/config.toml`) — no `.env` file is required. If you prefer env
overrides, add them with `Environment=` lines in the unit or a systemd
`EnvironmentFile=`.

For help diagnosing installs, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md).

## Usage

### CLI
Expand Down
137 changes: 137 additions & 0 deletions TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Troubleshooting

Common issues when running memayu self-hosted, and how to diagnose them. The
quickest first step for almost any "it's not working" problem is `memayu doctor`,
which checks config validity, storage, and LLM/embedder connectivity in one
command:

```bash
memayu doctor
```

> **Missing config?** `memayu doctor` exits nonzero if the config can't load at
> all. Run `memayu config check` / `memayu config show` (below) to see what's
> wrong.

## Does the config load?

`memayu` reads an on-disk `config.toml` (created by `memayu setup`), then applies
any `MEMAYU_*` environment overrides. The file is resolved in this order:

1. `$MEMAYU_CONFIG`
2. `$XDG_CONFIG_HOME/memayu/config.toml`
3. `~/.config/memayu/config.toml`

If you run memayu from a service (systemd), make sure the service user can read
that file.

```bash
memayu config show # effective config (secrets redacted)
memayu config check # validates; exits 1 listing any problems
```

If `memayu config check` fails, fix the reported fields (e.g. a missing
`llm.base_url` or `embedder.model`) in `config.toml` or via `MEMAYU_*` env.

A missing/empty model id for a local embedder, or no dimension, produces errors
like:

```
no embedding dimension available for storage; run `memayu setup` or set MEMAYU_EMBEDDER_DIM
```

Set `MEMAYU_EMBEDDER_DIM` (or run `memayu setup` for a remote embedder) to record
the dimension.

## Is the server actually up and usable?

`memayu serve` exposes an unauthenticated readiness endpoint:

```bash
curl -s http://localhost:18080/api/health
```

- `{"status":"setup_required"}` — the process is listening but first-run setup is
incomplete (no admin account and/or no provider config). Complete setup first.
- `{"status":"ready"}` — up and usable.

If `curl` can't connect, the server isn't listening on that address/port. Check
the bind address and port in `config.toml` (`[server]`), and that no firewall or
another process is blocking it.

## Provider problems (LLM / embedder reachable through a proxy)

`memayu doctor` verifies providers with a **real test call** (a completion for the
LLM, an embedding for the embedder), not a `/models` listing. So a provider behind
a proxy that never exposes `/models`, or renames models, still reports healthy as
long as the real call works.

If doctor reports a provider error, it prints the HTTP status or failure reason:

- **401** — the API key was rejected by the provider. Check `MEMAYU_*_API_KEY` /
`api_key` in `config.toml`, and that the key is valid for the configured
`base_url`.
- **500 / 5xx** — the provider (or proxy) returned a server error. Check the
provider's own logs and that the endpoint/model combination is valid.
- **connection / timeout** — host unreachable, TLS error, or the URL is wrong.
Confirm `base_url` is an `http(s)://` URL reachable from the memayu host, and
that a proxy/gateway in front of the provider is up.

A dimension mismatch (configured `MEMAYU_EMBEDDER_DIM` differs from what's stored
or produced) fails doctor and will error on writes:

```
configured dimension 8 differs from stored 3
```

Set the correct consistent dimension everywhere.

## Port already in use

```bash
lsof -i :18080 # macOS / Linux
ss -tlnp | grep 18080 # Linux
```

Find and stop the process, or change `port` in the `[server]` section (and any
Docker `-p` mapping). After editing config, restart memayu.

## Reset a forgotten admin password

The web dashboard has an **Account** page (`/accounts`) for changing the password
and email when you're logged in. If the admin password is forgotten entirely and
you can't log in, reset it from the terminal (this works for both libsql and
Postgres backends):

```bash
memayu reset-password 'NewHorse-Staple-99!'
```

This bypasses the login gate, validates the new password against the same policy
the UI enforces, and writes a fresh password hash. Log in with the new password
and rotate it from `/accounts` if you like.

## API key / 401 on API calls

API routes require either an **`x-api-key`** header or session cookie. If a call
returns 401:

- Confirm the API key is valid. Generate one from the dashboard's **API Keys**
page or via `memayu setup`. Keys are stored as a hash; the raw `mmyu_…` value is
shown once.
- Confirm you're sending it as `x-api-key: mmyu_...`.
- `GET /api/health` is the one endpoint that needs **no** auth.

## Where are logs?

- **Terminal:** `memayu` prints diagnostics (config, embedder dimension, listening
address) to stdout/stderr.
- **systemd:** `journalctl -u memayu -f`.
- **Docker:** `docker logs <container>`.
- Per-request HTTP logging appears in the dashboard's **Requests** page.

## Still stuck

Open an issue at https://github.com/savioruz/memayu/issues and include:
the output of `memayu doctor`, `memayu config show`, your OS/arch/build, and — for
a service — `systemctl status memayu` plus recent `journalctl -u memayu` lines.
60 changes: 60 additions & 0 deletions contrib/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Memayu — Docker Compose example
#
# Runs memayu serve in a container with restart-on-failure/reboot and a
# non-destructive `/api/health` healthcheck, mirroring the Dockerfile and the
# README "Docker" example.
#
# docker compose up -d
# docker compose logs -f
# docker compose ps
#
# The container stores its database at /data/memayu.db (see the Dockerfile's
# MEMAYU_LIBSQL_PATH), which is kept in the `memayu-data` named volume below so
# the store survives container recreation and upgrades.
#
# Configure via the `environment:` block. Everything is optional except the
# provider keys/model you plan to use; the README uses OpenAI-compatible
# defaults. For a local (on-device) embedder set instead:
#
# MEMAYU_EMBEDDER_BACKEND: local
# MEMAYU_EMBEDDER_MODEL: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
# MEMAYU_EMBEDDER_DIM: "768"
#
# The port on the left of `8080:8080` is the host port; change it if occupied.
#
# Healthcheck: the image is alpine, so `wget` (busybox) is available. The service
# is marked healthy only once /api/health returns {"status":"ready"} (i.e. first-
# run setup has completed). Before that it reports "unhealthy" — complete setup
# at http://localhost:8080/setup, then the container transitions to healthy.

services:
memayu:
image: ghcr.io/savioruz/memayu:latest
container_name: memayu
restart: unless-stopped
ports:
- "8080:8080"
environment:
# Storage (libsql embedded database under the named volume below).
MEMAYU_STORAGE_BACKEND: libsql
MEMAYU_LIBSQL_PATH: /data/memayu.db
# Provider config (OpenAI-compatible; adjust to your provider/proxy).
# MEMAYU_LLM_BASE_URL: https://api.openai.com/v1
# MEMAYU_LLM_API_KEY: sk-...
# MEMAYU_LLM_MODEL: gpt-4o-mini
# MEMAYU_EMBEDDER_BASE_URL: https://api.openai.com/v1
# MEMAYU_EMBEDDER_API_KEY: sk-...
# MEMAYU_EMBEDDER_MODEL: text-embedding-3-small
volumes:
- memayu-data:/data
healthcheck:
test:
- CMD-SHELL
- wget -q -O /dev/stdout http://127.0.0.1:8080/api/health | grep -q '"status":"ready"'
interval: 10s
timeout: 3s
start_period: 10s
retries: 5

volumes:
memayu-data:
58 changes: 58 additions & 0 deletions contrib/systemd/memayu.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#
# Memayu memory server — systemd unit example
#
# Place this file at /etc/systemd/system/memayu.service, adjust the paths and
# user to match how you installed memayu, then enable it:
#
# sudo systemctl daemon-reload
# sudo systemctl enable --now memayu
#
# Useful commands:
# systemctl status memayu # status incl. the last few log lines
# journalctl -u memayu -f # follow the service logs
# systemctl restart memayu # restart after editing config
# systemctl disable memayu # stop auto-starting on boot
#
# Configuration comes from memayu's on-disk config.toml, exactly as when you run
# `memayu serve` in a shell. memayu resolves it in this order (see
# memayu-config::config_path):
# 1. $MEMAYU_CONFIG (an explicit path, if set below)
# 2. $XDG_CONFIG_HOME/memayu/config.toml
# 3. ~/.config/memayu/config.toml
# Run `memayu setup` once as the service user (or copy a config.toml into place)
# so a valid file exists before the service starts.
#
# This unit runs as an unprivileged `memayu` user and stores its database under
# /var/lib/memayu. Create that directory, give the user ownership, and make sure
# the config.toml is readable by them:
#
# sudo mkdir -p /var/lib/memayu
# sudo useradd --system --home /var/lib/memayu memayu (if not already present)
# sudo chown -R memayu:memayu /var/lib/memayu
# sudo -u memayu mkdir -p /home/memayu/.config/memayu
# sudo -u memayu cp ./memayu-config.toml /home/memayu/.config/memayu/config.toml
# sudo -u memayu chmod 600 /home/memayu/.config/memayu/config.toml
# (or copy your existing machine's config.toml there)

[Unit]
Description=Memayu memory server
After=network.target
Wants=network-online.target

[Service]
Type=simple
# Adjust this path if you installed memayu somewhere other than /usr/local/bin.
ExecStart=/usr/local/bin/memayu serve
Restart=on-failure
RestartSec=5
User=memayu
Group=memayu
WorkingDirectory=/var/lib/memayu
# Optional: point at an explicit config.toml instead of the XDG default above.
#Environment=MEMAYU_CONFIG=/var/lib/memayu/config.toml
# Absolute libsql path so the database always lands in the working dir. Note that
# this overrides whatever libsql_path your config.toml sets.
Environment=MEMAYU_LIBSQL_PATH=/var/lib/memayu/memayu.db

[Install]
WantedBy=multi-user.target
Loading