NetValve™ is a latency equalization tool for CS2 servers. It applies Linux Traffic Control (tc netem) delays to low-ping players so matches feel fair and consistent.
Run:
curl -fsSL https://netvalve.hvh.gg/install | sudo bashto launch the interactive setup with the latest version.
To pin a specific release, use this:
NETVALVE_VERSION=1.0.0 curl -fsSL https://netvalve.hvh.gg/install | sudo bash- Traffic control based on live player ping data
- Strict mode: raise all players below target to the plugin-provided target ping
- Relaxed mode: raise players to the highest current ping, capped by target
- Bidirectional shaping on inbound and outbound traffic via IFB
- WebSocket ingest with HTTP fallback from the CS2 plugin
Set runtime behavior using plugin cvars:
netvalve_targetpingnetvalve_pingmode
Shaping stays off until the plugin exports a non-zero netvalve_targetping value.
Examples with target 20:
strictwith players5msand25ms->5is raised toward20ms,25msis unchangedrelaxedwith players5msand13ms->5msis raised toward13msrelaxedwith players5ms,13ms, and25ms->5msand13msare raised toward20ms
The setup script asks how CS2 servers should authenticate:
- Shared token (default, recommended for many servers): one token for all servers. Every plugin uses the same config and is identified by its game port. See Shared Binary / Auto ServerId Mode.
- Per-server: a unique
ServerIdand token for each server.
Leaving a token prompt empty auto-generates one. The script prints the resulting plugin config(s) at the end.
In per-server mode, enter the count of servers you want to connect. For each server, set:
ServerId(unique)AuthTokenPort
On each CS2 server, configure:
{
"ControllerUrl": "ws://host-ip:8443/ws",
"AuthToken": "server-token",
"ServerId": "server-1"
}Important: use the host machine IP, this might not always be 127.0.0.1. In BareMetal + Docker setups, plugins run inside containers and must reach the host network address.
If multiple CS2 servers share the same plugin config, leave ServerId empty:
{
"ControllerUrl": "ws://host-ip:8443/ws",
"AuthToken": "shared-token",
"ServerId": ""
}The plugin auto-generates ServerId from port (example: port-27015).
Example controller config (/etc/netvalve/config.json) for one shared token:
{
"Ingest": {
"Servers": [
{ "ServerId": "", "Token": "shared-token" }
]
}
}Set these in the server console or a cfg file after the plugin is loaded:
netvalve_targetping 50netvalve_pingmode strict
netvalve_targetping 0 disables shaping for that server.
- Linux kernel modules:
sch_netem,sch_htb,sch_ingress,cls_flower,act_mirred,ifb curl,tar,tc(usually fromiproute2,iproute, oriproute-tc)aspnetcore-runtime-8.0- Metamod:Source
- CounterStrikeSharp
. /etc/os-release
wget "https://packages.microsoft.com/config/${ID}/${VERSION_ID}/packages-microsoft-prod.deb" -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y aspnetcore-runtime-8.0For other Linux distributions, use Microsoft’s official .NET install docs: https://learn.microsoft.com/en-us/dotnet/core/install/linux
- Copy
netvalve-X.Y.X-linux-x64.tar.gzto the server - Extract it in a working directory on the server
- Run
NETVALVE_VERSION=X.Y.X sudo ./install.sh --localfrom that same directory - Start service:
sudo systemctl start netvalve-controller
- Download
NetValve.Plugin-X.Y.X.zip - Extract to
addons/counterstrikesharp/ - Edit
addons/counterstrikesharp/configs/plugins/NetValve/NetValve.json - Restart CS2 server or reload the plugin