Bufferbloat-Free Networking with CAKE
Fix bufferbloat on Linux using CAKE qdisc with tc, including autorate-ingress for variable links, persistent systemd configuration, and latency verification.
Before you start
- ▸Root or sudo access on the Linux host acting as the network gateway or direct modem connection
- ▸Linux kernel 4.19 or later (kernel 5.18+ recommended for autorate-ingress)
- ▸iproute2 with CAKE support installed
- ▸Knowledge of your WAN interface name (find it with: ip route show default)
Bufferbloat silently ruins interactive network performance: gaming latency spikes during downloads, video calls stutter while a file uploads, SSH becomes sluggish. The root cause is oversized in-flight queues at your bottleneck link. CAKE (Common Applications Kept Enhanced) is the current gold-standard queuing discipline in the Linux kernel that solves this with a single, well-reasoned configuration. Combined with autorate-ingress, it handles traffic shaping in both directions without needing a separate router firmware. This guide walks through deploying CAKE on a Linux host or router with tc, explains the knobs that matter, and shows how to verify the fix with real latency tests.
What CAKE Actually Does
CAKE is a queuing discipline (qdisc) implemented in the Linux kernel (mainline since 4.19). It combines:
- AQM (Active Queue Management) via COBALT (CoDel + BLUE), dropping or ECN-marking packets before the queue grows unmanageable.
- FQ (Flow Queuing) — each flow gets its own sub-queue, preventing a bulk transfer from starving interactive traffic.
- Traffic shaping — you tell CAKE your link rate; it keeps the queue at the bottleneck rather than upstream in your ISP's equipment.
- Overhead compensation — CAKE accounts for ATM cell tax, PPPoE headers, VLAN tags, so it shapes at the correct layer.
The key insight: bufferbloat lives in the queue ahead of your bottleneck. By shaping slightly below your line rate, CAKE owns the queue and keeps it tiny.
Prerequisites and Package Installation
CAKE is built into the kernel as a module. The tc userspace tool needs to be recent enough to know about it.
Debian / Ubuntu
sudo apt update
sudo apt install iproute2 iputils-ping
On Ubuntu 22.04+ and Debian 12, iproute2 already supports CAKE. Confirm with:
tc qdisc add dev lo root cake 2>&1 | grep -v 'File exists'
tc qdisc del dev lo root 2>/dev/null
Fedora / RHEL 9 / Rocky 9
sudo dnf install iproute tc-extra
On RHEL 9 derivatives, sch_cake may need enabling:
sudo modprobe sch_cake
echo 'sch_cake' | sudo tee /etc/modules-load.d/cake.conf
Arch Linux
sudo pacman -S iproute2
The module is already present in linux and linux-lts kernel packages.
Step 1 — Measure Your Real Line Rate
CAKE must shape below your actual throughput, not your advertised speed. Run a speed test under load using speedtest-cli or iperf3 against a nearby server. Record both upload and download in Mbit/s. A conservative starting point is 95% of the measured value — this guarantees CAKE, not your ISP, owns the queue.
sudo apt install speedtest-cli # Debian/Ubuntu
speedtest-cli --simple
Output will vary; note the Download and Upload figures. Subtract about 5% from each for your shaping targets.
Step 2 — Apply CAKE to Egress (Upload)
Egress — traffic leaving your machine toward the ISP — is straightforward. Replace eth0 with your WAN interface name and 50mbit with 95% of your measured upload speed.
sudo tc qdisc replace dev eth0 root cake bandwidth 50mbit diffserv4 nat wash ack-filter split-gso
Key options explained:
bandwidth 50mbit— your shaping rate. Usembit,kbit, orgbit.diffserv4— four-tin DiffServ scheduling: Bulk, Best Effort, Video, Voice. Matches most real-world DSCP markings.nat— makes flow isolation work correctly behind NAT by looking up the pre-NAT address.wash— strips incoming DSCP markings before forwarding (prevents abuse by upstream hosts).ack-filter— suppresses redundant TCP ACKs on the upload path, recovering bandwidth for asymmetric links.split-gso— splits GSO superpackets before queuing so CAKE's flow isolation and AQM act on real packet sizes.
For a PPPoE connection add overhead compensation:
sudo tc qdisc replace dev eth0 root cake bandwidth 50mbit diffserv4 nat wash ack-filter split-gso overhead 8 pppoe-vcmux
For VDSL with PTM framing (common in Europe) use overhead 8 ptm. For plain Ethernet use overhead 0 ethernet. CAKE's overhead keyword has several named presets — run man tc-cake for the full list.
Step 3 — Handle Ingress (Download) with autorate-ingress
Ingress is harder: packets arrive before you can shape them. The solution is to redirect all incoming traffic to a virtual ifb (Intermediate Functional Block) interface and apply CAKE there, effectively giving you an egress hook on the inbound stream.
Load the IFB module and bring up the interface
sudo modprobe ifb
sudo ip link add name ifb0 type ifb
sudo ip link set ifb0 up
Redirect ingress to ifb0
sudo tc qdisc add dev eth0 handle ffff: ingress
sudo tc filter add dev eth0 parent ffff: matchall action mirred egress redirect dev ifb0
Apply CAKE to ifb0 with ingress mode
sudo tc qdisc replace dev ifb0 root cake bandwidth 450mbit diffserv4 nat ingress wash split-gso
Note the ingress keyword — it tells CAKE which direction the traffic flows for correct flow isolation and the ack-filter (which applies on the opposite interface for ACKs). Replace 450mbit with 95% of your measured download speed.
autorate-ingress for variable links (LTE/VDSL)
If your line speed varies — mobile connections, VDSL under rain fade — static bandwidth settings leave you either over-shaping (wasted capacity) or under-shaping (bufferbloat returns). The autorate-ingress option tells CAKE to measure actual throughput and reduce its rate automatically when queuing is detected. Pair it with a conservative maximum:
sudo tc qdisc replace dev ifb0 root cake bandwidth 450mbit autorate-ingress diffserv4 nat ingress wash split-gso
autorate-ingress requires kernel 5.18+. It only works on the ingress path. The bandwidth parameter becomes the ceiling, not a fixed target.
Step 4 — Persist with a systemd Service
tc rules are lost on reboot. A simple systemd service applies them after the interface is up. Create /etc/systemd/system/cake-qos.service:
sudo tee /etc/systemd/system/cake-qos.service <<'EOF'
[Unit]
Description=CAKE QoS shaping
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/cake-qos.sh
ExecStop=/usr/local/sbin/cake-qos-stop.sh
[Install]
WantedBy=multi-user.target
EOF
Create /usr/local/sbin/cake-qos.sh (replace values to match your setup):
sudo tee /usr/local/sbin/cake-qos.sh <<'EOF'
#!/usr/bin/env bash
WAN=eth0
UPLOAD=50mbit
DOWNLOAD=450mbit
modprobe ifb
ip link add name ifb0 type ifb 2>/dev/null || true
ip link set ifb0 up
tc qdisc del dev "$WAN" root 2>/dev/null
tc qdisc replace dev "$WAN" root cake bandwidth "$UPLOAD" diffserv4 nat wash ack-filter split-gso
tc qdisc del dev "$WAN" ingress 2>/dev/null
tc qdisc add dev "$WAN" handle ffff: ingress
tc filter add dev "$WAN" parent ffff: matchall action mirred egress redirect dev ifb0
tc qdisc del dev ifb0 root 2>/dev/null
tc qdisc replace dev ifb0 root cake bandwidth "$DOWNLOAD" autorate-ingress diffserv4 nat ingress wash split-gso
EOF
sudo chmod +x /usr/local/sbin/cake-qos.sh
sudo tee /usr/local/sbin/cake-qos-stop.sh <<'EOF'
#!/usr/bin/env bash
tc qdisc del dev eth0 root 2>/dev/null
tc qdisc del dev eth0 ingress 2>/dev/null
tc qdisc del dev ifb0 root 2>/dev/null
ip link del ifb0 2>/dev/null
EOF
sudo chmod +x /usr/local/sbin/cake-qos-stop.sh
sudo systemctl daemon-reload
sudo systemctl enable --now cake-qos.service
Step 5 — Verify It's Working
Confirm the qdiscs are in place:
tc -s qdisc show dev eth0
tc -s qdisc show dev ifb0
You should see cake as the root qdisc on both, with incrementing statistics after traffic flows.
Run a real bufferbloat test. The most reliable method is load-induced latency: ping a nearby host while running a saturating download and upload simultaneously.
# Terminal 1 — continuous ping
ping -i 0.1 8.8.8.8
# Terminal 2 — saturate upload + download simultaneously
iperf3 -c iperf.he.net -t 30 -R &
iperf3 -c iperf.he.net -t 30
Without CAKE, ping latency during load often jumps to 200–2000 ms. With CAKE correctly configured and shaped slightly below line rate, you should see latency increase by no more than 5–20 ms above idle baseline.
The Waveform Bufferbloat Test is a useful browser-based alternative that grades your connection A–F and shows latency-under-load figures.
Troubleshooting
- CAKE not recognized by tc: Your
iproute2is too old orsch_cakemodule isn't loaded. Runmodprobe sch_cakeand retry. On RHEL, ensure thetc-extraoriproute-tcpackage is installed. - Throughput drops too much: Your bandwidth target is set too low. Raise it to 97–98% of measured speed and retest. Also check that
split-gsois active — without it, CAKE sees 64 KB superpackets and shapes incorrectly. - Latency is still high under load: You may have the wrong interface. Find your WAN interface with
ip route show default. Also confirm traffic actually passes through CAKE by checking thetc -spacket counters increase. - autorate-ingress kernel version: The
autorate-ingresskeyword requires kernel 5.18+. Check withuname -r. On older kernels, remove that keyword and use a static bandwidth. - IFB interface missing after reboot: Ensure
ifbis in/etc/modules-load.d/or is loaded by your service script before theip link addcommand.
Frequently asked questions
- Why shape to 95% of my line rate instead of 100%?
- CAKE must own the bottleneck queue. If you shape at exactly 100%, bursts momentarily exceed the rate and the queue forms upstream at your ISP's hardware, where you have no control. The 5% headroom ensures the queue always forms inside CAKE.
- Does CAKE work on a regular desktop, or only on a dedicated router?
- It works on any Linux machine that is the bottleneck — typically the machine directly connected to the modem. On a desktop behind a home router, CAKE on your desktop only shapes that one machine's traffic, not the whole household. For whole-network coverage you need CAKE on the router.
- What is autorate-ingress and when should I use it?
- autorate-ingress makes CAKE dynamically lower its rate limit when it detects queue growth, then recover when the link clears. It is most useful on LTE, VDSL, or cable connections where throughput varies with signal conditions. It requires Linux 5.18+ and only applies to ingress-direction CAKE instances.
- Will CAKE affect my maximum throughput?
- Slightly, by design. You lose the 5% headroom you gave up to own the queue. In exchange, interactive latency under load drops dramatically. On a 500 Mbit link that 25 Mbit ceiling is irrelevant for most workloads.
- How do I choose between diffserv4, diffserv8, and besteffort?
- diffserv4 is right for most setups: it maps real-world DSCP values into four tins (Bulk, Best Effort, Video, Voice) with appropriate priorities. Use diffserv8 only if you generate and fully trust DSCP markings across your network. besteffort disables tin prioritisation entirely and is only appropriate when you want pure flow fairness with no DSCP treatment.
Related guides
Build a Mesh VPN with Nebula
Build a fully self-hosted mesh VPN with Nebula: create a CA, sign node certs, configure lighthouses, enforce group-based firewall rules, and run as a systemd service.
Common Linux Network Ports Reference
Learn Linux port ranges, read /etc/services, find what's listening with ss and nmap, and apply solid firewall rules to expose or block the right ports.
How to Configure a Static IP on Linux
Configure a static IP on Linux using Netplan, NetworkManager (nmcli), or systemd-networkd across Ubuntu, Fedora, Debian, and Arch with verified steps.
Expose a Service with Cloudflare Tunnel
Expose local services to the internet without port-forwarding using Cloudflare Tunnel. Install cloudflared, create a named tunnel, configure ingress rules, and run as a systemd service.