TTR Networks

Intermediate · Traffic shaping with CAKE on Debian · section 6 of 8

Making it permanent

✓ Lab-tested 5 November 2026 on Debian 13.7 (kernel 6.12, in a QEMU VM), iproute2 6.15.0, systemd 257, iperf3 3.18

Everything so far was typed at the command line, and is gone after a reboot. Making it permanent takes two pieces.

Uploads: systemd-networkd. networkd can configure CAKE directly, with a [CAKE] section in the WAN interface's .network file: the bandwidth, and FlowIsolationMode=dual-src-host. networkd applies it whenever it sets up the interface.

Downloads: a small service. networkd can't add the ingress redirect to the IFB, so a short script does the five steps from Downloads, and a oneshot systemd unit runs it at boot, after networkd. RemainAfterExit=yes keeps the service "active" so systemctl status shows it ran, and systemctl stop removes the shaping.

After a reboot, both are back on their own: CAKE on ens4 in dual-srchost mode from networkd, and the IFB with CAKE in dual-dsthost mode from the service. Loaded latency is again about 2 ms on uploads and under 1 ms on downloads.

/etc/systemd/network/20-wan.network
# /etc/systemd/network/20-wan.network: the link to the modem, with CAKE on the way out.
[Match]
Name=ens4

[Network]
Address=192.0.2.2/24
Gateway=192.0.2.1
LinkLocalAddressing=no
IPv6AcceptRA=no

# Uploads: shape to just under the line's 5 Mbit/s, sharing it equally between LAN hosts.
[CAKE]
Parent=root
Bandwidth=4500K
FlowIsolationMode=dual-src-host
  • Lines 12–15: networkd puts CAKE on ens4 whenever it configures the interface.
/usr/local/sbin/cake-ingress
#!/bin/sh
# /usr/local/sbin/cake-ingress: shape downloads arriving on the WAN with CAKE.
# Incoming traffic is redirected to an IFB device, so CAKE can queue it "on the way out" of that.
set -e
WAN=ens4 IFB=ifb4ens4 BANDWIDTH=18mbit

case "$1" in
start)
  ip link add "$IFB" type ifb 2>/dev/null || true
  ip link set "$IFB" up
  tc qdisc del dev "$WAN" ingress 2>/dev/null || true
  tc qdisc add dev "$WAN" handle ffff: ingress
  tc filter add dev "$WAN" parent ffff: matchall action mirred egress redirect dev "$IFB"
  tc qdisc replace dev "$IFB" root cake bandwidth "$BANDWIDTH" ingress dual-dsthost
  ;;
stop)
  tc qdisc del dev "$WAN" ingress 2>/dev/null || true
  ip link del "$IFB" 2>/dev/null || true
  ;;
esac
  • Line 5: The three things to change for your own line.
  • Lines 9–14: The download shaping, as typed earlier.
  • Lines 16–19: And how to undo it.
/etc/systemd/system/cake-ingress.service
# /etc/systemd/system/cake-ingress.service
[Unit]
Description=CAKE shaping for downloads on the WAN link
After=systemd-networkd.service
Wants=systemd-networkd.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/cake-ingress start
ExecStop=/usr/local/sbin/cake-ingress stop

[Install]
WantedBy=multi-user.target
  • Lines 4–5: Run after networkd has set up the interfaces.
  • Lines 8–11: A oneshot service that stays "active", so status shows it ran and stop undoes it.
After a reboot (real output)
root@gw:~# chmod 755 /usr/local/sbin/cake-ingress && systemctl daemon-reload && systemctl enable cake-ingress.service
Created symlink '/etc/systemd/system/multi-user.target.wants/cake-ingress.service' → '/etc/systemd/system/cake-ingress.service'.

root@gw:~# systemctl reboot

root@gw:~# uptime -p; systemctl is-active systemd-networkd cake-ingress; tc qdisc show dev ens4; tc qdisc show dev ifb4ens4
up 0 minutes
active
active
qdisc cake 8001: root refcnt 2 bandwidth 4500Kbit diffserv3 dual-srchost nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 
qdisc ingress ffff: parent ffff:fff1 ---------------- 
qdisc cake 8002: root refcnt 2 bandwidth 18Mbit diffserv3 dual-dsthost nonat nowash ingress no-ack-filter split-gso rtt 100ms raw overhead 0 

root@client:~# iperf3 -c 198.51.100.10 -t 20 > /tmp/up.txt & sleep 3; ping -q -c 75 -i 0.2 198.51.100.10 | tail -2; wait; grep receiver /tmp/up.txt
75 packets transmitted, 75 received, 0% packet loss, time 15099ms
rtt min/avg/max/mdev = 0.436/2.375/4.095/0.918 ms
[  5]   0.00-20.01  sec  10.2 MBytes  4.30 Mbits/sec                  receiver

root@client:~# iperf3 -c 198.51.100.10 -t 20 -R > /tmp/down.txt & sleep 3; ping -q -c 75 -i 0.2 198.51.100.10 | tail -2; wait; grep receiver /tmp/down.txt
75 packets transmitted, 75 received, 0% packet loss, time 15312ms
rtt min/avg/max/mdev = 0.203/0.715/1.844/0.293 ms
[  5]   0.00-20.00  sec  40.6 MBytes  17.0 Mbits/sec                  receiver
  • Lines 1–2: Enable the service.
  • Lines 6–12: After the reboot: networkd's CAKE on ens4 (dual-srchost), and the service's IFB with CAKE (dual-dsthost).
  • Lines 14–22: Loaded latency still about 2 ms up and under 1 ms down.