{"id":27218520,"url":"https://github.com/rkfg/regolancer","last_synced_at":"2025-10-27T19:16:10.134Z","repository":{"id":57566712,"uuid":"526945436","full_name":"rkfg/regolancer","owner":"rkfg","description":"lnd channel rebalancer written in Go","archived":false,"fork":false,"pushed_at":"2023-08-01T08:18:07.000Z","size":782,"stargazers_count":47,"open_issues_count":3,"forks_count":9,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-15T09:53:42.581Z","etag":null,"topics":["automation","bitcoin","channels","lightning","lightning-network","rebalancer","rebalancing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rkfg.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-20T14:11:10.000Z","updated_at":"2024-10-09T14:23:46.000Z","dependencies_parsed_at":"2024-06-21T01:07:55.438Z","dependency_job_id":"af6c3a24-14a8-4f50-bac8-fd675a94cf04","html_url":"https://github.com/rkfg/regolancer","commit_stats":{"total_commits":156,"total_committers":3,"mean_commits":52.0,"dds":"0.19871794871794868","last_synced_commit":"fb578adc0169e6efffe03222390dbe5815bc11fc"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkfg%2Fregolancer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkfg%2Fregolancer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkfg%2Fregolancer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkfg%2Fregolancer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rkfg","download_url":"https://codeload.github.com/rkfg/regolancer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248165402,"owners_count":21058332,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","bitcoin","channels","lightning","lightning-network","rebalancer","rebalancing"],"created_at":"2025-04-10T05:38:31.321Z","updated_at":"2025-10-27T19:16:10.060Z","avatar_url":"https://github.com/rkfg.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Intro\n\nWhy even write another rebalancer? Usually, the only motivation is that the\nexisting software doesn't satisfy the developer. There may be different reasons\nwhy improving the existing programs isn't something the dev wants. Maybe the\nlanguage is the problem or architecture. For me there were many reasons\nincluding these. Two most advanced rebalancers for lnd are\n[rebalance-lnd](https://github.com/C-Otto/rebalance-lnd) (Python) and\n[bos](https://github.com/alexbosworth/balanceofsatoshis) (JS). However, each has\nsomething the other one lacks, both have runtime requirements and both are\n_slow_. I decided to fix these issues by rewriting the things I liked in Go\ninstead so it can be easily compiled and used anywhere. I also made the output\npretty, close to the design of [accumulator's fork of\nrebalance-lnd](https://github.com/accumulator/rebalance-lnd).\n\n![screenshot](screenshot.png)\n\n# Features\n\n- automatically pick source and target channel by local/remote liquidity ratio\n- retry indefinitely until it succeeds or 6 hours pass (by default)\n- payments time out after 5 minutes (by default) so if something's stuck the\n  process will continue shortly\n- timeouts can be customized\n- JSON/TOML config file to set some defaults you prefer\n- optional route probing using binary search to rebalance a smaller amount\n- optional rapid rebalancing using the same route for further rebalances\n  unitl route is depleted in case a rebalance succeeds\n- data caching to speed up alias resolution, quickly skip failing channel pairs\n  etc.\n- storing/loading cached nodes information to disk to \"warm up\" much faster next\n  time you launch the program\n- sensible node capacity formatting according to [Bitcoin\n  design](https://bitcoin.design/guide/designing-products/units-and-symbols/)\n  guidelines (easy to tell how many full coins there are)\n- automatic max fee calculation from the target channel policy and preferred\n  economy fee ratio (the amount spent on rebalance to the expected income from\n  this channel)\n- excluding your channels from consideration\n- excluding any nodes from routing through (if they're known to be slow or constantly failing to route anything)\n- using just one source and/or target channel (by default all imbalanced\n  channels are considered and pairs are chosen randomly)\n- calculate the rebalance amount automatically from current and desired balance\n  percent\n- safety precautions that prevent balances going beyond 50% of channel capacity,\n  can be turned off explicitly if that's what you want\n- saving successful rebalance parameters into a CSV file for further profit analysis\n  with any external tools\n\n# Installation\n\nYou need to have Go SDK installed, then simply run `go install\ngithub.com/rkfg/regolancer@latest` and by default it will download, compile and\nbuild the binary in `~/go/bin/regolancer`. To crosscompile for other platforms\nuse `GOARCH` and `GOOS` env vars to choose the target architecture and OS. For\nRPi run it as `GOARCH=arm64 go install github.com/rkfg/regolancer@latest` if you\nrun a 64 bit system (and you should!). You'll find the binaries in\n`~/go/bin/linux_arm64`. For 32 bit use `GOARCH=arm`, the binary will be located\nin `~/go/bin/linux_arm`.\n\n# Parameters\n\n```\nConfig:\n  -f, --config                 config file path\n\nNode Connection:\n  -c, --connect                connect to lnd using host:port\n  -t, --tlscert                path to tls.cert to connect\n      --macaroon-dir           path to the macaroon directory\n      --macaroon-filename      macaroon filename\n  -n, --network                bitcoin network to use\n\nCommon:\n      --pfrom                  channels with less than this inbound liquidity percentage will be considered as source channels\n      --pto                    channels with less than this outbound liquidity percentage will be considered as target channels\n  -p, --perc                   use this value as both pfrom and pto from above\n  -a, --amount                 amount to rebalance\n      --rel-amount-to          calculate amount as the target channel capacity fraction (for example, 0.2 means you want to achieve at most 20% target channel local balance)\n      --rel-amount-from        calculate amount as the source channel capacity fraction (for example, 0.2 means you want to achieve at most 20% source channel remote balance)\n  -b, --probe-steps            if the payment fails at the last hop try to probe lower amount using this many steps\n      --allow-rapid-rebalance  if a rebalance succeeds the route will be used for further rebalances until criteria for channels is not satifsied\n      --min-amount             if probing is enabled this will be the minimum amount to try\n  -i, --exclude-channel-in     (DEPRECATED) don't use this channel as incoming (can be specified multiple times)\n  -o, --exclude-channel-out    (DEPRECATED) don't use this channel as outgoing (can be specified multiple times)\n      --exclude-from           don't use this node or channel as source (can be specified multiple times)\n      --exclude-to             don't use this node or channel as target (can be specified multiple times)\n  -e, --exclude-channel        (DEPRECATED) don't use this channel at all (can be specified multiple times)\n  -d, --exclude-node           (DEPRECATED) don't use this node for routing (can be specified multiple times)\n      --exclude                don't use this node or your channel for routing (can be specified multiple times)\n      --exclude-channel-age    don't use channels opened less than this number of blocks ago\n      --to                     try only this channel or node as target (should satisfy other constraints too; can be specified multiple times)\n      --from                   try only this channel or node as source (should satisfy other constraints too; can be specified multiple times)\n      --fail-tolerance         a payment that differs from the prior attempt by this ppm will be cancelled\n      --allow-unbalance-from   (DEPRECATED) let the source channel go below 50% local liquidity, use if you want to drain a channel; you should also set --pfrom to \u003e50\n      --allow-unbalance-to     (DEPRECATED) let the target channel go above 50% local liquidity, use if you want to refill a channel; you should also set --pto to \u003e50\n  -r, --econ-ratio             economical ratio for fee limit calculation as a multiple of target channel fee (for example, 0.5 means you want to pay at max half the fee you might\n                               earn for routing out of the target channel)\n      --econ-ratio-max-ppm     limits the max fee ppm for a rebalance when using econ ratio\n  -F, --fee-limit-ppm          don't consider the target channel fee and use this max fee ppm instead (can rebalance at a loss, be careful)\n  -l, --lost-profit            also consider the source channel fee when looking for profitable routes so that route_fee \u003c target_fee * econ_ratio - source_fee\n\nNode Cache:\n      --node-cache-filename    save and load other nodes information to this file, improves cold start performance\n      --node-cache-lifetime    nodes with last update older than this time (in minutes) will be removed from cache after loading it\n      --node-cache-info        show red and cyan 'x' characters in routes to indicate node cache misses and hits respectively\n\nTimeouts:\n      --timeout-rebalance      max rebalance session time in minutes\n      --timeout-attempt        max attempt time in minutes\n      --timeout-info           max general info query time (local channels, node id etc.) in seconds\n      --timeout-route          max channel selection and route query time in seconds\n\nOthers:\n  -s, --stat                   save successful rebalance information to the specified CSV file\n  -v, --version                show program version and exit\n      --info                   show rebalance information\n  -h, --help                   Show this help message\n```\n\nLook in `config.json.sample` or `config.toml.sample` for corresponding keys,\nthey're not exactly equivalent. If in doubt, open `main.go` and look at the `var\nparams struct`. If defined in both config and CLI, the CLI parameters take\npriority. Connect, macaroon and tls settings can be omitted if you have a\ndefault `lnd` installation.\n\n# Node cache\n\nEnable the cache by setting `--node-cache-filename=/path/to/cache.dat` (or\n`node_cache_filename` config parameter), you're free to choose any path and file\nname you like. It speeds up printing routes and lowers load on lnd in case you\nrun multiple regolancer instances. If you're not interested in technical\ndetails, feel free to skip the following section.\n\n## How this cache works\n\nNode cache is only used for printing routes, it contains basic node information\nsuch as alias, total capacity, number of channels, features etc. However,\ngetting this information might be slow as every request to lnd is processed\nsequentially. The first few routes print noticeably slower until more nodes\n\"around\" you are queried and cached in RAM. This information shouldn't be very\nup-to-date (unlike the channel balances, policies etc. which are retrieved on\nevery launch and are only cached for the run time) and nodes themselves\nbroadcast updates not very often. It makes sense to persist this data to disk\nand load it on every run so that routes are printed almost instantly, and the\npayment is only attempted after the route is fully printed. It would be good to\nrun a payment attempt and route print in parallel but currently the payment\nfunction can dump errors and it would interfere with the route output.\n\nHowever, there's a gotcha that I learned from other users of regolancer: people\nrun multiple instances of it in parallel, so they might terminate at different\ntimes. If all those instances use the same cache file, they will overwrite it\nand lose information that another instance might have stored before them, or\nthey might start writing at the same time and corrupt it. One way to solve it is\nto use separate cache files but then each instance would query lnd for the same\nnodes as other instances. So instead of this I added file locking (using\n`/tmp/regolancer.lock` file on Linux and probably `%tmpdir%/regolancer.lock` on\nWindows, haven't tested) that allows multiple readers but just one writer and\nimplemented simple cache merging. When it's saved, first we load the existing\ncache (under a write lock so no one can access it), copy all nodes that are\nmissing in our own cache or have a more recent update time, then save the result\nreplacing the cache file.\n\nThere's also the cache expiration parameter (`--node-cache-lifetime`) set to\n1440min/24h by default that lets you skip cached nodes that are older than that.\nIt doesn't affect any actual logic, just that these nodes will be queried again\nfrom lnd when they're printed for the first time. Set it to a bigger number if\nyou don't care about the node stat actuality.\n\nCache is also saved if you interrupt regolancer with Ctrl+C.\n\n# Probing\n\nThis is an obscure feature that `bos` uses in rebalances, it relies on protocol\nerror messages. I didn't read the `bos` source but figured out how to check if\nthe route can process the requested amount without actually making a payment:\ngenerate a random payment hash and send it. `lnd` will refuse to accept it\n(because there's no corresponding invoice) but the program gets a different\nerror than the usual `TEMPORARY_CHANNEL_FAILURE`. Then we can do a binary search\nto estimate the best amount in a few steps. Note, however, that the smallest\namount can be 2\u003csup\u003en\u003c/sup\u003e times less than you planned to rebalance (where `n`\nis the number of steps during probing). For example, 5 steps and 1,000,000 sats\namount mean that you might rebalance at least 1000000/2\u003csup\u003e5\u003c/sup\u003e = 31250 sats\nif the probe succeeds. You can override this minimum with `--min-amount` so that\nprobing begins with this amount instead and either goes up or fails immediately.\nAnother problem is that fees can become too high for smaller amounts because of\nthe base fee that starts dominating the fee structure. It's handled properly,\nhowever.\n\nWhen enabled, probing starts if the payment fails at the second to last channel.\nThe last channel comes to yourself so you know it's guaranteed to accept the\nspecified amount. If all other channels could route this amount, the only\nunknown one is that second to last. Then we try different amounts until either a\ngood amount is found or we run out of steps. If a good amount is learned the\npayment is then done along this route and it should succeed. If, for whatever\nreason, it doesn't (liquidity shifted somewhere unexpectedly) the cycle\ncontinues.\n\n# Docker Setup\n\nIn general its recommanded to run regolancer in a normal environment because it is\nso easy to install as mentioned above. However if you want to run it in a docker\nwe also provide the Dockerfile so that you can easily get started.\n\nBuild the container or pull it:\n\n`docker build -t regolancer .` or `docker pull ziggie1984/regolancer`\n\nNow you can use regolancer with your lnd instance:\n\n`docker run --rm --add-host=host.docker.internal:host-gateway -it -v /home/lnd:/root/.lnd regolancer --connect host.docker.internal:10009`\n\nThe above command assumes /home/lnd is your lnd configuration directory. Please adjust as required.\n\nIf you want to use a config file either copy the file into the mounted volume (/home/lnd) or mount a separate volume. Then just add the `--config /root/.lnd/config.toml` parameter to your start command\n\n**Note for Umbrel/Umbrel-OS users**\n\n`docker run --rm --network=umbrel_main_network -it -v /home/umbrel/umbrel/app-data/lightning/data/lnd:/root/.lnd regolancer --connect 10.21.21.9:10009`\n\nOptionally you can create an alias in your shell's environment file like so:\n\nalias regolancer=\"docker run --rm --network=umbrel_main_network -it -v /home/umbrel/umbrel/app-data/lightning/data/lnd:/root/.lnd regolancer --connect 10.21.21.9:10009\"\n\nFor older versions of Umbrel please use `/home/umbrel/umbrel/lnd` instead of `/home/umbrel/umbrel/app-data/lightning/data/lnd`\n\n# What's wrong with the other rebalancers\n\nWhile I liked probing in `bos`, it has many downsides: gives up quickly on\ntrivial errors, has very rigid channel selection options (all should be chosen\nmanually), no automatic fee calculation, cryptic errors, [weird\ndefaults](https://github.com/alexbosworth/balanceofsatoshis/issues/88) and\n[ineffective\ndesign](https://github.com/alexbosworth/balanceofsatoshis/issues/125). It can\nalso unbalance another channel, there are no safety belts. It might be okay if\nyou absolutely need one channel to be refilled no matter the cost but if you\nwant your node to be profitable you have to account for every sat.\n\nRebalance-lnd is much better for automation but it still can't choose multiple\nsource and destination channels and try to send between them. You have to select\none source and/or one target, the other side is chosen randomly, often only to\ndiscard a lot of routes because of too high fees (this constraint can be\nspecified while querying routes but it isn't). The default route limit is 100\nso you either have to increase it or restart the script until it succeeds. I\nnoticed multiple times that it concedes after 20-30 attempts saying there are no\nmore routes but after restart still finds and tries more. It also lacks probing,\nconsumes quite a lot of CPU time sometimes and I personally find Python a big\npain to work with.\n\n# Why rebalance at all\n\nI'm still a bit torn on this topic. At some points in time I was a fan of\nrebalancing, then I stopped, now I began again. I guess it all comes with\nexperience. LN is still in its infancy and when the network is widely available\nand used on daily basis rebalancing won't be needed. But today we have a lot of\npoorly managed nodes (especially the big ones!) with default minimal fees and\nmore experienced nodes quickly drain this liquidity only to resell it for a\nhigher price. If a node has hundreds or thousands of channels with zero\nliquidity hints it becomes very hard to balance such channels. It essentially\nboils down to a bruteforce which is exactly what this program does. It seeks the\nnetwork for liquidity that's cheaper than your own and moves it to you.\n\nFor now some liquidity can be just dead. Even if you set 0/0 on a full channel\nyou see no routing through it. Because it's the opposite direction that everyone\nwants. So you have to move it manually, getting incoming liquidity to sell for\nsome other outbound liquidity you have. And when you run out of it you need to\nrefill the channels using that dead liquidity. In the future, hopefully, the\ndaily network activity will do this job thanks to circular economy. Today it's\nnot yet the case.\n\nHowever, there's not much point in rebalancing all the channels. See which are\nempty for weeks and consider them as candidates. From my experience, you might\nhave a few channels that can be drained very quickly if the fee is too low. They\nare channels to exchanges and service providers, sometimes other big nodes that\nconsume all liquidity you throw at them. That's your source of income,\nbasically. These channels should be added to exceptions in your config so\nthey're never used as a source, even when they match the percent limit.\n\n# How to route better\n\nBy all means, use [charge-lnd](https://github.com/accumulator/charge-lnd). Your\ngoal is to minimize local forward failures. It can be achieved with fees and/or\nmax HTLC parameter. You can try to move the dead liquidity with 0/0 fee before\ndoing rebalance. You absolutely should discourage routing through empty\nchannels. Best way is to set max_htlc on them so they're automatically discarded\nduring route construction. You can also disable them (it only happens on your\nend so you'll be able to receive liquidity but not send it) but it hurts your\nscore on various sites so better not to do it. Increase fees or lower max_htlc and\nyou'll be good. You can set multiple brackets with multiple limits like:\n\n- 20% local balance =\u003e set max_htlc to 0.1 of channel capacity (so it can\n  process ≈2 payments max or more smaller payments)\n- 10% local balance =\u003e set max_htlc to 0.01 of channel capacity (small payments\n  can get through but channel won't be drained quickly)\n- 1% local balance =\u003e set max_htlc to 1 sat essentially disabling it\n\nSame can be done with fees but if you decide to rebalance, watch out: you might\nspend a lot on rebalancing if your empty channel sets 5000ppm fee but after it\ngets refilled it switches back to regular 50 or 100ppm. You'll never earn that\nback. Learn how `charge-lnd` works and write your own rules!\n\n# Goals and future\n\nIt's a small weekend project that I did for myself and my own goals. I gladly\naccept contributions and suggestions though! For now I implemented almost\neverything I needed, maybe except a couple of timeouts being configurable. But I\ndon't see much need for that as of now. The main goals and motivation for this\nproject were:\n\n- make it reliable and robust so I don't have to babysit it (stop/restart if it\n  hangs, crashes or gives up early)\n- make it fast and lightweight, don't stress `lnd` too much as it all should run\n  on RPi\n- provide many settings for tweaking, every node is different but the incentives\n  are the same\n- since it's a user-oriented software, make the output pleasant to look at, the\n  important bits should be highlighted and easy to read\n\n# Feedback and contact\n\nWe have a Matrix room to discuss this program and solve issues, feel free to\njoin [#regolancer:matrix.org](https://matrix.to/#/#regolancer:matrix.org)!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frkfg%2Fregolancer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frkfg%2Fregolancer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frkfg%2Fregolancer/lists"}