Run LN Operator on your node
Built for an LND node on a unix machine, 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
from database
adjust_fees [debug] Adjust channel fee rates based on current
balance ratios
rebalance_channels
[debug] Move sats from overfull to depleted channels
(auto-chunks: halves down to 100k on route failure)
manual_rebalance [feature] Rebalance a SPECIFIC source→target pair
(recorded as manual)
sync_routing [debug] Sync routing events from LND into the local
database
healthcheck [debug] Snapshot channel states, check for problems,
fire alerts
backup [automated] Push channel.backup to remote host (called
by systemd)
overwrite_fee [feature] Pin a channel's outbound fee — pipeline will
leave it alone
clear_fee [feature] Remove a fee pin so the pipeline can auto-
adjust again
recompute_signals
[automated] Refresh slow per-channel signals (market
multiplier). Designed for a nightly cron.
refresh_graph [automated] Pull the network graph into the local cache
(multi-MB; daily cron ahead of daily-check).
suggest_peers [feature] Suggest peers to open toward a target (alias
or pubkey) for cheaper refills.
monitor_htlcs [automated] Long-running: record dropped forwards from
LND's HTLC event stream (systemd)
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=IPADDRESS # destination host (blank = disabled)
BACKUP_SSH_USER=joe # destination usarname
BACKUP_SSH_PORT=22 # destination host's ssh port
BACKUP_DEST_DIR=/home/joe/lnd_backup # 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
Three cron lines do the automation — the fast pipeline every 2 hours, a nightly refresh of the slow per-channel signals (the market multiplier that drives demand-based pricing), and a nightly pull of the network graph into a local cache (used by plan, suggest_peers, and the daily-check agent's capital suggestions):
# crontab -e
# fast loop — fees → rebalance → sync → healthcheck
0 */2 * * * cd /path/to/ln-operator && ./ln-operator pipeline 2>&1
# nightly — refresh slow market signals (per-channel market multiplier)
15 3 * * * cd /path/to/ln-operator && ./ln-operator recompute_signals >> logs/signals.log 2>&1
# nightly — cache the network graph (multi-MB pull; runs ahead of the daily check)
30 3 * * * cd /path/to/ln-operator && ./ln-operator refresh_graph >> logs/graph.log 2>&1
👉 The 2-hour pipeline is the only loop you need for fees and
rebalancing — both the outbound fee engine and the optional node-level
inbound-fee ladder run inside it. To turn the inbound-fee
layer on, set INBOUND_FEE_ENABLED = True in config.py
(it defends a depleted channel with a small negative inbound fee to pull an
organic refill before paying for a circular rebalance); no extra cron line —
the next pipeline run picks it up.
5 · Forwarding-failure monitor (recommended)
One piece does not run on cron: the HTLC listener
(monitor_htlcs). It subscribes to LND's live HTLC event stream and
records dropped forwards — the ones you couldn't route for lack of liquidity —
into the database. LND persists these nowhere: the stream is
live-only with no replay, so it has to run continuously as an always-on
service, not every 2 hours. It feeds the dashboard's lost-revenue watch and the
fast-drain fee bump, so install it like the dashboard unit:
# 1. copy the unit into systemd
sudo cp services/lnd-htlc-monitor.service /etc/systemd/system/
# 2. edit User=, WorkingDirectory=, ExecStart= and EnvironmentFile=
# to match your install (the shipped file assumes /home/pi/ln-operator)
sudo nano /etc/systemd/system/lnd-htlc-monitor.service
# 3. reload systemd, enable at boot, and start it now
sudo systemctl daemon-reload
sudo systemctl enable --now lnd-htlc-monitor
# 4. confirm it's up (Restart=always keeps it alive across LND restarts)
systemctl status lnd-htlc-monitor
journalctl -u lnd-htlc-monitor -f ⚠️ While this service is down you are blind to dropped forwards, not clean — any downtime is a gap in the data, so let systemd keep it running.
6 · The daily check
A thin cron wrapper, scripts/daily-check.sh, runs once a day and
always sends a summary to Telegram. It has two modes. By
default it runs only deterministic, read-only
checks — the reconcile data-integrity arithmetic plus the
unit suite (make test) — and Telegrams a pass/fail summary with any
issues inline. No LLM, no spend.
Flip one env var and the same daily run becomes a Claude Code
skill instead: the accumulated operating judgment — what to check,
what's noise vs. a real problem — as a reusable agent. There is
nothing to download: it's already in the repo you cloned from
GitHub. The agentic prompt lives at scripts/daily-check-prompt.md;
the wrapper runs Claude headless against it (claude -p), caps spend
at --max-budget-usd 5, then sends the richer summary to Telegram.
The agent is off by default and gated behind one env var. Add
this line to your crontab (crontab -e) — it fires at 09:00 daily
and forces a read-only macaroon so the unattended agent can never move
funds. Set the flag to 1 to run the agent, or 0 for
the deterministic checks:
# daily check at 09:00 — flag=1 runs the AI agent, 0 runs deterministic checks
0 9 * * * LN_OPERATOR_ENABLE_AI_DAILY_CHECK=0 \
DAILY_CHECK_LND_MACAROON=/home/pi/.lnd-macaroons/ln-operator-readonly.macaroon \
/home/pi/ln-operator/scripts/daily-check.sh
With LN_OPERATOR_ENABLE_AI_DAILY_CHECK=1 the AI agent runs; with
0 (or the flag absent) the deterministic checks run instead — either
way the daily run happens and Telegrams you. Remove the whole line to stop the
daily check entirely.
7 · 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.
👉 Want to know what every card on the dashboard shows? See the dashboard, card by card in the tech docs.