The fastest way to get Nerve running:
git clone https://github.com/ClickHouse/nerve.git nerve
cd nerve
pip install -e . # or: uv pip install -e .
cd web && npm install && npm run build && cd ..
nerve init # Interactive wizard — handles everything
nerve startThe nerve init wizard walks you through deployment, mode selection, API keys, workspace setup, and cron configuration. Nothing is written until you confirm.
- Python 3.12+
- Node.js 18+ (for web UI build)
- Anthropic API key or Claude subscription (via CLIProxyAPI proxy)
- Docker with Compose V2 (
docker compose) - Anthropic API key or Claude subscription (via CLIProxyAPI proxy)
git clone https://github.com/ClickHouse/nerve.git nerve
cd nerve
# Create virtual environment
uv venv
source .venv/bin/activate
# Install Nerve
uv pip install -e .
# Build web UI
cd web && npm install && npm run build && cd ..
# Run the setup wizard
nerve initgit clone https://github.com/ClickHouse/nerve.git nerve
cd nerve
pip install -e . # Needed to run the wizard on the host
nerve init # Choose "docker" at the deployment stepThe wizard handles everything: generates Dockerfile + docker-compose.yml, builds the image, starts the container, and continues setup inside it. You never write Docker files manually.
What happens under the hood:
nerve initasks "How do you want to run Nerve?" → choosedocker- Generates
Dockerfile,docker-compose.yml,docker-entrypoint.sh,.dockerignore - Runs
docker compose build - Runs
docker compose run nerve nerve init --inside-docker(seamless transition) - The rest of the wizard (mode, API keys, workspace, crons) continues inside the container
- After setup, Nerve starts automatically inside the container
Subsequent starts:
docker compose up # Start
docker compose up -d # Start in background
docker compose down # Stop
docker compose logs -f # Follow logsNon-interactive Docker setup (CI / automation):
cp .env.example .env
# Edit .env with your ANTHROPIC_API_KEY (or set NERVE_USE_PROXY=1)
docker compose upThe entrypoint runs nerve init --if-needed --non-interactive before starting, using environment variables from .env.
Using CLIProxyAPI instead of an API key:
Set NERVE_USE_PROXY=1 in your environment (no ANTHROPIC_API_KEY required). The proxy authenticates via Claude Code's OAuth — requires a Claude Max/Pro subscription at claude.ai. See config.md for details.
Volumes:
| Mount | Purpose |
|---|---|
.:/nerve |
Application code (bind mount) |
nerve-data:/root/.nerve |
Databases, logs, PID, sessions |
nerve-workspace:/root/nerve-workspace |
Workspace files (SOUL.md, tasks, skills) |
You can re-run nerve init at any time — it's safe on existing installations.
What gets overwritten:
config.yamlandconfig.local.yaml— regenerated from your choices~/.nerve/cron/system.yaml— regenerated (picks up new built-in cron prompts from Nerve updates)
What's preserved:
- All workspace files (
SOUL.md,IDENTITY.md,USER.md,MEMORY.md, skills, tasks, etc.) ~/.nerve/cron/jobs.yaml— your custom crons are never touched~/.nerve/nerve.dband~/.nerve/memu.sqlite— databases are preserved
When you run nerve init on an existing install, it prompts: "Nerve is already configured. Re-run setup?" The --if-needed flag skips setup entirely if already configured (useful in Docker entrypoints).
When deploying via Docker, nerve init needs to pass authentication credentials from the host into the container. It resolves credentials using a priority waterfall:
- macOS Keychain —
Claude Code-credentials— extracts OAuth access token from the JSON stored by Claude Code - macOS Keychain —
Claude Code— raw API key CLAUDE_CODE_OAUTH_TOKENenv var~/.claude/.credentials.jsonfile — where Linux stores Claude credentialsANTHROPIC_API_KEYenv var
The first match wins. The extracted credential is passed to docker compose run as an environment variable, then written into config.local.yaml inside the container during setup.
Note: The
~/.claudedirectory is NOT mounted into the container. Instead, credentials are resolved on the host and injected via environment variables. This avoids file permission issues and macOS Keychain access from within Docker.
The wizard handles all of this automatically, but you can also configure manually:
# Create secrets file (gitignored)
cat > config.local.yaml << 'EOF'
anthropic_api_key: sk-ant-...
openai_api_key: sk-... # Optional — enables vector-based memory search
telegram:
bot_token: "123456:ABC..."
auth:
password_hash: "$2b$12$..." # Generate below
jwt_secret: "..." # Generate below
EOF# Password hash
python -c "import bcrypt; print(bcrypt.hashpw(b'yourpassword', bcrypt.gensalt()).decode())"
# JWT secret
python -c "import secrets; print(secrets.token_hex(32))"nerve doctor # Verify everything is set up
nerve start # Start the server
# Open http://localhost:8900# Install mkcert
sudo apt install libnss3-tools
curl -L https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-arm64 -o mkcert
chmod +x mkcert && sudo mv mkcert /usr/local/bin/
# Create certificates
mkdir -p ~/.nerve/certs
mkcert -install
mkcert -cert-file ~/.nerve/certs/cert.pem -key-file ~/.nerve/certs/key.pem \
localhost 127.0.0.1 "$(hostname)" "$(hostname).local"Update config.yaml:
gateway:
ssl:
cert: ~/.nerve/certs/cert.pem
key: ~/.nerve/certs/key.pem# On Pi: copy the CA cert
cat "$(mkcert -CAROOT)/rootCA.pem"
# On Mac: save to file and trust
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain rootCA.pemNerve has built-in daemon management. No systemd required for basic usage.
nerve start # Start as background daemon
nerve stop # Stop the daemon (graceful, 15s timeout)
nerve restart # Stop + start
nerve status # Show PID, memory, uptime
nerve status -f # Show status then tail logs
nerve logs # Tail the daemon log
nerve start -f # Run in foreground (for debugging)PID file: ~/.nerve/nerve.pid
Log file: ~/.nerve/nerve.log
For auto-start on boot, create /etc/systemd/system/nerve.service:
[Unit]
Description=Nerve Personal AI Assistant
After=network.target
[Service]
Type=simple
User=YOUR_USER
WorkingDirectory=/home/YOUR_USER/nerve
Environment=PATH=/home/YOUR_USER/nerve/.venv/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/home/YOUR_USER/nerve/.venv/bin/nerve start --foreground
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetNote: Use --foreground with systemd since it manages the process lifecycle.
sudo systemctl daemon-reload
sudo systemctl enable nerve
sudo systemctl start nerve
# Check status
sudo systemctl status nerve
journalctl -u nerve -fNerve auto-migrates the SQLite database on startup. If a migration fails or the schema gets out of sync, you can inspect and fix it manually.
Check current schema version:
sqlite3 ~/.nerve/nerve.db "SELECT version FROM schema_version"Verify sessions table columns:
sqlite3 ~/.nerve/nerve.db "PRAGMA table_info(sessions)"Expected columns (as of V3): id, title, created_at, updated_at, source, metadata, status, sdk_session_id, parent_session_id, forked_from_message, connected_at, last_activity_at, archived_at, message_count, total_cost_usd, last_memorized_at.
Add a missing column manually:
sqlite3 ~/.nerve/nerve.db "ALTER TABLE sessions ADD COLUMN last_memorized_at TEXT"After any manual schema fix, restart Nerve: nerve restart.