Skip to content

Instantly share code, notes, and snippets.

@toneoesa
Created November 30, 2025 11:55
Show Gist options
  • Select an option

  • Save toneoesa/81f712b9650aaeae85fdd7e88c4b30ae to your computer and use it in GitHub Desktop.

Select an option

Save toneoesa/81f712b9650aaeae85fdd7e88c4b30ae to your computer and use it in GitHub Desktop.
Limiting the TX speed of docker containers
#!/bin/bash
# Fallback defaults
DEFAULT_RATE="35mbit"
DEFAULT_BURST="16kb"
DEFAULT_LATENCY="100ms"
# Label Keys
LABEL_ENABLE="dsl.txlimit"
LABEL_RATE="dsl.txrate"
LABEL_BURST="dsl.txburst"
LABEL_LATENCY="dsl.txlatency"
# Function to timestamp logs
log() {
echo "[DockerTrafficControl] $1"
}
apply_limit() {
local cid=$1
# Inspect container to get PID and Labels
# We use a custom format with pipe delimiter to parse easily
local inspect_format="{{.State.Pid}}|{{index .Config.Labels \"$LABEL_ENABLE\"}}|{{index .Config.Labels \"$LABEL_RATE\"}}|{{index .Config.Labels \"$LABEL_BURST\"}}|{{index .Config.Labels \"$LABEL_LATENCY\"}}"
local container_info
container_info=$(docker inspect -f "$inspect_format" "$cid" 2>/dev/null)
# If docker inspect failed (container might be gone), return
if [ -z "$container_info" ]; then
return
fi
# Read variables from the pipe-delimited string
IFS='|' read -r pid limit_enable label_rate label_burst label_latency <<< "$container_info"
# 1. Validation: PID check
if [ -z "$pid" ] || [ "$pid" == "0" ]; then
return
fi
# 2. Validation: Check if limiting is enabled (Strictly check for "1")
if [ "$limit_enable" != "1" ]; then
return
fi
# 3. Parameter Resolution: Use label value or fallback to default
local rate=${label_rate:-$DEFAULT_RATE}
local burst=${label_burst:-$DEFAULT_BURST}
local latency=${label_latency:-$DEFAULT_LATENCY}
log "Container $cid (PID: $pid) requesting limit. Rate=$rate, Burst=$burst, Latency=$latency."
# 4. Execution: Apply TC rules via nsenter
# Delete existing rules
local del_output
del_output=$(nsenter -t "$pid" -n tc qdisc del dev eth0 root 2>&1)
# Add new rules
local add_output
add_output=$(nsenter -t "$pid" -n tc qdisc add dev eth0 root tbf rate "$rate" burst "$burst" latency "$latency" 2>&1)
local status=$?
if [ $status -eq 0 ]; then
log "Limit applied successfully."
else
log "Failed to apply limit."
log "tc del: $del_output"
log "tc add: $add_output"
fi
}
log "Docker Speed Limit Monitor Started"
log "Processing running containers..."
docker ps -q | while read -r container_id; do
apply_limit "$container_id"
done
log "Listening for docker start events..."
docker events --filter 'event=start' --format '{{ .Actor.ID }}' | while read -r container_id; do
# Short sleep to ensure PID is ready in some edge cases
sleep 1
apply_limit "${container_id::12}"
done
[Unit]
Description=Docker Speed Limit
After=docker.service network.target
Requires=docker.service
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/docker_speed_limit.sh
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target

Limiting the TX speed of docker containers

Inspired by docker-tc.

It works by entering the container's network namespace and limiting the speed of eth0.

Therefore, it may only work on containers that connect only one bridge network.

Usage:

  1. You should have nsenter and tc command.

  2. Place the two files, set executable permission, and start the systemd service.

  3. Run container with these labels:

docker run -it --rm --name=iperf3-server -p 5201:5201 \
  --label dsl.txlimit=1 \
  --label dsl.txrate=35mbit \
  --label dsl.txburst=16kb \
  --label dsl.txlatency=100ms \
  networkstatic/iperf3 -s
services:
  iperf3-server:
    ...
    labels:
      - dsl.txlimit=1
      - dsl.txrate=35mbit
      - dsl.txburst=16kb
      - dsl.txlatency=100ms
    ...

Parameters

  • dsl.txrate: see RATES in this page
  • dsl.txburst, dsl.txlatency: see burst and latency in this page
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment