Nerve ships a built-in updater that pulls the latest published release from GitHub, rebuilds, restarts the service, and verifies health — all in one command.
Before using the updater, make sure this checkout has an HTTPS GitHub origin, for example:
git remote -vorigin should point to https://github.com/<owner>/<repo>.git. If it does not, fix it first:
git remote set-url origin https://github.com/<owner>/<repo>.gitnpm run update -- --yesThis will:
- Check prerequisites (git, Node.js, npm, and an HTTPS GitHub
originremote) - Resolve the latest published GitHub release (fallback: latest semver tag)
- Snapshot the current state for rollback
git fetch --tags && git checkout <tag>npm install && npm run build && npm run build:server- Restart the systemd/launchd service
- Verify
/healthand/api/versionmatch the target
| Flag | Description |
|---|---|
--version <vX.Y.Z> |
Pin to a specific version instead of latest |
--yes, -y |
Skip the confirmation prompt |
--dry-run |
Show what would happen without making changes |
--verbose, -v |
Extra logging (git commands, service detection) |
--rollback |
Restore the last-known-good snapshot |
--no-restart |
Skip service restart and health checks |
--help, -h |
Show help |
# Preview what an update would do
npm run update -- --dry-run
# Update to a specific version
npm run update -- --version v1.4.0 --yes
# Rollback to the previous version
npm run update -- --rollback
# Update without restarting (e.g. to restart manually)
npm run update -- --yes --no-restart| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Already up to date |
| 10 | Preflight failure (missing git/node/npm) |
| 20 | Version resolution failure (release/tag not found) |
| 40 | Build failure (npm install or build step) |
| 50 | Service restart failure |
| 60 | Health check failure (service unhealthy or version mismatch) |
| 70 | Rollback failure (critical — manual intervention needed) |
| 80 | Lock acquisition failure (another update is running) |
lock → preflight → resolve → confirm → snapshot → git checkout
→ npm install + build → restart → health check → done
Each stage has a dedicated exit code. If any stage after snapshot fails, the updater attempts an automatic rollback.
Before making changes, the updater saves:
- The current git ref (commit hash)
- The current version from
package.json - A SHA-256 hash of
.env - A timestamped backup of
.env
Snapshots are stored in ~/.nerve/updater/. The .env file is never overwritten during an update — only backed up.
Rollback restores the snapshot ref, cleans node_modules, rebuilds, and restarts the service. It runs automatically on build/restart/health failures, or manually via --rollback.
The rollback flow:
git checkout --force <snapshot-ref>- Remove
node_modules(clean slate) npm install && npm run build && npm run build:server- Restart and verify the service
After restart, the updater polls:
GET /health— must return 2xxGET /api/version— must report the target version
Retries with exponential backoff (2s, 4s, 8s) up to a 60-second deadline. If the version doesn't match, the updater assumes the old process is still serving and triggers rollback.
A PID-based lock file prevents concurrent updates. The lock is acquired with wx (exclusive create) and released on exit. If a lock is stale (the PID no longer exists), it's automatically cleaned up.
The updater auto-detects the service manager:
- systemd —
systemctl restart nerve - launchd —
launchctl kickstart -k
If no service manager is found, the updater skips restart and prints manual start instructions.
| Path | Purpose |
|---|---|
~/.nerve/updater/last-good.json |
Last-known-good snapshot |
~/.nerve/updater/last-run.json |
Result of the most recent update attempt |
~/.nerve/updater/snapshots/<timestamp>/.env |
Backed-up .env files |
~/.nerve/updater/nerve-update.lock |
PID lock file |
On first start after upgrading to 1.5.0, Nerve automatically migrates legacy Kanban runtime data from:
server-dist/data/kanbanserver/data/kanban
into the canonical runtime location:
${NERVE_DATA_DIR:-~/.nerve}/kanban
What to do:
- Let the first post-upgrade start complete before judging the migration
- Update backup scripts to follow
${NERVE_DATA_DIR:-~/.nerve}/kanban - Do not keep writing to the old
server-distorserverdata paths after upgrade
The updater resolves versions from GitHub Releases first. If release lookup fails (network/rate limits), it falls back to semver tags. If both sources fail, it exits with code 20.
Fix: Verify remote/release access and tags:
git remote -v
git remote set-url origin https://github.com/<owner>/<repo>.git
git fetch --tags origin
curl -sSf https://api.github.com/repos/<owner>/<repo>/releases/latest | jq .tag_nameAnother update process is running, or a stale lock file exists.
Fix: Check if an update is actually running:
cat ~/.nerve/updater/nerve-update.lock # Shows the PID
ps -p <pid> # Check if it's aliveIf the process is gone, the lock is stale — delete it:
rm ~/.nerve/updater/nerve-update.lockThe service restarted but /api/version reports the old version.
Causes:
- The old process didn't shut down cleanly (port still bound)
- systemd started the service before the build finished
Fix: Restart manually and check:
systemctl restart nerve
curl http://127.0.0.1:3080/api/versionnpm install or npm run build failed on the new version.
Fix: The updater will attempt automatic rollback. If rollback also fails (exit 70), restore manually:
cat ~/.nerve/updater/last-good.json # Get the snapshot ref
git checkout --force <ref>
npm install && npm run build && npm run build:server
systemctl restart nerve