Routing fees roll in. Channels open and close. Rebalances drain. Three streams of activity, three sources of truth, and no single number that tells you whether your node is paying for itself.
Lightning node profitability is not a single metric. It is the difference between routing fees earned and costs paid, and those costs are scattered across multiple layers: on-chain channel funding transactions, rebalancing payments, force-close penalty fees, and the opportunity cost of capital locked in channels. The data is all on your node, Lightning state and on-chain state both, but in different shapes and different units with no view that ties it to revenue and cost lines. To produce a real answer in 2026 you need it in one place, easy to query, in a shape your accountant can understand.
This post walks through how to do that with the Clams CLI, using an LND node with three weeks of routed traffic as the example. The workflow is the same whether you run a node with a handful of channels or hundreds of them as an LSP.
What profitability actually means for a Lightning node
Revenue is straightforward: routing fees earned by forwarding payments. The cost side is where node operators get blindsided.
There are four cost buckets. The first is on-chain fees for channel lifecycle events: funding transactions when you open a channel, closing transactions when you close cooperatively, and the more expensive sweeps and HTLC resolutions that follow a force-close. The second is rebalancing: when you pay yourself across your own channels to restore inbound liquidity, the fees you pay other nodes are an expense, not a transfer. The third is liquidity itself: any sats paid for inbound liquidity ads, JIT channel opens with an LSP, or submarine swaps to top up an empty channel. The fourth is opportunity cost. Capital locked in channels could be earning yield elsewhere, or sitting in cold storage. It is the single largest input to "should I keep running this node?".
Routing fee revenue minus the first three is your gross profitability. The fourth is a modeling choice. The other three are non-negotiable if the number is going to mean anything.
The Clams data model maps directly to these events
Lightning's accounting nuances are spelled out in the BOLT specs, and our Lightning Network Accounting post walks through them end-to-end. The short version: every channel event is normalized into one of six canonical record types (channels, forwards, invoices, pays, transactions, UTXOs), and every record produces a double-entry journal that conserves sats.
For a profitability number, four of those record types matter:
forwardsproduce credits toIncome:Routing Incomefor the fee delta between incoming and outgoing HTLCs.paysproduce debits toExpense:Lightning Feesfor any routing fees you paid as the originator, including rebalances.transactionsproduce debits toExpense:Onchain Feesfor funding, closing, sweep, and penalty transactions you signed.channelscarry capacity, opener, and anchor outputs, which you need to attribute on-chain fees correctly and quantify locked capital.
Once your node is connected and synced, those records populate automatically. The numbers below are pulled directly from a running CLI, no spreadsheet work in between.
The walkthrough
Start with the connection you already have synced.
$ clams connections list
Connection ID Label Kind Last Sync
019d6381-a971-7a43-ad1c-93c979e99f80 lnd-node Lnd 2026-04-21T23:09:54+00:00
Then look at channel state. Capacity tells you how much capital is locked. The opener flag tells you which side paid the on-chain funding fee.
$ clams records channels list
ID Capacity Funding Outpoint Opener
... 1000000 77bd93e66adde188...:0 false
... 1000000 7f4167ec5297187c...:0 false
Two channels, one million sats each, both opened by the remote peer. That last detail matters: when opener is false the on-chain funding fee was paid by the counterparty. Your channel-open cost line for these is zero. If opener were true, the funding fee for that channel would show up as Expense:Onchain Fees keyed to the funding transaction.
Aggregate routing income next. Forwards carry a per-event fee already computed by the node:
$ clams records forwards list --format json | \
jq '[.data.items[].amount | tonumber] | add'
2808.446
2,808 sats earned across roughly fourteen thousand forwards over three weeks.
The cost side comes from the same journal-entries report. Two account labels matter for ongoing operations: Expense:Lightning Fees for routing fees you paid as the origin of a payment (including any rebalances you ran), and Expense:Onchain Fees for any funding, closing, or sweep transactions you signed.
$ clams reports journal-entries --format json | jq '
[.data.rows[]
| select(.account_kind == "expenses")
| {label: .account_label, amount: (.amount_sats | tonumber)}]
| group_by(.label)
| map({label: .[0].label, total: (map(.amount) | add)})'
[
{ "label": "Lightning Fees", "total": 10737.030 },
{ "label": "Expenses", "total": 324870000 }
]
10,737 sats paid to other nodes as the originator of outgoing payments, including any rebalances you ran. The much larger Expenses line is the principal of those payments, not a P&L cost: those are funds leaving Lightning to settle a payable. The data model keeps the two distinct so a tax report does not double-count outflows as losses.
The simple math, then, for this period:
Routing income: +2,808 sats
Lightning Fees expense: -10,737 sats
Onchain Fees expense: 0 sats (no opens or closes by us)
Net: -7,929 sats
A negative number on a regtest setup is unsurprising: synthetic traffic doesn't pay your real fees. On mainnet the same workflow produces a real number. The shape of the calculation does not change.
Per-channel profitability
The aggregate is one question. "Which channel is paying for itself" is a sharper one. Today, that view requires joining the journal-entries export against the channels and forwards records using their canonical IDs. A dedicated Lightning profitability report is on our roadmap and will produce per-channel revenue, rebalance cost, lifecycle cost, and break-even time in a single command.
The pattern to look for is the channel where lifetime routing fees earned is less than the sum of your share of the funding fee, the eventual closing or force-close on-chain cost, and the rebalances you paid to keep it liquid. That channel is costing you money. Whether to close it is a strategy call. Clams produces the number; the call is yours.
What the number is for
A profitability figure is data. A profitability decision is not.
Once you have the number, two things tend to happen with it. The first is a tax conversation. Tax treatment of routing fee income varies by jurisdiction, by whether the node is a hobby or a business, and by whether the fees crossed an entity boundary. Your accountant handles those questions. Clams gives them the journal entries report: every routing event timestamped, denominated in your reporting currency, and ready to import.
The second is an operational conversation. Some operators run nodes for the network, not the yield, and don't care that the number is small. Others are stress-testing whether to scale to ten, fifty, or two hundred channels for a payment processing business. The same data answers both. We produce it; we don't tell you what to do with it.
Get started
The CLI is one command to install and one to sync. Connect your LND or Core Lightning node, run clams journals process, and the records and reports above are populated.
If you'd rather pull this through an API, the Clams REST API exposes the same record types and reports. For the wider picture of what Clams is and where it is heading, Clams: Building the Bitcoin Accounting Layer covers the vision.
Routing a payment takes a few hundred milliseconds. Knowing what it cost you should not take a quarter.
Clams Team