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>
259 lines
10 KiB
Markdown
259 lines
10 KiB
Markdown
# 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](https://github.com/SirPlease/Server4Dead-Project/tree/master/Tickrate%20Enabler).
|
||
|
||
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_min*rate vs. sv_max*rate
|
||
|
||
- 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](https://forums.alliedmods.net/showthread.php?t=344019) 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)
|
||
```
|
||
|
||
## Recommended plugins (SourceMod ecosystem)
|
||
|
||
| Plugin | Purpose |
|
||
|---|---|
|
||
| MetaMod:Source + SourceMod | Required foundation for most of the below |
|
||
| [Tickrate Enabler](https://github.com/SirPlease/Server4Dead-Project/tree/master/Tickrate%20Enabler) | Unlock >30 tick servers |
|
||
| [Little Anti-Cheat](https://github.com/J-Tanzanite/Little-Anti-Cheat) | Aimbot / angle-cheat detection |
|
||
| SMAC | Secondary AC layer (older but still works) |
|
||
| [ZoneMod](https://github.com/SirPlease/L4D2-Competitive-Rework) | 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](https://forums.alliedmods.net/showthread.php?t=344019) | 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](https://www.sourcemm.net/) and
|
||
[SourceMod releases](https://github.com/alliedmodders/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:
|
||
|
||
- [L4D2-Competitive-Rework server.cfg](https://github.com/SirPlease/L4D2-Competitive-Rework/blob/master/cfg/server.cfg)
|
||
- [L4D2 Dedicated Server Guide (Steam Community)](https://steamcommunity.com/sharedfiles/filedetails/?id=276173458)
|
||
- [L4D2 Dedicated Server Network Tweaks (Steam Discussions)](https://steamcommunity.com/app/550/discussions/1/1839063537784156851/)
|
||
- [SirPlease/Server4Dead-Project — Tickrate Enabler](https://github.com/SirPlease/Server4Dead-Project/tree/master/Tickrate%20Enabler)
|
||
- [Valve Developer Community — L4D2 console commands](https://developer.valvesoftware.com/wiki/List_of_Left_4_Dead_2_console_commands_and_variables)
|
||
- [AlliedModders — nb_update_frequency fix (Experimental)](https://forums.alliedmods.net/showthread.php?t=344019)
|
||
- [Source Multiplayer Networking — Valve Developer Community](https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking)
|
||
- [Required Versions (SourceMod wiki)](https://wiki.alliedmods.net/Required_Versions_(SourceMod))
|
||
- [MetaMod:Source news](https://www.sourcemm.net/)
|