left4me/docs/l4d2-server-cvar-reference.md
mwiegand e38b844978
docs: janitorial cleanup checklist + L4D2 server cvar reference
Two follow-ups bundled into a single commit:

- docs/superpowers/specs/2026-05-15-janitorial-cleanup.md collects
  the "do later" small TODOs that surfaced across the recent idmap
  + consolidation work: dead cake-related artifacts, obsolete
  static systemd units in deploy/files/, the bubblewrap→systemd-run
  doc drift, stale gameserver-side idmap binds on un-checked
  instances, calendar reminder for SM 1.13 stable. Each item is
  small and self-contained.

- docs/l4d2-server-cvar-reference.md captures the research from
  the early-session L4D2 cvar deep-dive: tickrate sweet spots,
  nb_update_frequency cheat-protection + sm_cvar workaround,
  cvars that don't exist in L4D2 (net_maxcleartime,
  z_resolve_zombie_collision_multiplier per RCON probe), recommended
  plugins, MetaMod/SourceMod branch tracking, and the empirically-
  verified idmap-propagation-through-rebind kernel-6.12 quirk.
  Reference material, not a spec — lives at docs/ root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 02:05:12 +02:00

10 KiB
Raw Blame History

L4D2 server cvar reference

Working notes from the 2026-05 research session on best-practice L4D2 dedicated server settings. Sources cited inline; some findings verified empirically on the left4.me Trixie test server (kernel 6.12.86). This is reference material, not a settled design.

Quick lookup

Topic Recommended
Tickrate (stock) 30
Tickrate (competitive) 100, requires Tickrate Enabler plugin
sv_pure 2 (strict), or 0/1 for modded servers
sv_cheats 0 (set to 1 only on private practice servers; disables VAC)
sv_consistency 0 (allow custom campaigns) or 1 (strict for competitive)
sv_alltalk 0 (no cross-team voice), 1 for casual / fun servers
sv_lan 0 (internet server)
sv_voiceenable 1
nb_update_frequency 0.033 (safe, no plugin), 0.014 with the SM fix plugin. Cheat-protected — must be set via sm_cvar.
fps_max 64 for 30-tick, 0 (uncapped) for higher ticks

Network rates

L4D2 default tickrate is 30. Rates above the corresponding ceiling are ignored without the Tickrate Enabler plugin.

Rule of thumb: sv_maxrate = tickrate × 1000.

30-tick (stock)

sv_minrate              30000
sv_maxrate              30000
sv_mincmdrate           30
sv_maxcmdrate           30
sv_minupdaterate        30
sv_maxupdaterate        30
net_splitpacket_maxrate 30000
fps_max                 64

60-tick (requires Tickrate Enabler)

sv_minrate              60000
sv_maxrate              60000
sv_mincmdrate           60
sv_maxcmdrate           60
sv_minupdaterate        60
sv_maxupdaterate        60
net_splitpacket_maxrate 60000
fps_max                 128

100-tick (competitive, requires Tickrate Enabler)

sv_minrate              100000
sv_maxrate              100000
sv_mincmdrate           100
sv_maxcmdrate           100
sv_minupdaterate        100
sv_maxupdaterate        100
net_splitpacket_maxrate 100000
fps_max                 0

sv_minrate vs. sv_maxrate

  • Locking min == max (competitive servers do this) ensures every client sends at the tickrate exactly. Strict — kicks clients that dip below.
  • Leaving a range (e.g. min=10, max=30 on a 30-tick public server) tolerates clients on weak connections or loaded CPUs.
  • Setting sv_mincmdrate=0 means no enforced minimum — clients could send as few as 1-2 cmds/sec. Bad. Pick a floor that's playable (~10 minimum).

Cheat-protected cvars and sm_cvar

Several gameplay-affecting cvars are flagged as "cheat" in L4D2 and cannot be set via server.cfg unless sv_cheats 1 — which disables VAC and gates achievements. Trying to set them from cfg silently fails (the value stays at default).

To set them on a real (VAC-protected) server: install SourceMod and use sm_cvar <name> <value> instead of <name> <value>. SourceMod bypasses the cheat protection for server-side cvar writes only (does not grant cheat commands to players).

Cheat-protected cvars worth knowing:

  • nb_update_frequency — common-infected pathing/state update rate (see below).
  • director_* — most director cvars (AI difficulty, panic events, pacing).
  • z_* — most zombie-behavior cvars.

sm_cvar writes go in cfg/sourcemod/sourcemod.cfg (auto-execed by SM on map change) or in any cfg under cfg/sourcemod/. SM re-applies these on every map change — important because cheat-protected cvars reset to defaults on map change even within the same server session.

nb_update_frequency

Controls how often common infected and witches recalculate their pathfinding and state. Default 0.1 (10 Hz), independent of server tickrate.

Value Effect
0.1 (default) Common-infected look choppy regardless of tickrate
0.033 ~30 Hz updates; smooth, safe without plugin
0.024 Lowest "safe" without plugin per community testing
0.014 ~71 Hz; clients with cl_interp 0 see jittery commons unless the nb_update_frequency fix plugin is installed

Set via sm_cvar nb_update_frequency 0.033 in cfg/sourcemod/sourcemod.cfg (or any sm-auto-execed cfg). Without SourceMod, you cannot reliably set this on a VAC-protected server.

If commons still look choppy after lowering nb_update_frequency, try sm_cvar z_resolve_zombie_collision_multiplier 0.6 (default 0.25) — helps commons unstick from each other. Cheat-protected, same sm_cvar mechanism.

Cvars that DO NOT exist in L4D2 (despite some guides claiming otherwise)

These come up in older guides or are inherited from other Source games but don't actually exist in L4D2's command set. Verified by RCON <cmd> returning "unknown command":

  • net_maxcleartime — CSGO/CS:S only.
  • z_resolve_zombie_collision_multiplier — confirmed unknown in current L4D2 builds (verified via RCON 2026-05-14). Some community guides list it; it's not in the binary.

If a guide tells you to set one of these in L4D2, the guide is wrong or out of date.

Security and integrity

sv_cheats       0
sv_pure         2             # force Steam-only files (strictest)
sv_consistency  1             # enforce file hashes for critical files
                              # (set 0 if hosting custom campaigns)
sv_lan          0             # internet server

Launch the server with -secure to enable VAC. sv_cheats 1 requires -insecure (no VAC) — only acceptable on private practice servers.

sv_pure 2 breaks many workshop maps/mods. Use sv_pure 0 or 1 for modded servers.

Player limits

# Co-op / Scavenge
sv_maxplayers           4
sv_visiblemaxplayers    4

# Versus
sv_maxplayers           8
sv_visiblemaxplayers    8

Voice

sv_voiceenable  1
sv_alltalk      0   # 1 = cross-team voice (casual / fun servers)
Plugin Purpose
MetaMod:Source + SourceMod Required foundation for most of the below
Tickrate Enabler Unlock >30 tick servers
Little Anti-Cheat Aimbot / angle-cheat detection
SMAC Secondary AC layer (older but still works)
ZoneMod Competitive Versus ruleset (full bundle: ZoneMod + MatchMode + Confogl-style plugins)
l4d2_TKStopper Teamkill / griefing control
l4d_sb_fix Survivor bot behavior fixes
nb_update_frequency fix Eliminates client-side jitter at very low nb_update_frequency values

MetaMod:Source / SourceMod versioning

  • Stable branches are pinned in URL paths: 1.10, 1.11, 1.12, etc. There is no "latest stable" alias URL — you pick the branch.
  • Within a branch, the mmsource-latest-linux and sourcemod-latest-linux text files contain the current build's filename, e.g. mmsource-1.12.0-git1219-linux.tar.gz. Curl the pointer file, then curl the actual tarball.
  • AM bumps stable every ~2-3 years. When 1.13 (or later) is declared stable, update the MM_BRANCH=1.12 / SM_BRANCH=1.12 pins in the seeded Sourcemod overlay script.
  • L4D2 has no special branch — it uses whatever the current stable supports. L4D2's engine is so stable that SM 1.11 and 1.12 both work.

Watch for stable announcements at Metamod:Source news and SourceMod releases.

Empirically-verified kernel quirk (relevant if you tweak the helpers)

Idmapped bind mounts on kernel 6.12 (Trixie) do propagate through plain mount --bind re-binds. Verified end-to-end on left4.me during the 2026-05-15 build-time-idmap refactor: a sandbox process inside a re-bound idmapped mount can write files, and those writes land on disk with the idmap-translated uid.

This contradicts some published claims (including a generic research-agent summary) that idmaps don't propagate through plain re-bind on this kernel. Our use case is mount --bind --map-users src staging → systemd-run with BindPaths=staging:/overlay (a plain re-bind into the unit's namespace). It works.

The --map-users <a>:<b>:<count> direction is on-disk uid first, then in-mount uid. The util-linux man page calls these <inner>:<outer> which is confusing — <inner> means "the filesystem's native uid" (on disk) and <outer> means "the uid exposed outward through the mount." Empirically verified; do not trust the man page's word choice.

Launch parameters (reference)

Typical srcds invocation:

./srcds_run -console -game left4dead2 -secure -autoupdate \
    +maxplayers 8 -port 27015 +exec server.cfg +log on
  • -secure enables VAC. Don't run public servers without it.
  • -autoupdate keeps the server patched automatically.
  • +exec server.cfg runs your config on startup.
  • -tickrate <N> sets the engine tickrate (requires Tickrate Enabler for N > 30).

Sources

Primary references used for the recommendations above: