Bug 55
Node memory grows while Stratum v2 mining clients are connected
- Status
- Open
- Symptom
- Memory leak; worst effect seen: crash
- Where seen
- A production node
- Trigger
- Normal use; no special event is needed
- First reported
- 2025-11-24 (first report)
- Fixed
- Not yet
- In released binaries
- Yes (v30.0)
- Defect ever on master
- Unknown
Summary
bitcoin-node v30.0 memory grew steadily while Stratum v2 mining clients used the IPC mining interface on mainnet, until a node on a 2 GB machine was killed by the kernel's out-of-memory killer after about a day. The cause is not known, and it is still undetermined whether the growth is a Bitcoin Core bug, a consequence of how many block templates clients keep alive, or a client-side problem. Seen with a released v30.0 binary. No fix yet; tracked in bitcoin#33940 ("Memory leak when using IPC mining interface").
What happened
- @lucasbalieiro ran the SRI
sv2-appspool againstbitcoin-nodev30.0 on a 2 GB mainnet VPS, and the kernel killed the node after roughly a day (Out of memory: Killed process ... (bitcoin-node) ... anon-rss:1302224kB). He first reported it on the sv2-apps PR sv2-apps#59. - @plebhash measured the node's memory with
psrecordon mainnet for about an hour, in separate runs with the Rust SRI pool and with the C++sv2-tp, and saw a steady upward trend in both. He posted the plots (2025-11-24) on bitcoin#33899, a design issue about managing block template memory for IPC clients. ryanofsky split the report out as bitcoin#33940 the same day. - lucasbalieiro followed up with the out-of-memory log and further runs (first comment, second comment, 2025-11-25): a build of the
30.xbranch used about 100 MB less at baseline, but memory still did not drop after the IPC clients disconnected.
Root cause
Unknown. What has been checked:
- Block templates handed to clients stay alive on the server until the client destroys them or disconnects, and each template pins its transactions even after they leave the mempool; bitcoin#33899 is the design issue about bounding that. Many live templates would look like this.
- @Sjors first suspected that
createNewBlocklacking aContextparameter keptdestroyfrom running, then retracted it: thesv2-tpsymptom was an unrelated client regression. - ryanofsky noted that since commit 949573da8411 (in mp#160, which reached Bitcoin Core before v30.0), a server object is freed soon after the client drops its reference even without
destroy. He added logging toBlockTemplateImpland showed with a Python client (2025-12-02) that the destructor runs promptly ondestroy, on dropping the reference, and on disconnect, on both master and v30.0. - plebhash's logs show the Rust pool destroying its stale templates (84 at one tip change) while memory still grew.
- ryanofsky pointed out that memory not shrinking after a disconnect is not by itself evidence of a leak, since the allocator can keep freed pages, and asked for a heaptrack run or a fast reproducer. His regtest stress run (2025-12-08) with the
sv2-appsv0.1.0 pool did not reproduce it: templates accumulated between blocks and were released when a new block connected, though the client fell behind in releasing them when blocks came quickly. Sjors replied that template providers hold templates for a grace period of about 10 seconds after a new tip.
Fix
No fix yet. The next step asked for in the issue is a heap profile or a reproducer that grows memory quickly.
Next steps
- Get a heap profile (for example with heaptrack) from a node serving Stratum v2 clients, or a reproducer that grows memory quickly. ryanofsky asked for these in bitcoin#33940, and no one has posted one since 2025-12-08.
- On a long mainnet run, count live
BlockTemplateImplobjects over time, as ryanofsky's destructor logging did on regtest. This would show whether templates held by clients account for the growth. - If they do, discuss a limit on live block templates, as in bitcoin#33899 and a privately reported proposal.
Timeline
- 2025-11-24: reported by plebhash on bitcoin#33899, relaying lucasbalieiro's out-of-memory report; bitcoin#33940 opened.
- 2025-11-25: out-of-memory log and further measurements from lucasbalieiro.
- 2025-12-02: server-side template lifetime tested; no problem found.
- 2025-12-08: regtest reproduction attempt; no leak reproduced. This is the last comment.
- 2026-01-14: labeled Bug, Resource usage and interfaces. The issue is still open.
- Releases: observed on v30.0.
Lessons
What would have prevented this bug, found it sooner, or made it easier to debug. The patterns page groups these lessons across all bugs.
Pattern An in-process interface trusting what now comes from another process: No limit on what a peer can make the server hold
- Prevented by a design rule
- Earliest detector a stress test (did not exist then)
- Diagnosis hard
- Fix design hard
- Debugging aids logging, a reproduction script, other
- Rust no
Would Rust have prevented it? No. Server-side objects whose lifetime a peer controls stay alive under Rust ownership too; bounding what a remote party may hold is a design decision, and the cause here is still undiagnosed. More on Rust
Could it have been prevented?
Unknown, because the cause is unknown; the honest answer is conditional. The one
mechanism everyone agrees exists is that a block template handed to a client stays
alive on the server, with its transactions pinned, until the client destroys it or
disconnects, and nothing bounds how many a client may hold. That is a design rule that
was stated only after the fact, as the design issue
bitcoin#33899: server-side state whose
lifetime a remote peer controls needs a cap or an eviction policy, the same as
connection slots or mempool size. If the growth is templates, the rule would have
prevented it; if it is something else (allocator retention, a client bug, an unrelated
leak), it would not, and ryanofsky's tests on 2025-12-02 and 2025-12-08 showed the
server releasing templates promptly on destroy, on dropped references and on
disconnect, which points away from the simplest version of the template theory.
Could it have been found sooner?
Only by a soak test that did not exist: a node on mainnet with a real Stratum v2 client
attached for hours, with RSS recorded. Nothing in CI runs an IPC client for more than
seconds, regtest does not produce the mainnet template churn (a 4 MB template every few
seconds through tip changes and mempool updates), and the v30.0 release shipped
bitcoin-node before any operator had run it that way. The report came about six weeks
after release, from the first people to leave it running on a small machine, which is
roughly the earliest the existing process could have produced it.
What made it hard to debug?
It is still open. The reports are RSS plots and an OOM-kill log from mainnet runs; RSS not falling after a disconnect is not evidence of a leak since the allocator keeps freed pages, and no one has produced a heap profile or a reproduction that grows memory in minutes, which is what ryanofsky asked for. Two things would have helped: a heap profiler run (heaptrack) on the affected node, and accounting the node could report itself, such as a count of live templates and the bytes they pin, logged or exposed over RPC, so a reporter can say whether memory tracks templates without a debugger. Without either, the investigation has had to guess at mechanisms and test them one at a time on regtest.
Lesson
When a remote peer decides how long a server-side object lives, bound it, and make the count and size of such objects observable; a memory report from the field is only actionable if the process can say what it is holding on whose behalf.
Classification
The survey's classification of this bug. The explorer charts these fields across all bugs; the method page explains them.
| Field | Value |
|---|---|
| Symptom | leak |
| Worst effect observed | crash |
| Seen in | production-node |
| Scope | production |
| Trigger | Normal use (ordinary-path) |
| Phase | steady-state |
| Side | server |
| Component | bitcoin-mining |
| Root cause kind | unknown |
| Fix kind | none |
| Detection | user-report |
| Needs a peer that is not libmultiprocess C++ | unknown |
| Needed a sanitizer to see | no |
| Defect ever on master | unknown |
| Released | binary |
| Confidence | low |