Who this is for: you run an LND node (Umbrel, Start9, Raspiblitz, or your own) and you want to move onchain sats from your cache/buffer into your Lightning channels: to refill a drained channel, or to fund the channel that pays your "daily driver" mobile wallets.
What we'll use: Lightning Loop, Lightning Labs' non-custodial submarine swap service for LND.
loop in= onchain → your LN channels (this is the "refill the SPEND wallet / restore outbound" direction).loop out= your LN channels → onchain (frees inbound liquidity, or sends to cold storage).
Loop is fully non-custodial: your sats are moved through a script-controlled HTLC, no third party can take them. Another valid alternative to Boltz. Consider reading the [Loop CLI manual(https://github.com/lightninglabs/loop/blob/master/docs/loop.md) and learn about Static Loop In Addresses before implementing this guide.
The plan (desktop node option)
- Install and run
loopdnext to your LND node. loop inan amount from onchain into your chosen channel.- Move that fresh liquidity over Lightning from your desktop node to your mobile SPEND wallets (pay their invoices).
- Optionally
loop outwhen you need inbound capacity or want sats back onchain. - Optionally switch on AutoLoop so it happens by itself.
Prerequisites
- An LND node with all subservers (official release binaries include them; if you build from source, build with
tags="signrpc walletrpc chainrpc invoicesrpc"). - loopd + the
loopCLI. Get them from the releases page, or use Lightning Terminal (litd) which bundles Loop, or install the Loop app from your node's app store (Umbrel/Start9 have one). - Onchain sats somewhere: in your node's own onchain wallet, or in an external cache wallet (you'll use
--externalin that case). - Loop requires you to be able to receive a Lightning payment, so the channel you refill needs some inbound capacity (which a drained spend channel naturally has).
Step 1: Run loopd
loopd connects to your LND (default localhost:10009, reading ~/.lnd) and exposes its own CLI. On the command line:
loopdOn Umbrel/Start9, just start the Loop app. In Lightning Terminal you can drive everything from the web UI instead of the CLI.
Step 2: Check terms and get a quote
Always quote before swapping: fees move with the mempool.
loop termsThis shows the minimum and maximum amounts for in and out, plus the CLTV delta (how long funds stay locked in a worst-case failed swap).[1]
loop quote in 500000Gives a fee estimate for a 500,000-sat loop-in: service fee + onchain fee. If you don't like it, wait for lower fees and retry.
Step 3: Loop in (onchain → your channel)
loop in --amt 500000 --last_hop <peer-pubkey> --private --label "refill spend"Flags explained:
--amt: amount in sats (within the min/max fromloop terms).--last_hop <pubkey>: force the incoming sats to land in a specific channel (the one to your LSP / daily-driver node).[2] Skip it and Loop picks the best route.--private: important for private nodes: generates route hints so the Loop server can route the payment to you even though your channels aren't announced.--label: your own note ("refill spend", "august top-up", whatever).
Loop now prints one (or two) onchain HTLC addresses. Send the exact amount to that address:
- From your node's onchain wallet (default): just confirm, it pays itself.
- From an external cache wallet (exchange, Electrum, Sparrow, cold storage): use
--externalwhen you start the swap, then send the exact amount from that wallet to the address Loop printed.
Once your onchain transaction confirms, the Loop server pays you the sats over Lightning into your channel. That channel now has outbound liquidity again.
Step 4: Watch it complete
loop monitoror
loop listswapsBoth show in-flight and past swaps with their state, fees, and any failure reason.
Step 5: Move the liquidity to your SPEND wallets
The point of Loop here: the fresh sats now sit in your desktop node. To actually refill your mobile SPEND wallet:
- Open your mobile wallet (Zeus / Blixt / Lightning Pub), tap Receive, create an invoice for the amount (e.g. 210,000 sats).
- Pay that invoice from the desktop node: via the
lncli payinvoicecommand, or the node's web UI (ThunderHub / RTL / Lightning Terminal "Send"). - Sats arrive on your phone in seconds.
The desktop node + Loop is your buffer; the mobile wallets are your spending front-ends.
The reverse: loop out (LN → onchain)
Use this to free inbound liquidity (so you can receive again), or to move sats to your cache/cold wallet without closing channels:
loop out 250000 --addr bc1q... --conf_target 144 --label "to cache"--addr: where the onchain sats go (default: your node's wallet).--channel <short-channel-id>: drain a specific channel.--fast: instant loop out (skips batching, pays more).--conf_target: high number (e.g. 144) = slower, cheaper sweep; low = fast, pricey.--max_swap_routing_fee: cap the offchain routing fee.
Optional: automate with AutoLoop
Once you're comfortable, let Loop keep you balanced on its own:
loop setparams --autoloop=true --easyautoloop=true --localbalancesat=1000000Or per channel (refill a channel whenever its outbound drops below 25%):
loop setrule <channel-id> --outgoing_threshold=25 --incoming_threshold=25Advanced: static loop-in addresses (pre-fund during low fees)
Loop v0.27+ lets you create a permanent taproot address that you can fund onchain at any time: even months ahead: and execute the actual swap later:
loop static new # create your static address
loop static summary # see it- Send onchain sats to that address whenever fees are low (the deposit expires after ~3 months).[3]
- Later, swap it into your channels:
loop static in --amt ...(or--all). - It doubles as a normal onchain wallet:
loop static withdraw <address> <amt>. - Guard your L402 token in
~/.loop/<network>/: if you lose it, unspent deposits are stuck until the timeout expires.[4]
Newer versions can also open a channel directly from static deposits: loop openchannel.
Gotchas & notes
- Check
loop termsfor min/max: loop-out minimum is often ~250,000 sats and can rise in high-fee periods. - Loop In needs inbound capacity to receive the Lightning payment. If the target channel is already full on your side, loop-out first (or pick another channel).
- Fees vary a lot with the mempool. Always
loop quotefirst; during low-fee weekends a 500k swap can cost ~2-3k sats total, during spikes it can be far more. - Loop requires LND built with all subservers; if swaps fail at startup with a subserver error, that's why.
loopdhas its own macaroon/TLS (~/.loop), separate from LND's: don't mix them up.- Loop works with Neutrino, Bitcoin Core and btcd backends.
Cost check (rough, mainnet, 2026)
- Loop service fee: ~0.05–1% (in/out differ; quoted up-front).
- Onchain miner fee: funding tx (in) or sweep tx (out): the biggest variable.
- Lightning routing fee: capped via
--max_swap_routing_fee. - No Boltz, no exchange withdrawal fee, no channel closes.
Note: This guide may contain inaccurate information as technology evolve and tooling tune up. Feel free to report any inaccurancy in the comments below.
The CLTV delta is how many blocks your sats stay locked in the HTLC if a loop-in can't be completed (e.g. no inbound capacity): worst case you wait it out; you don't lose them. See The Loop CLI. ↩
Loop In delivers the incoming sats to you as a Lightning payment, so the channel it lands in needs inbound capacity (which a drained spend channel naturally has).
--last_hop <pubkey>pins the payment to one specific channel, identified by the peer's node pubkey. See The Loop CLI. ↩Static addresses are 2-of-2 taproot addresses (one key yours, one Loop's); deposits expire after ~14,400 blocks (~100 days), and you can withdraw from them at any time. See Static Loop In Addresses. ↩
Loop authenticates with L402s (LSAT); the token lives in
~/.loop/<network>. If you lose it, unspent static-address deposits can't be swapped until the timeout passes. See Static Loop In Addresses. ↩
I would recommend using static addresses if you're running a node with an active LOOP channel, and want to automate refilling it's liquidity.
That's a great point and useful suggestion. But wouldn't be risky to use a static address? As
loop static newgives a permanent on-chain deposit address (2-of-2 taproot: one key yours, one Loop's). This is a pre-funding box, not an actual swap. From my innerstanding, the flow would work like this:loop static in --allconverts those confirmed deposits into channel balance instantly (no waiting on a new onchain tx to confirm).~/.loop.The other point you mention is about automaticly refill channel liquidity, I found two ways to do it:
loopdto refill the channel itself, funded from those deposits:loopd --experimental loop setparams --autoloop=true --type=in --loopinsource=static-address loop setrule <channel-id> --outgoing_threshold=30 loop setparams --autobudget=100000 --autobudgetrefreshperiod=604800sNow whenever a channel's outbound liquidity drops below your threshold,
loopdshould fire a loop-in automatically from the static deposits.--loopinsource=static-addressis the piece that ties them together.@AuthenticityBTC how else would you do it?
Yes, there is risk if they go offline or their key acts maliciously your funds may be locked until the HTLC timeout (14400 blocks). But, the protection it gives me in busy periods is valuable enough - as having LOOP close during a busy period can be an expensive loss of liquidity while waiting for the channel to refill (naturally and rebalancing). I keep a safety amount in my static address for these busy periods.
There are periods when I have a full 500M drain on my channel.
I use custom code for managing my static loop in. I have not looked at the loopd automation at this time.
Read Managing LN wallets liquidity first if you haven't.