Memory-constrained hosts1.x
infinidysk has a .NET backend and a Node frontend. Size the limit for both processes, plus sockets, TLS, SQLite, and native allocations—not only the managed .NET heap.
Container memory limits
Section titled “Container memory limits”Prefer a real container memory limit (docker run --memory or the equivalent
Compose/Kubernetes setting). .NET detects a cgroup limit and uses it when it
derives its available-memory and unset streaming-budget defaults.
Set an explicit In-flight article budget in Settings → Streaming on small hosts. Also cap provider connections and the streaming/queue connection settings to the throughput the host can sustain.
Hosts with ulimit -v / RLIMIT_AS
Section titled “Hosts with ulimit -v / RLIMIT_AS”ulimit -v caps virtual address space, not resident memory. A process can
hit it while its RSS is comparatively small.
On 64-bit Linux, the .NET regions GC reserves virtual address space up front.
When only DOTNET_GCHeapHardLimit is configured, the default region range is
up to five times that limit, subject to the virtual address space limit. Set
DOTNET_GCRegionRange explicitly if that reservation does not fit alongside
the rest of the process.
Environment-variable byte values for these .NET GC settings are hexadecimal:
environment: # 0x20000000 bytes (about 537 MB) managed heap ceiling DOTNET_GCHeapHardLimit: "20000000" # 0x40000000 bytes (about 1.07 GB) regions reservation, 2× the heap ceiling DOTNET_GCRegionRange: "40000000" # Upper bound (0–9). Prefer 5–7 as a first measured canary; see below. DOTNET_GCConserveMemory: "9" # Bound worker growth separately from the GC heap. THREADPOOL_MAX_THREADS: "200"Treat a 2× region range as a starting point, not a universal value. Workloads
that frequently make large object allocations can need a larger range; a range
that is too small trades virtual address space for more full compacting GCs.
DOTNET_GCHeapHardLimitPercent is usually unsuitable for a host that has only
an address-space limit, because its percentage is based on physical/cgroup
memory rather than RLIMIT_AS.
Set these values before the backend starts. Confirm the active values in
environment.json inside the support pack ZIP (Settings → Support):
runtime.addressSpaceLimitBytes
is the finite RLIMIT_AS when Linux exposes one, and the gc section reports
the region range, size, hard limit, committed heap, detected heap limit,
collection identity, memory-load, and LOH-specific hard limits.
DOTNET_GCConserveMemory is a restart-required runtime canary, not an
infinidysk default. The image does not set it. A value in the 5–7 range can
reduce LOH fragmentation at the cost of more frequent collections and longer
pauses; measure playback and import latency before keeping it.
Repeated 67 MB PROT_NONE mappings
Section titled “Repeated 67 MB PROT_NONE mappings”On a glibc host, a repeated pair of anonymous RW and PROT_NONE mappings
whose combined size is 67 MB (64 × 2²⁰ bytes) commonly identifies glibc malloc
arenas. It is not evidence that each NNTP socket has allocated 67 MB.
After verifying that map shape, try a conservative arena cap:
environment: MALLOC_ARENA_MAX: "2"Fewer arenas can add allocator lock contention under highly concurrent TLS or
download workloads, so measure throughput after changing it. This tuning is
glibc-specific; the shipped Alpine image uses musl and does not use
MALLOC_ARENA_MAX.
Additional safeguards
Section titled “Additional safeguards”- Lower provider Max connections and explicit download/queue connection limits before reducing buffers. NNTP I/O is asynchronous; it does not create a dedicated OS thread per socket.
- Disable warm connections or shorten the idle timeout if many pooled sockets are unnecessary.
- Lower Streaming body batch width and set an explicit In-flight article budget to reduce active decoded-pipe retention.
- Keep
usenet.segment-cache.write-behind-mbat0, or include its full additional pooled-buffer budget when sizing the container. Under pressure, write-behind skips new cache writes instead of exceeding its byte/job limits. DOTNET_GCRetainVM=0is already the .NET default; setting it does not shrink the initial regions-GC reservation.
See .NET runtime GC configuration for the complete runtime-setting semantics.