Run LN Operator on your node
Built for an LND node on a Raspberry Pi, but works anywhere LND's REST API is reachable. About 15 minutes end to end.
What you need
- A running LND node with REST enabled (Bitcoin Core or Neutrino backend).
- Read access to the node's
tls.certand anadmin.macaroon. - Python 3.10+ and
giton the same host. - Optional: a Telegram bot token + chat ID for alerts and the daily summary.
1 ยท Install
# clone and set up a virtualenv
git clone https://github.com/lnbright/ln-operator.git
cd ln-operator
python3 -m venv venv
venv/bin/pip install -r requirements.txt
# make the CLI callable from anywhere
sudo ln -s "$PWD/ln-operator" /usr/local/bin/ln-operator 2 ยท Check the install
Run ln-operator --help. If you see the full subcommand list, the wrapper and virtualenv are wired up correctly:
ln-operator --help
usage: ln-operator [-h] [--no-telegram] command ...
LN Operator โ Lightning Node Channel Management
positional arguments:
command See 'ln-operator <command> --help' for details
pipeline [automated] Run full pipeline: adjust_fees โ
rebalance_channels โ sync_routing โ healthcheck
plan [feature] Channel investment plan โ reads wallet
balance, proposes allocation
status [feature] Quick node overview with channel balance bars
history [feature] Show fee revenue, rebalance stats, and alerts
adjust_fees [debug] Adjust channel fee rates from balance ratios
rebalance_channels
[debug] Move sats from overfull to depleted channels
sync_routing [debug] Sync routing events from LND into the database
healthcheck [debug] Snapshot channel states, check problems, alert
backup [automated] Push channel.backup to remote host
overwrite_fee [feature] Pin a channel's outbound fee
clear_fee [feature] Remove a fee pin so auto-adjust resumes
recompute_signals [automated] Refresh slow per-channel signals (nightly)
options:
-h, --help show this help message and exit
--no-telegram Skip Telegram notifications 3 ยท Configure
Copy the template to .env and fill it in with your own node's
details โ this is the only file you must edit:
cp .env.example .env
nano .env What each key means:
# โโ LND connection (required) โโโโโโโโโโโโโโโโโโโโโโโโโโ
LND_REST_URL=https://127.0.0.1:9000 # your LND REST endpoint
LND_CERT=/home/lnd/tls.cert # path to LND's TLS cert
LND_MACAROON=/.../admin.macaroon # admin macaroon (read/write)
# โโ Telegram alerts (optional) โโโโโโโโโโโโโโโโโโโโโโโโโ
TELEGRAM_BOT_TOKEN= # from @BotFather
TELEGRAM_CHAT_ID= # your chat id (via @userinfobot)
# โโ Off-site channel.backup upload (optional, recommended) โโ
BACKUP_SOURCE_PATH=/home/lnd/.../channel.backup # file to push
BACKUP_SSH_HOST= # destination host (blank = disabled)
BACKUP_SSH_USER=
BACKUP_SSH_PORT=22
BACKUP_DEST_DIR= # remote directory
# โโ Dashboard bind (optional) โโโโโโโโโโโโโโโโโโโโโโโโโโ
DASHBOARD_BIND_IP=127.0.0.1 # or e.g. set your Tailscale IP for tailnet access
DASHBOARD_PORT=4000
Anything you leave blank falls back to the defaults in config.py.
Tuning knobs (fee margin, rebalance budgets) live there too.
Then initialise the database and confirm the connection:
venv/bin/python3 db.py
ln-operator status # should print your node + channel overview 4 ยท Automate
Run the pipeline every 2 hours and refresh slow signals nightly:
# crontab -e
0 */2 * * * cd /path/to/ln-operator && ./ln-operator pipeline 2>&1
15 3 * * * cd /path/to/ln-operator && ./ln-operator recompute_signals >> logs/signals.log 2>&1 5 ยท The ln-operator skill
LN Operator ships a Claude Code skill that turns the accumulated operating judgment โ what to check, what's noise vs. a real problem โ into a reusable agent. Drop it into your Claude Code skills folder, point it at your node, and get a reasoned daily health read without rebuilding that knowledge yourself.
~/.claude/skills/# install the skill
unzip ln-operator-skill.zip -d ~/.claude/skills/
# then in Claude Code:
/lnd-node-triage 6 ยท Dashboard (optional)
For a quick look, just run it:
venv/bin/python3 dashboard/app.py
To keep it running across reboots, install the bundled systemd unit. It
ships in services/lnd-dashboard.service โ copy it into place,
adjust the paths and user to match your install, then enable it:
# 1. copy the unit into systemd
sudo cp services/lnd-dashboard.service /etc/systemd/system/
# 2. edit User=, WorkingDirectory= and ExecStart= to match your install
# path and virtualenv (the shipped file assumes /home/pi/ln-operator)
sudo nano /etc/systemd/system/lnd-dashboard.service
# 3. reload systemd, enable at boot, and start it now
sudo systemctl daemon-reload
sudo systemctl enable --now lnd-dashboard
# 4. confirm it's up, and follow the logs
systemctl status lnd-dashboard
journalctl -u lnd-dashboard -f
๐ It binds to 127.0.0.1 by default; set DASHBOARD_BIND_IP
in .env to your preferred private network IP address โ e.g. your
Tailscale IP for tailnet access.
โ ๏ธ We suggest serving it on a private network only โ Tailscale or LAN. Never port-forward it to the open internet, as it has no authentication.
7 ยท Dashboard in detail
The dashboard โ โก LND Health โ is a single scrolling page
that reads live from LND plus the operator database. It refreshes on load;
hit โป Refresh in the header for a fresh fetch. Here's what every
card shows, top to bottom.
Node status, channels & backend
- Node Status โ chain sync state, current block height, the LND version you're running, and process uptime.
Syncedin green is what you want. - Channels โ active / inactive / pending channel counts and connected peers. Any inactive count turns red, since an offline channel can't earn or route.
- Bitcoin Backend โ which chain and network LND is on (e.g.
bitcoin / mainnet) and whether the chain and the graph are both synced. The graph is the network map used for routing and rebalancing. - Watchtowers โ your wtclient towers. Active is the count actually accepting new sessions; Configured counts every tower including deactivated ones; Backups delivered is the lifetime total of state updates shipped off-site. The badge is
healthyonly with โฅ1 active tower and zero failed backups.
Node balance & channel details
- Node Balance โ Total Funds Controlled โ every sat the node holds, split across on-chain confirmed, unconfirmed, in channels (local/sendable), pending open, and the total. Unsettled HTLCs (sats mid-flight) are called out separately so the total always reconciles.
- The Channel Liquidity bar underneath is the whole node's sendable vs. receivable split โ outbound (orange) on the left, inbound (purple) on the right. A balanced node routes in both directions.
- Channel Details โ one row per channel. The balance bar is colour-coded: red when depleted (<20% local), blue when saturated (>80% local), green when healthy. Then capacity, lifetime sent/received, your outbound fee (local) and the peer's (remote) in ppm, and the operator P&L columns: Revenue 30d (fees earned), Rebal Cost 30d (what you paid to keep it filled), Net 30d (revenue โ cost), and Net Lifetime. Green nets earn; red nets cost more than they make.
Off-site channel backup
Tracks the backup job that pushes channel.backup to a
remote host. Status is fresh when the last upload
succeeded recently, stale if it's overdue, and last attempt
failed on error. It shows when the last good upload landed, the file
size, the destination, and what fired it (timer for the systemd
timer, vs. a manual run). This is your loss-of-funds insurance โ watch that it
stays fresh.
Forwarding failures โ lost-revenue watch
Routing attempts that your node dropped in the last 24h โ fees you
could have earned but didn't. These events are live-only (LND persists them
nowhere), so the Monitor service badge matters: if the
htlc_monitor daemon is down, you're blind, not clean. It separates
empty-channel drops (recoverable โ refill that channel and the
revenue comes back) from fee-too-low and expiry/misc causes,
estimates the lost fees, and names the worst channel to top up
first.
Recent payments & invoices
- Recent Payments โ outbound payments the node made (mostly rebalances), with amount, routing fee, and
OK/FAILEDstatus. Failed attempts cost nothing but signal routes that wouldn't complete. - Recent Invoices โ inbound: settled invoices and any still open. Memos like
rebal:โฆare the node's own circular rebalances; others are real receives.
Routing events & daily revenue
- Recent Routing Events โ successful forwards through your node: amount in, amount out, and the difference you kept as a fee earned. This is the node doing its job.
- Daily Fee Revenue (30d) โ a bar per day of routing fees earned, with the 30-day total. Useful for spotting which days actually moved volume.
Sat flow โ where sats route
A routing map built from the forwarding log: it answers where does liquidity enter, and where does it leave? The dropdown switches the window between 30d, 7d, and all time, and the header sums it up โ total sats routed, number of forwards, and fees earned in that window. The IN and OUT dropdowns narrow the view to a single source and/or destination peer, so you can isolate one channel's routes; leave them on All sources / All destinations for the full picture.
- The table ranks the busiest in โ out peer pairs โ the actual routes sats took through your node โ with sats routed (bar-scaled to the biggest pair), the forward count, and fees earned on that route.
- โ Inbound โ where sats come from totals receipts per source peer: who feeds liquidity into your node.
- โ Outbound โ where sats go to totals sends per destination peer: where that liquidity leaves.
Read together, this shows your node's natural flow direction โ useful for deciding which channels to keep filled on the inbound side and which earn their keep on the outbound side.
Rebalance history & fee updates
- Rebalance History โ every rebalance the pipeline attempted: route (source โ target), amount, fee paid with effective ppm, success (โ/โ), and whether it was
autoormanual. Failed rows show theโคNppmbudget that was tried but not spent. - Recent Fee Updates โ automatic fee changes per peer: old โ new ppm (โ raised when local liquidity is scarce, โ lowered to attract flow), the channel's local ratio at the time, and whether it was an
autoadjust or a๐ pinyou set manually.
Recent alerts
The healthcheck's running log โ offline peers (with sats locked
behind them), depleted and saturated channels, and
rebal failing when a channel can't be refilled after repeated
tries. These are the same alerts that go to Telegram if you've wired it up;
an empty card means all clear.