Running locally instead? See INSTALL.md. Want the fast path? See LAZYDEPLOY.md.
Prepare · Install · Configure · Services · Caddy · Manage · Update · Troubleshooting
This guide gets NUTbits running on a Linux VPS with HTTPS via Caddy. For service management details (start/stop, auto-start, removal), see SERVICE.md.
Architecture:
- Backend API on
127.0.0.1:3338 - GUI server on
127.0.0.1:8080 - Caddy terminates TLS and proxies both to the public domain
sudo apt update && sudo apt install -y curl git build-essentialInstall Node.js 20:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt install -y nodejsgit clone https://github.com/DoktorShift/nutbits.git ~/nutbits
cd ~/nutbits
npm install
npm --prefix gui install
npm install better-sqlite3cp .env.example .env
nano .envMinimum .env for a VPS:
NUTBITS_MINT_URL=https://mint.minibits.cash/Bitcoin
NUTBITS_STATE_PASSPHRASE=replace-with-a-strong-secret
NUTBITS_STATE_BACKEND=sqlite
NUTBITS_SQLITE_PATH=./nutbits_state.db
NUTBITS_RELAYS=wss://relay.getalby.com/v1,wss://relay.8333.space
NUTBITS_API_ENABLED=true
NUTBITS_API_PORT=3338
NUTBITS_API_TOKEN=replace-with-a-long-random-token
NUTBITS_GUI_HOST=127.0.0.1
NUTBITS_GUI_PORT=8080Sanity check — before moving on, verify .env is valid:
npm startYou should see a clean boot with mint info and relay status. Stop it with Ctrl+C.
If you see Missing NUTBITS_STATE_PASSPHRASE, your .env is not being read. Recreate it:
cp .env.example .env && nano .envnpm run service:linux
loginctl enable-linger "$USER"This builds the GUI, creates two user services, and starts them:
nutbits-backend.service— the NWC backendnutbits-gui.service— the web GUI
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddyEdit /etc/caddy/Caddyfile — replace nutbits.example.com with your domain:
nutbits.example.com {
encode gzip zstd
@api path /api/* /connect /app-icons/*
reverse_proxy @api 127.0.0.1:3338
reverse_proxy 127.0.0.1:8080
}/api/*, /connect, and /app-icons/* go to the backend. Everything else goes to the GUI.
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl enable caddy
sudo systemctl reload caddyFrom the VPS:
curl -H "Authorization: Bearer YOUR_TOKEN" http://127.0.0.1:3338/api/v1/status
curl http://127.0.0.1:8080/healthzFrom your browser:
https://nutbits.example.com
In the GUI, set:
- API URL: leave empty (same-origin)
- API Token: the exact
NUTBITS_API_TOKENfrom your.env
Quick reference:
systemctl --user status nutbits-backend nutbits-gui # status
systemctl --user restart nutbits-backend nutbits-gui # restart
journalctl --user -u nutbits-backend -f # backend logs
journalctl --user -u nutbits-gui -f # GUI logsFor the full list of service commands (start/stop, auto-start toggle, removal), see SERVICE.md.
cd ~/nutbits
git pull
npm install
npm --prefix gui install
npm run build:gui
systemctl --user restart nutbits-backend nutbits-guiRemove the services, fix your config, then reinstall:
npm run service:linux:remove
systemctl --user daemon-reload
# Fix your .env
npm run service:linux
loginctl enable-linger "$USER"See SERVICE.md for details.
GUI loads but API calls fail (401)
- Verify the token works locally:
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3338/api/v1/status - In the browser, clear stale state: open devtools console and run:
localStorage.removeItem('nutbits_api_url') localStorage.removeItem('nutbits_api_token') location.reload()
- Re-enter the token in the GUI and save
Relays show 0/0 or 0/N connected
- Check:
grep '^NUTBITS_RELAYS=' .env - If empty or missing, add relays and restart the backend
- If relays were missing during first boot, create a new NWC connection after fixing
- Node.js < 21: Node.js 20 and earlier don't have a native
WebSocketglobal, which the Nostr relay library requires. NUTbits includes awspolyfill that handles this automatically. If you see relay failures on Node 20, make surewsis installed (npm install).
/connect page fails but / works
- Caddy is sending
/connectto the GUI instead of the backend. Check your Caddyfile@apimatcher.
Caddy won't issue a certificate
- DNS must already point to the VPS
- Ports 80 and 443 must be open
Missing NUTBITS_STATE_PASSPHRASE
.envis missing orNUTBITS_STATE_PASSPHRASEis empty/commented. Recreate it from.env.example.
- SERVICE.md — full service management reference (start/stop, auto-start, removal)
- INSTALL.md — local setup (bare metal, Docker)
- DATABASE.md — storage backends (file, SQLite, MySQL)
- BACKUP.md — backup and recovery procedures