build: headless Linux node that does not need libGL - #37
Merged
Conversation
The GUI build requires libGL at load time even when started with -nogui. The dynamic loader resolves every dependency before main() runs, so the flag arrives far too late -- on a clean server the binary dies with error while loading shared libraries: libGL.so.1: cannot open shared object file and nothing suggests it would otherwise have worked. A server is where a node most naturally lives, so this is the deployment that mattered most and the one that failed. The AppImage additionally needs FUSE, which minimal servers lack. Bundling libGL into the AppImage would be the wrong fix: GL has to come from the host's driver stack, and shipping our own breaks machines that have a real GPU. So: a second binary, built from the same node objects without ImGui, GLFW or OpenGL. `make linux-node` produces bitflash-node-<version>-x86_64, 2.5 MB stripped, with no graphics or FUSE dependency at all. `make linux` now builds it alongside the AppImage. Anyone wanting a window keeps using the AppImage. Two small moves were needed. MainFrameRepaint is called from main.cpp and net.cpp but defined in gui.cpp, so headless.cpp provides an empty one rather than putting ifdefs at six call sites. DateTimeStr also lived in gui.cpp while main.cpp logs block times with it -- nothing about formatting a date is GUI work, so it moved to util.cpp and both builds share one definition. Verified on Debian 13: ldd bitflash-node | grep -iE 'libGL|glfw|X11' -> nothing ldd bitflash | grep -iE 'libGL|glfw' -> libglfw.so.3, libGL.so.1, libGLdispatch.so.0 and at runtime, from an empty datadir: btfseed: reached ygffz37...btf via 90.156.222.107:8434 rendezvous: registered at 90.156.222.107:8434, waiting for a dial self-resolve OK (meeting_node=90.156.222.107:8434) btfpeers: kept 15 of 15 descriptor(s) first block at ~30s, 3182 blocks accepted, synced to the tip The GUI builds on Linux and Windows were rebuilt and are unaffected.
Merged
Bitflash-sh
added a commit
that referenced
this pull request
Jul 29, 2026
Vic-Nas
added a commit
to Vic-Nas/bitflash-mod
that referenced
this pull request
Jul 30, 2026
…sh#37); fix /gen not actually mining (port of Bitflash-sh#42) Two real gaps, both pulled from upstream, both squarely in "the command line has been neglected" territory. ## Headless build (Bitflash-sh#37) The GUI binary needs libGL present at load time even when started with -nogui/-daemon: the dynamic loader resolves every dependency before main() runs, so the flag arrives far too late. A clean server -- where a node most naturally lives -- fails outright with a shared- library error that gives no hint the program would otherwise work fine. The AppImage additionally needs FUSE, which minimal servers lack. `make linux-node-build` (wired into both `linux` and `linux-self`) produces a second binary, `$(APPNAME)-node-<version>-x86_64`, from the same node objects, without ImGui/GLFW/OpenGL at all -- no graphics or FUSE dependency. Two small moves were needed: headless.cpp provides an empty MainFrameRepaint() (declared in headers_core.h, previously only ever defined in gui.cpp, which a headless build doesn't compile), and DateTimeStr moved from gui.cpp to util.cpp since it's not actually GUI-specific. Adapted rather than copied: our src/Makefile carries extra objects (nostr/compactblock/update) and the mandatory REPO/UPDATE_PUBKEY (or NO_UPDATE=1) machinery upstream doesn't have. Caught one real bug while wiring the root Makefile's linux-node-build target: forwarding NO_UPDATE unconditionally (even as an empty value) would have made `ifdef NO_UPDATE` true in the child invocation regardless -- GNU Make's ifdef checks whether a variable was set at all, not whether it's non-empty -- silently disabling the updater in every node build, including regular (non-self) ones. Branched explicitly instead. ## /gen not mining (Bitflash-sh#42) nMineMode defaults to MINE_RELAY and nothing in ParseStartupArguments ever changed it for the plain /gen case (only /operator and /participant set their own mode) -- so a node started with /gen alone spawned ThreadBitcoinMiner, hit BitcoinMiner()'s relay guard, and returned immediately. The node ran indefinitely looking completely healthy -- synced, held peers, no errors -- while mining nothing, with the only hint being nMineMode = 0 in a startup line that reads like a status echo. This affects every headless miner, since the GUI was the only other way to select a mining mode. /gen now implies MINE_SOLO unless /operator or /participant asked for something else.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
The GUI build requires
libGLat load time even with-nogui. The dynamic loader resolves every dependency beforemain()runs, so the flag arrives far too late:Nothing in that message suggests the program would otherwise have worked fine. A clean Linux server is where a node most naturally lives — so this was the deployment that mattered most and the one that failed. The AppImage also needs FUSE, which minimal servers do not have.
Found while setting up the bootstrap seed from #36: a fresh VPS could not run our own release.
Why not just bundle libGL
Because GL has to come from the host's driver stack. Shipping our own would fix the headless server and break every machine with a real GPU. It trades a loud failure for a quiet one.
The fix
A second binary from the same node objects, without ImGui, GLFW or OpenGL.
make linux-nodeproducesbitflash-node-<version>-x86_64— 2.5 MB stripped, no graphics or FUSE dependency.make linuxnow builds it beside the AppImage. Anyone wanting a window keeps using the AppImage.Two small moves were needed:
MainFrameRepaintis called frommain.cppandnet.cppbut defined ingui.cpp.headless.cppprovides an empty one, rather than#ifdefat six call sites.DateTimeStralso lived ingui.cppwhilemain.cpplogs block times with it. Nothing about formatting a date is GUI work, so it moved toutil.cppand both builds now share one definition.Verified
Runtime, empty datadir, Debian 13:
First block at ~30s, 3182 blocks accepted, synced to the tip.
GUI builds on Linux and Windows rebuilt and unaffected.