Personal tech blog — thoughts on systems, networks, and code
Understanding TCP congestion control in modern kernels
March 12, 2026 · 8 min read
The evolution of congestion control algorithms in the Linux kernel has been remarkable. From the early days of Reno and CUBIC to the more recent BBR algorithm developed by Google, each iteration brought significant improvements in throughput and fairness.
BBRv3, now available in kernel 6.8+, introduces better inter-flow fairness while maintaining the high throughput characteristics that made BBR popular. The key insight remains the same: rather than using packet loss as a signal of congestion, BBR builds an explicit model of the network path by estimating bottleneck bandwidth and round-trip propagation time.
In practice, enabling BBR on a server is straightforward: set net.ipv4.tcp_congestion_control = bbr and net.core.default_qdisc = fq in your sysctl configuration. The fq qdisc is essential — BBR relies on pacing, and fq provides the per-flow pacing support that BBR needs to function correctly.
Why I switched from Ansible to plain shell scripts
February 28, 2026 · 5 min read
After years of managing a handful of personal servers with Ansible, I made the controversial decision to switch back to plain bash scripts. The reason was simple: for a fleet of three servers, the cognitive overhead of maintaining Ansible roles, inventories, and vault files exceeded the complexity of the actual tasks.
My new approach is a single setup.sh per server role, idempotent by design. Each script checks the current state before making changes, logs every action, and can be re-run safely. The scripts live in a private git repository with a simple CI pipeline that validates syntax with shellcheck.
The tradeoff is real: I lose Ansible's declarative model and its module ecosystem. But for personal infrastructure, the ability to read and modify a deployment script without consulting documentation for a specific Ansible module version is worth it.
Practical notes on QUIC deployment
February 15, 2026 · 6 min read
QUIC has matured significantly since its standardization in RFC 9000. Deploying QUIC-based services in production taught me several lessons that aren't immediately obvious from the specification.
First, UDP receive buffer sizes matter enormously. The default rmem_max of 208KB on most Linux distributions is woefully inadequate for high-throughput QUIC. Setting net.core.rmem_max to at least 8MB prevents packet drops under load. Similarly, net.core.wmem_max should be increased for send operations.
Second, not all networks treat UDP equally. Some corporate firewalls and mobile carriers still throttle or block UDP on non-standard ports. A robust deployment should always have a TCP fallback. The ideal architecture serves QUIC on UDP/443 with automatic fallback to TLS over TCP/443 — the client probes both and uses whichever succeeds with lower latency.