Skip to content

Backup, upgrade, uninstall

Is it up?

Terminal window
curl -fsS http://127.0.0.1:18254/health
{"status":"ok","uptime":147,"version":"0.1.0","agents":6,"install":"a1b2c3d4e5f6","gateways":{"telegram:bot":"connected"}}

status is degraded — and the endpoint answers 503 — when a gateway is dead, disconnected or reconnecting, or when the SQLite WAL has grown past its threshold. issues then lists what is wrong. install is a hash of the data directory, so a CLI can tell it is talking to the right daemon when two installs on one machine compete for the port.

Check this, not just the service state. systemctl start returning 0 means a process was spawned, not that it is serving — a daemon that refuses its config exits after systemd reports success.

Terminal window
nouride status # agents, gateways, queue
nouride doctor # what is wrong, and what to do about it
nouride service status # which supervisor, and whether exiting gets you respawned

Start, stop, restart

Terminal window
# Linux / Raspberry Pi
sudo systemctl restart nouride
sudo systemctl stop nouride
sudo journalctl -u nouride -f
# macOS
launchctl unload -w ~/Library/LaunchAgents/tech.nouverse.nouride.plist # stop
launchctl load -w ~/Library/LaunchAgents/tech.nouverse.nouride.plist # start
# Docker
docker compose restart
docker compose logs -f

nouride restart works on any of them, but it works by asking the daemon to exit so its supervisor brings it back. With nothing supervising it, that is just a stop — so it refuses with a 409 rather than silently shutting you down. nouride service status answers for the process actually running and says plainly whether anything will respawn it.

Inside a container it reports docker and refuses for the same reason: the restart policy lives outside the container. Use docker restart <container>.

nouride stop is the one command that reaches the daemon without the control API — it reads the lock file and sends a signal, which is the path that still works when the HTTP server is the thing that has wedged.

Backup

The daemon snapshots its own database at boot and then daily, and keeps the newest backup_keep (seven by default) under data/backups/db/. Every snapshot is integrity-checked before it is kept, and one that fails the check is discarded rather than left where a restore might find it.

Terminal window
nouride db backup # take one now, before you do something risky
nouride db list # newest first
nouride db verify [file] # check a snapshot, or the live database
nouride db restore <file> # put one back — the daemon must be stopped

A snapshot is a single ordinary SQLite file with no sidecars, so recovery does not need these commands: cp it over data/nouride.db with the daemon stopped and delete nouride.db-wal and nouride.db-shm beside it. nouride db restore does exactly that, plus two things worth having — it refuses to run while the daemon is up, and it moves the database it replaces to nouride.db.replaced-<timestamp> so restoring the wrong snapshot is itself undoable.

Why not cp of the live file? Because it looks like it works. The database is three files, and everything committed since the last checkpoint is in nouride.db-wal. A copy of the main file alone opens without complaint and is missing the newest rows — a backup that exists, restores cleanly, and is silently behind. The snapshots use SQLite’s own VACUUM INTO, which is consistent against a running daemon.

For the whole install rather than the database alone, stop it first:

Terminal window
sudo systemctl stop nouride
sudo tar -czf nouride-backup-$(date +%F).tar.gz -C /srv/nouride .nouride .env
sudo systemctl start nouride

What is in there, ranked by how much it would hurt to lose:

agents/MEMORY.md above all. Months of accumulated identity, not in git, and the only thing here that cannot be rebuilt
data/conversations, audit log, standing grants, the secret store
config.toml and .envhow this install differs from a default one
workspace/whatever the agents have built. Output rather than identity — restore it if you want the work back, skip it if you only want the daemon back

The daemon also keeps dated copies of the persona packs under data/backups/agents/, seven by default. Those are kilobytes of Markdown and recovery is cp, deliberately — it does not need this code to still work. They cover an agent corrupting its own memory; they do not cover the directory being deleted, a bad checkout, or a disk.

Both sets live under data/, which is the directory every deployment shape mounts — so a snapshot lands wherever you already back up. That also means they are on the same disk as the thing they protect: copy them somewhere else for the failure that takes the disk with it.

Upgrade

Re-run the installer. It stops the daemon, replaces the binary, and starts it again. Your database, secrets, agent packs and workspace in .nouride/ are left alone.

Anyone mid-conversation is told. Stopping the daemon while an agent is working — an upgrade does exactly that — used to leave the person who asked waiting for a reply that never came. The daemon now sends one line into each affected conversation before it goes, and stops those turns rather than waiting on them:

The daemon is shutting down, so I stopped in the middle of this. Nothing came of this turn — send it again once I am back.

It cannot tell an upgrade from a shutdown, so the message does not guess. Turns that were not running are unaffected, and a stop with nothing in flight sends nothing.

If the daemon is killed outright instead — kill -9, the OOM killer, a power cut — no message can be sent at the time, so the next boot reports it to the same conversations once the gateways are up.

Terminal window
# Linux / Raspberry Pi
curl -fsSL https://get.nouride.com/install.sh | sudo sh
# macOS — replace the binary, then restart the job
cd ~/nouride && curl -fsSL https://get.nouride.com/latest/nouride-darwin-arm64.tar.gz | tar -xz
launchctl kickstart -k gui/$(id -u)/tech.nouverse.nouride
# Docker
docker compose pull && docker compose up -d

Pin a version with --version v0.1.0.

Migrations run at boot and are compiled into the binary, so there is no separate step and nothing to remember. Check afterwards:

Terminal window
nouride version
nouride doctor

Moving it to another machine

One directory, so this is a copy:

  1. Install the binary on the new box (the installer, with the daemon left stopped).
  2. Stop the old daemon.
  3. Copy .nouride/ and .env across, preserving ownership and modes — data/ holds 0600 files.
  4. Start the new one and run nouride doctor.
  5. Only then decommission the old one. Two daemons must not run against the same bot token — two Telegram bots polling one token get half the messages each.

workspace_dir can point at another disk if that is where the work belongs; nothing else has to move.

Renaming a gateway

The one operation that rewrites both the config and the database, so it refuses to run while the daemon is up:

Terminal window
sudo systemctl stop nouride
nouride gateway rename telegram:old telegram:new --apply
sudo systemctl start nouride

Without --apply it shows what it would change.

Uninstall

Terminal window
# Linux / Raspberry Pi
sudo systemctl disable --now nouride
sudo rm /etc/systemd/system/nouride.service /usr/local/bin/nouride
sudo systemctl daemon-reload
sudo rm -rf /srv/nouride # this deletes your agents and conversations
# macOS
launchctl unload -w ~/Library/LaunchAgents/tech.nouverse.nouride.plist
rm ~/Library/LaunchAgents/tech.nouverse.nouride.plist
rm -rf ~/nouride
# Docker
docker compose down
rm -rf ./nouride

nouride service uninstall prints the right commands for whichever supervisor you are on.

Disk, and what grows

.nouride/workspace/ is the only part that grows without bound — it is whatever the agents produce. Everything else ages out on the schedule in [retention]: audit rows at 30 days, job runs at 14, attachments and tool output on their own clocks.

The daemon watches its own disk: [health] disk_warn_threshold_mb (1 GB) raises an alert rather than only logging, and /health reports degraded once the SQLite WAL passes wal_warn_threshold_mb (100 MB).

Alerts

Set these before you walk away from it:

[alerts]
gateway = "telegram:nouva"
chat_id = "123456789"

Without them a dead gateway is only a line in the log, so you find out when you notice the agent has gone quiet — hours later, looking like a different problem. Boot warns when chat gateways are enabled and this is empty.