Litmus Documentation
Server Setup Guide
Run a permanent Litmus test server for your network. Applies to Litmus v1.1.1 — binaries are self-contained .NET 8 single files, no runtime install required. The same binary is also the client.
| Platform | Binary | Server modes |
|---|---|---|
| Linux x64 | litmus | Interactive TUI (-s), headless (-s --no-tui), systemd service |
| Windows x64 | Litmus.exe | Server mode tab in the desktop app, optional Auto-Start |
Default port: 7201 (TCP + UDP on the same number).
Linux Server
Recommended for permanent servers — headless mode, systemd integration, and BBR congestion control.
Quick start
Grab the Linux binary from the downloads section (or wget it directly as below) and drop it in /opt/litmus:
mkdir -p /opt/litmus && cd /opt/litmus
# download the Linux binary from bobpopcorn.com/tools/litmus, then:
wget https://bobpopcorn.com/tools/litmus-x64 -O litmus
chmod +x litmus
./litmus -s # interactive 3-pane TUI (server info / connections / chat)
./litmus -s --no-tui # plain text output (for logging pipelines / no TTY)
./litmus -v # verify version
Useful server flags
| Flag | Purpose |
|---|---|
-p, --port <n> | Control port (default 7201) |
-B, --bind <ip> | Bind a specific local interface |
--port-range 10000-10100 | Constrain data connections to a fixed port range (required behind strict firewalls — without it data ports are OS-assigned ephemerals) |
--log <file-or-dir> | Write a server log (directory = auto-named file) |
--no-tui | Plain text output, required for systemd |
Firewall
Each test uses the control port plus one data connection per stream on a separate port. Without --port-range, data ports are random ephemerals and inbound rules can't be written for them — so on firewalled hosts always run with a range and open it:
sudo ufw allow 7201/tcp
sudo ufw allow 7201/udp
sudo ufw allow 10000:10100/tcp # match your --port-range
sudo ufw allow 10000:10100/udp
LAN-only ports — do not expose on public hosts
UDP 45678 (multicast peer discovery/chat, group 239.255.69.67) and TCP 45679 (direct chat) bind all interfaces — block them at the firewall on internet-facing servers.
Kernel tuning (strongly recommended)
Stock Linux uses CUBIC congestion control and 4 MB buffer ceilings. On high-latency or lossy paths (satellite, intercontinental, congested peering) this caps each TCP stream far below the line rate — a 75 ms path with light loss can sit at 30–40 Mbps while the link carries 10× that. BBR + bigger autotuning ceilings fix it:
# BBR is built into kernels >= 4.9
sudo modprobe tcp_bbr
echo tcp_bbr | sudo tee /etc/modules-load.d/bbr.conf
sudo tee /etc/sysctl.d/90-litmus-tcp.conf <<'EOF'
# BBR: rate-based congestion control - tolerant of loss bursts that collapse CUBIC
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# Autotuning ceilings: high-bandwidth/high-RTT paths need windows beyond the 4MB stock max
# (300 Mbps @ 200 ms bufferbloated RTT needs ~8 MB in flight)
net.ipv4.tcp_rmem = 4096 131072 67108864
net.ipv4.tcp_wmem = 4096 16384 67108864
# Ceilings for explicitly pinned buffers (Litmus --buffer option); stock cap is ~208 KB
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
EOF
sudo sysctl --system
# verify
sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc # expect: bbr / fq
- Congestion control is assigned per-socket at creation — restart a running Litmus server after changing it. Existing SSH sessions keep their old algorithm; that's normal.
- BBR on the server governs download tests (server → client). Upload tests are governed by the client OS.
- Rollback:
sudo rm /etc/sysctl.d/90-litmus-tcp.conf && sudo reboot. - Leave Litmus's
--bufferat 0 (OS auto-tuned). Pinning a size disables receive-window auto-tuning and caps each stream at roughly size ÷ RTT.
Run as a systemd service
# /etc/systemd/system/litmus.service
[Unit]
Description=Litmus network test server
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/opt/litmus/litmus -s --no-tui --port-range 10000-10100 --log /var/log/litmus.log
Restart=on-failure
RestartSec=5
User=litmus
NoNewPrivileges=true
StateDirectory=litmus
Environment=DOTNET_BUNDLE_EXTRACT_BASE_DIR=/var/lib/litmus
[Install]
WantedBy=multi-user.target
sudo useradd -r -s /usr/sbin/nologin litmus
sudo touch /var/log/litmus.log && sudo chown litmus:litmus /var/log/litmus.log
sudo systemctl daemon-reload
sudo systemctl enable --now litmus
systemctl status litmus
Security hardening for public servers
- Use SSH key auth only (
PasswordAuthentication noin/etc/ssh/sshd_config) — public test servers attract constant brute-force scanners. - Firewall UDP 45678 / TCP 45679 (chat/discovery) from the internet.
- Anyone who can reach port 7201 can run tests that saturate your uplink. Restrict source IPs at the firewall if the server isn't meant to be public.
Verifying server-side performance
While a download test runs, on the server:
watch -n1 "ss -ti dst <client-ip>"
Read three things per data connection:
- The congestion algorithm tag — expect
bbrafter kernel tuning. rwnd_limited— a high % means the client's receive window is the ceiling.rttvsminrtt— a large gap under load = bufferbloat on the path.
Windows Server
The Windows build is the desktop app (Litmus.exe); it contains the full server in the Server tab.
- 1Download
Litmus.exe, run it, switch to Server mode, set the port (default 7201), Start. - 2Enable Auto-Start in server settings to begin listening on launch.
- 3Add firewall rules (run as Administrator):
netsh advfirewall firewall add rule name="Litmus TCP" dir=in action=allow protocol=TCP localport=7201
netsh advfirewall firewall add rule name="Litmus UDP" dir=in action=allow protocol=UDP localport=7201
Windows TCP tuning
Defaults are usually fine. Confirm receive autotuning is on (it is unless someone disabled it):
netsh interface tcp show global # "Receive Window Auto-Tuning Level" should be: normal
netsh interface tcp set global autotuninglevel=normal # restore if not
No production BBR on Windows
Windows sends with CUBIC; there is no production BBR. Windows 11 has an experimental BBR2 (netsh int tcp set supplemental Template=Internet CongestionProvider=BBR2) — known to break some apps; revert with CongestionProvider=CUBIC. For serious server duty on hard paths, prefer a Linux host.
Port Reference
| Port | Protocol | Purpose | Expose publicly? |
|---|---|---|---|
7201 | TCP | Control channel: handshake, ping/latency, stats | Yes (that's the service) |
7201 | UDP | UDP test traffic | Yes |
OS-assigned or --port-range | TCP/UDP | Data connections (one per stream) | Yes — use --port-range |
45678 | UDP | Multicast peer discovery + LAN chat (239.255.69.67) | No |
45679 | TCP | Direct peer chat (desktop app) | No |
Reading Results on Hard Paths
Expectation setting for satellite, long-haul, and lossy links.
- Single-stream TCP on high-RTT/lossy links (satellite, long-haul) reads far below link capacity — that is TCP physics, not a server fault. Use
-P 4–-P 8parallel streams and BBR on the server. - The UDP test with a bandwidth cap (
--udp -b 300) measures raw path capacity and loss with no TCP dynamics — use it to establish the ceiling, then compare TCP against it. - Latency rising under load (visible in test results) is bufferbloat on the path, not server overload.