Tuning¶
Docs-site note (rewritten 2026-07-11): Part of the Vexor documentation site (Anza-style). This page covers the performance knobs that are actually worth an operator's attention — what each does, its default, and the telemetry line to watch when you change it. The exhaustive, source-grounded list of every environment variable (including the ones the binary now sets for you) lives on the Environment Variable Reference; this page is the curated "what to tune and what to watch" companion to it, not a duplicate of it.
The short version: most of this is baked in now¶
As of the current binary, Vexor bakes its own proven performance defaults at startup — parallel execution, the
FEC-dedup cache, the ledger write path, and about twenty-three other VEX_* variables are set to their proven values
automatically, unless you've already set them yourself. See the baked necessity
set for the full table.
That leaves a small, genuinely host-specific surface: which binary to run, whether this NIC can do AF_XDP, which cores the parallel-execution workers live on, and the process-wide core mask. The reference deploy looks like this:
VEX_BINARY=<pinned> \
VEX_ENABLE_AFXDP=1 \
VEX_FRESH_SNAPSHOT=1 \
VEX_PARALLEL_EXEC=1 VEX_PARALLEL_EXEC_CORES=25,26 \
VEX_TASKSET_CORES=<mask> \
bash deploy.sh dev
(In practice this is appended with --identity, --vote-account, and --public-ip — see the minimal
recipe for a fully worked example.)
VEX_PARALLEL_EXEC=1 is written out explicitly above even though it's also baked on by default — it's harmless
either way, and spelling it out on the command line makes the intent visible in process listings and unit files.
Everything below this section is either one of those genuinely host-specific knobs, or a baked-on default that's
still worth understanding and knowing how to override for a specific diagnostic session.
The guiding rule hasn't changed: a perf knob is either proven bank-hash-neutral (safe to leave on) or it stays
off. None of the knobs on this page should ever change the bank hash; if one ever correlates with divergence, the
correct move is to turn it back off (=0) and report it, not to tune around it.
Transaction execution¶
VEX_PARALLEL_EXEC¶
Arms the wave-barrier parallel transaction execution path (see
Enhancements). Requires a binary built with
-Dparallel_exec (included in -Dprod).
| Effect | =1 (or unset — this is baked on) arms the WavePool; conflict-free native transactions execute in parallel on the worker cores, then commit serially in wave order. Explicit 0 runs the serial path, byte-identically. |
| Default | Baked on (1) as of the current binary. Proven bank-exact over ~1,600 offline-replay slots plus continued live operation. |
| Risk | Bank-hash-neutral by construction (commutative freeze fold + DAG write→read exclusion). Throughput-only. |
VEX_PARALLEL_EXEC_CORES¶
| Effect | Comma-separated list of cores for the persistent wave workers, e.g. 25,26. |
| Default | Not baked — hardware-specific, so you set it explicitly. A pair of cores on a cold CCX, never on CCX0 (cores 0–3 are OS/kernel reserved) and never colliding with a hot tile (verify 8–15, replay 16, produce 20, gossip 24). 25,26 is the proven choice on the reference 32-core box — see Hardware. |
| What to measure | During catch-up, watch that the node stays CURRENT; if parallel catch-up lags, confirm the wave cores are actually free (the deploy auto-carves them out of the floater mask when VEX_PARALLEL_EXEC is armed). |
Snapshot warm-up¶
VEX_FRESH_SNAPSHOT¶
| Effect | =1 re-extracts the snapshot on every boot and, if the local one is more than VEX_SNAP_MAX_AGE_SLOTS behind the live tip, downloads a fresh near-tip base + incremental pair before launch. |
| Default recommendation | On (1). This is the production-correct boot path — it never inherits possibly-corrupt local state, and the loader is parallelized (mmap + worker-thread pool), so a near-tip incremental boot is tens of seconds, not a slow single-threaded unpack. See the snapshot policy for the full contract, including the reuse-mode alternative for pinning a specific slot. |
| What to measure | Boot-to-CURRENT time in the deploy log; if it's unexpectedly long, check whether a fresh download was actually needed (VEX_SNAP_MAX_AGE_SLOTS, default 25000 slots ≈ 2.8 hours) or the entrypoint fetch itself is slow. |
VexLedger (blockstore)¶
These knobs control VexLedger. The storage path is always active when the ledger is
enabled (VEX_LEDGER=1, baked on by default); these tune durability vs throughput and disk footprint. Full
table on the VexLedger Configuration section.
VEX_LEDGER_FSYNC_EVERY¶
| Effect | fsync the ledger once per N completed slots. 1 = fsync every slot (the durable default); higher N = fewer fsyncs = more write throughput, at the cost of losing the last up-to-N slots of shreds on an unclean shutdown. |
| Default | 1 (every slot). Shreds are best-effort and re-fetchable from the network, so a higher value is a reasonable throughput trade if persist latency is a concern — but 1 is the durable baseline. |
| What to measure | persist_us(avg/max) in the [LEDGER-TILE-STATS] line (below). The fsync dominates per-slot persist latency, so this is the knob that moves that number. |
VEX_LEDGER_MAX_BYTES¶
| Effect | Bound the on-disk ledger to N bytes (--limit-ledger-size semantics). The oldest whole sealed segment is evicted with an O(1) unlink() — no compaction, instant space reclaim. Takes precedence over VEX_LEDGER_KEEP_SLOTS if both are set. |
| Default | Baked on at 107374182400 (100 GiB) as of the current binary — no longer unbounded by default. Set it explicitly to the size budget of your ledger drive minus headroom if 100 GiB isn't the right number for your box. |
| What to measure | The pruned=<seg>/<MiB> field in [LEDGER-TILE-STATS], and free space on the ledger drive — confirm eviction is keeping pace with the disk budget. |
Networking¶
VEX_ENABLE_AFXDP¶
| Effect | =1 enables the AF_XDP zero-copy ingest datapath (with --enable-af-xdp --xdp-zero-copy). Off uses the conventional socket RX path. |
| Default | Not baked — hardware-specific, so you set it explicitly per host. But AF_XDP must run on a NIC queue that does not also carry management/SSH traffic — enabling XDP on the live-cabled port link-resets it. Treat first-time enablement as a maintenance-window operation with console access. See Network setup. |
| What to measure | The [AFXDP-PHASE] line (loops/s, recv/zc-recv latency, ring_depth_max, zc_overrun): zc_overrun≈0 and ring_depth_max≈0 at steady state means RX is keeping up. |
VEXOR_ED25519_FEC_DEDUP¶
| Effect | =1 arms the FEC-set ed25519 dedup cache (skip re-verifying a signature already verified for the same FEC set). Requires a binary built with -Dfec_dedup (included in -Dprod). |
| Default | Baked on (1) as of the current binary. Bank-hash-neutral — it only ever skips a verify that would have succeeded, so it cannot change what is admitted. |
| What to measure | Steady-state catch-up health (the node should stay CURRENT and bank-exact); a healthy dedup path shows zero false rejects. |
Core placement¶
VEX_TASKSET_CORES¶
| Effect | The process-wide taskset core mask. Defines which cores the validator may run on; the per-tile pins (from src/vex_topo.zig) place specific threads within that mask, and the deploy also applies a cgroup-v2 cpuset wall-off. |
| Default | Not baked — hardware-specific, so you set it explicitly. Reserve CCX0 (cores 0–3) for the OS/kernel and place the validator on the remaining cores. With AF_XDP the proven mask on the reference box is 1-3,5-27; if parallel-exec catch-up lags, carve the wave cores out explicitly (1-3,5-24,27) so floaters never preempt cores 25,26. The deploy auto-carves the wave cores when VEX_PARALLEL_EXEC=1. |
| What to measure | The deploy-time /proc tiling verifier — it prints the live thread→core map and flags any consensus-core floater. A clean verifier output is the gate; an unexpected floater on a hot core is the signal to fix the mask. |
Telemetry — the data source¶
Vexor emits structured per-window stats lines that are the ground truth for tuning. Read these from the live log rather than guessing.
[LEDGER-TILE-STATS]¶
The primary signal for the ledger persist path. One line per stats window:
[LEDGER-TILE-STATS] slots=… enq=… applied=… dropped=… ring_high_water=…/<cap> persist_us(avg/max)=…/… pruned=…seg/…MiB
| Field | What it tells you |
|---|---|
persist_us(avg/max) |
Per-slot persist latency (serialize meta + append + fsync). The fsync dominates — this is the VEX_LEDGER_FSYNC_EVERY tuning signal. ~2 ms is the healthy range on the reference box. |
ring_high_water / cap |
Peak occupancy of the producer→consumer ring since the last window, against its capacity. If ring_high_water approaches cap, the persist consumer is falling behind the completion threads (disk backpressure) — raise VEX_LEDGER_FSYNC_EVERY or check the ledger drive. |
dropped |
Ring-full / backpressure drops. The ledger is best-effort and off the consensus path, so a drop self-identifies a missing shred on replay but never affects the bank hash. A non-zero dropped means the persist tile is overloaded — investigate the ledger drive and fsync cadence. It should be 0 at steady state. |
slots / enq / applied |
Completed-slot count and enqueued-vs-applied message counts — sanity that the tile is draining everything it is handed. |
pruned=<seg>/<MiB> |
Cumulative segments/space reclaimed by VEX_LEDGER_MAX_BYTES pruning. |
Other useful lines¶
[AFXDP-PHASE]— RX datapath health (seeVEX_ENABLE_AFXDPabove):zc_overrunandring_depth_maxshould be ≈0 at steady state.[QUIC-VOTE-STATS]— vote submission path (QUIC vs UDP counts, pool size/evictions, cache hit/miss), logged roughly every 10 s.
A safe tuning workflow¶
- Start from the reference deploy above — the baked defaults plus that handful of host-specific variables are the proven starting point.
- Change one knob at a time, then watch the telemetry for a full stats window before judging.
- Gate on correctness, not just speed. The node must stay CURRENT and bank-exact after any change. If a knob ever correlates with divergence, turn it off — it should not, and that is a bug to report, not to tune around.
- For durability vs throughput (
VEX_LEDGER_FSYNC_EVERY), trade only as far aspersist_usandring_high_waterjustify, and remember shreds are re-fetchable — so the only loss from a higher fsync cadence is a brief re-fetch on unclean restart. - To disarm a baked default for a diagnostic session, set it explicitly to
0on the command line — an explicit value always wins over the bake, no rebuild required. See Environment Variable Reference for the full baked list.
See also¶
- Performance Enhancements — what each feature does and its live/gated status.
- Environment Variable Reference — the exhaustive, source-grounded list of every runtime flag, including the baked-default table this page only summarizes.
- Build-Flag Reference — the build-time (
-D) side, and the minimal recipe. - VexLedger Configuration — the full ledger flag table.
- Hardware / Network setup — the reference box and the pinning/AF_XDP setup.