Skip to content

Scheduled indexing (Linux)

Most users don't need this

obsidian-brain server auto-watches the vault when running from any MCP client — the index stays live as you edit, no scheduled job required. Use scheduled indexing only when you can't keep server running continuously, or when the watcher can't observe changes reliably (SMB, some NFS setups).

Two approaches for keeping the index fresh on Linux outside of an active MCP client session: a persistent watch daemon via a systemd user service, or a timer-driven index job that runs every 30 minutes. No root, no system-wide install — everything lives under ~/.config/systemd/user/.

obsidian-brain server already watches the vault whenever it's running, so most users who launch obsidian-brain from an MCP client don't need anything here. If you want a dedicated daemon that keeps the index fresh even when no MCP client is running, use the watcher service below. If your vault lives somewhere inotify can't observe (SMB, some NFS setups) or you prefer a fixed schedule, skip to the timer fallback.

Run the watch subcommand as a systemd user service — useful on a server, a headless box, or any machine where you want the index live even when no MCP client is running. Create ~/.config/systemd/user/obsidian-brain-watch.service:

[Unit]
Description=obsidian-brain live vault watcher
After=network.target

[Service]
Type=simple
Environment=VAULT_PATH=/absolute/path/to/your/vault
ExecStart=/usr/bin/obsidian-brain watch
Restart=always
RestartSec=5
StandardOutput=append:%h/.local/state/obsidian-brain-watch.log
StandardError=append:%h/.local/state/obsidian-brain-watch.err

[Install]
WantedBy=default.target

Then:

mkdir -p ~/.local/state
systemctl --user daemon-reload
systemctl --user enable --now obsidian-brain-watch.service
systemctl --user status obsidian-brain-watch.service

Adjust /usr/bin/obsidian-brain to match which obsidian-brain. If you want the service to survive logout, run sudo loginctl enable-linger $USER once. The timer-based flow below is the fallback if you set OBSIDIAN_BRAIN_NO_WATCH=1 or your vault lives on a filesystem where inotify doesn't fire (SMB, some NFS setups).

What it does (scheduled fallback)

A systemd user timer runs obsidian-brain index every 30 minutes as your user account. Because everything lives under ~/.config/systemd/user/, there is no sudo required and nothing is installed system-wide. The timer is tied to your login session and will stop when you log out — unless you enable linger (see the optional step below).

Prerequisites

  • npm install -g obsidian-brain — puts the obsidian-brain binary on your PATH. Confirm with which obsidian-brain and note the path (commonly /usr/bin/obsidian-brain, /usr/local/bin/obsidian-brain, or an nvm-scoped variant).
  • You know the absolute path to your Obsidian vault (VAULT_PATH).
  • A systemd-based Linux distribution (most modern distros: Ubuntu, Debian, Fedora, Arch, openSUSE, etc.).

If you're running from a local source clone instead of npm, see the source install variant at the bottom of this file.

1. Create the service unit

Create ~/.config/systemd/user/obsidian-brain.service:

[Unit]
Description=obsidian-brain vault reindex
After=network.target

[Service]
Type=oneshot
Environment=VAULT_PATH=/absolute/path/to/your/vault
ExecStart=/usr/bin/obsidian-brain index
StandardOutput=append:%h/.local/state/obsidian-brain-index.log
StandardError=append:%h/.local/state/obsidian-brain-index.err

Notes:

  • %h is expanded by systemd to your $HOME directory.
  • Adjust /usr/bin/obsidian-brain to match wherever which obsidian-brain reports on your system. If you installed node via nvm, the binary will be under /absolute/path/to/.nvm/versions/node/vXX.Y.Z/bin/obsidian-brain — use that full path; systemd does not expand ~ inside ExecStart.
  • Type=oneshot is correct here: the reindex runs to completion and exits; the timer will trigger the next run.

2. Create the timer unit

Create ~/.config/systemd/user/obsidian-brain.timer:

[Unit]
Description=Run obsidian-brain reindex every 30 minutes

[Timer]
OnBootSec=5min
OnUnitActiveSec=30min
Unit=obsidian-brain.service
Persistent=true

[Install]
WantedBy=timers.target

What these fields mean:

  • OnBootSec=5min — wait 5 minutes after the user manager starts before the first run, so login isn't slowed by indexing.
  • OnUnitActiveSec=30min — after each run finishes, wait 30 minutes before firing the next one.
  • Persistent=true — if the machine was off or asleep when a run was supposed to fire, systemd will run it as soon as possible after resume / boot. Without this, missed runs are simply skipped.

3. Enable and start

mkdir -p ~/.local/state
systemctl --user daemon-reload
systemctl --user enable --now obsidian-brain.timer
systemctl --user list-timers --all | grep obsidian-brain

The last command should print a row showing obsidian-brain.timer along with its NEXT and LEFT columns.

4. Optional — survive logout

By default, user services stop when you log out. On a desktop that's usually fine. On a server, or if you want indexing to continue after logging out of a graphical session, enable linger for your user:

sudo loginctl enable-linger $USER

This is the one step in the whole guide that needs sudo, and it's optional.

5. Check status and logs

systemctl --user status obsidian-brain.service
systemctl --user status obsidian-brain.timer
journalctl --user -u obsidian-brain.service -n 50
tail -f ~/.local/state/obsidian-brain-index.log

journalctl shows systemd's view of every execution (exit codes, timings). The log file captures the indexer's stdout; the .err file captures stderr.

6. Trigger a run manually

Useful for verifying the unit works without waiting for the timer:

systemctl --user start obsidian-brain.service

Then check the log file or journalctl to confirm it ran.

7. Adjust the interval

Edit ~/.config/systemd/user/obsidian-brain.timer, change OnUnitActiveSec to whatever you want (for example 15min, 1h, 6h), then reload:

systemctl --user daemon-reload
systemctl --user restart obsidian-brain.timer

8. Disable and uninstall

systemctl --user disable --now obsidian-brain.timer
rm ~/.config/systemd/user/obsidian-brain.service
rm ~/.config/systemd/user/obsidian-brain.timer
systemctl --user daemon-reload

Log files under ~/.local/state/ are left in place — remove them manually if you want.

9. Troubleshooting

  • status=203/EXEC — systemd could not execute the binary. Usually the path in ExecStart is wrong. Run which obsidian-brain and put that exact absolute path into the unit file, then systemctl --user daemon-reload and try again.
  • nvm users — always use the fully expanded absolute path (for example /absolute/path/to/.nvm/versions/node/v20.11.1/bin/obsidian-brain). systemd does not expand ~ inside ExecStart, so paths like ~/.nvm/... will fail with 203/EXEC.
  • Timer not firingsystemctl --user list-timers must show obsidian-brain.timer with a real NEXT time. If it doesn't appear, re-run systemctl --user enable --now obsidian-brain.timer. If NEXT is n/a, double-check the [Timer] section syntax.
  • better-sqlite3 native-module errors — the native binding must be built against the same node version systemd will invoke. Rebuild it with that node on your PATH:
PATH=/absolute/path/to/node/bin:$PATH npm rebuild -g better-sqlite3
  • Environment looks empty — systemd user services start with a minimal environment. If your indexer needs extra variables beyond VAULT_PATH, add more Environment=KEY=VALUE lines to the [Service] section, one per line.

Variant: running from a local clone

If you're developing obsidian-brain from a source clone rather than the npm package, your [Service] section needs both a WorkingDirectory and a node dist/cli/index.js index invocation:

[Service]
Type=oneshot
WorkingDirectory=/absolute/path/to/obsidian-brain
Environment=VAULT_PATH=/absolute/path/to/your/vault
ExecStart=/usr/bin/node dist/cli/index.js index
StandardOutput=append:%h/.local/state/obsidian-brain-index.log
StandardError=append:%h/.local/state/obsidian-brain-index.err

Everything else (timer unit, enable/start flow, troubleshooting) stays identical.