Shape Queue Delay with tc-codel

tc-codel attaches CoDel, an active queue management discipline that controls delay without acting as a bandwidth shaper. It still allows bursts, and it is not a general-purpose replacement for every queueing discipline out there.

Allow about 15 minutes, plus time to confirm that changing the queue on the chosen interface is acceptable. The examples use eth0 as an obvious placeholder, replace it with the real device name before running anything. This is based on the installed tc-codel(8) manual from iproute2 6.1.0.

1. Check the interface and current queue

Identify the interface and read its existing queue configuration. Both commands are ordinary read-only checks and don't need elevated privileges on a typical system:

$ ip -br link
$ tc -s qdisc show dev eth0

Use the interface that carries the traffic you actually want to manage. The second command may show a root queue such as mq, pfifo_fast or something else. Save that output on a production host, it's the easiest reference for undoing the change later.

Checkpoint: Don't continue until eth0 is the intended device and you know whether it already has a root qdisc you'll need to restore.

2. Add CoDel with its documented defaults

Adding a root qdisc changes packet handling immediately, so it can affect latency and loss. Use a maintenance window or a test interface when the link carries anything important. The command needs elevated privileges:

$ sudo tc qdisc add dev eth0 root codel

With no optional parameters, the installed manual documents a 1,000-packet queue limit, a 5 ms target, a 100 ms interval and ECN marking disabled. CoDel measures packet sojourn time, not just queue length, and it enters its dropping mode only once the local minimum delay has stayed above the target for longer than the interval. That's why a short burst doesn't automatically trigger continuous loss.

There's normally no success message. Verify the new root qdisc:

$ tc qdisc show dev eth0
qdisc codel 8001: root refcnt 2 limit 1000p target 5.0ms interval 100.0ms

The handle and reference count can differ on your system. The useful checks: the qdisc is codel, it's attached at root, and the reported values match what you intended.

3. Enable ECN only when endpoints support it

CoDel can mark packets with ECN instead of dropping them when the queue needs control. Enable it explicitly:

$ sudo tc qdisc change dev eth0 root codel ecn
$ tc qdisc show dev eth0
qdisc codel 8001: root refcnt 2 limit 1000p target 5.0ms interval 100.0ms ecn

ECN doesn't repair endpoints or network paths that mishandle congestion marks. If you have no tested ECN deployment, leave the default noecn behaviour in place. To turn marking off again, change the existing qdisc:

$ sudo tc qdisc change dev eth0 root codel noecn

Changing a qdisc is also a live traffic change. Keep the command and its verification output together in your change record.

4. Tune the queue only with a reason

The manual recommends a target of 5 ms and an interval on the order of the worst-case round-trip time through the bottleneck. Start with the defaults unless measurements show a problem. If you have a measured reason for different values, set them explicitly:

$ sudo tc qdisc change dev eth0 root codel limit 100 target 4ms interval 30ms ecn
$ tc qdisc show dev eth0
qdisc codel 8001: root refcnt 2 limit 100p target 4.0ms interval 30.0ms ecn

limit is a hard packet limit: reaching it drops incoming packets, and lowering it can drop packets immediately to meet the new ceiling. A small limit isn't automatically a low-latency configuration, and an interval copied from an unrelated link may give senders too little time to react. Values such as 4ms and 30ms are accepted by the command exactly as shown in the manual.

Don't confuse CoDel with a rate limiter. If you need to cap throughput, pick and configure a shaping discipline separately, then check how it interacts with the queue you're deploying.

5. Read statistics after real traffic

Use the statistics form once the interface has carried enough traffic to exercise the queue:

$ tc -s qdisc show dev eth0
qdisc codel 8001: root refcnt 2 limit 100p target 4.0ms interval 30.0ms ecn
 Sent 237573074 bytes 268561 pkt (dropped 0, overlimits 0 requeues 5)
 backlog 0b 0p requeues 5
  count 0 lastcount 0 ldelay 76us drop_next 0us
  maxpacket 2962 ecn_mark 0 drop_overlimit 0

Don't treat zero drops in one sample as proof the queue can never build. Take at least two samples during representative load, then compare the queue configuration, backlog, drop counters and ECN marks against the latency and loss measurements that motivated the change.

6. Remove CoDel and recover the previous queue

Warning: Removing the root qdisc is service-disrupting for the interface's queue and discards its accumulated state. Use elevated privileges:

$ sudo tc qdisc del dev eth0 root
$ tc qdisc show dev eth0

Recovery: The resulting queue is picked by kernel and driver defaults, it's not necessarily the exact qdisc that was there before CoDel. Restore the recorded configuration if the old queue was deliberate. For example, only run the following if your saved pre-change output showed this matching discipline and settings:

$ sudo tc qdisc add dev eth0 root pfifo_fast

Don't run that restoration example just because it looks familiar. The installed system may have used mq, fq_codel or something else entirely, and the correct recovery command depends on the original output and the device.

Common failure traps

Done means