Help Center

Step-by-step guides for servers, networks and your account.

Network & IP

Linux Network Tuning for Long-Distance Connections: TCP Buffers, BBR + fq, MTU/MSS and sysctl

Linux Network Tuning for Long-Distance Connections: TCP Buffers, BBR + fq, MTU/MSS and sysctl

A single TCP connection over a long, high-latency path (for example a VPS in Tokyo, Seoul or Moscow serving users in Europe or mainland China) is often limited by the kernel’s socket buffers and congestion control, not by the 500 Mbps port. This guide shows how to measure the limit, apply a safe sysctl profile with larger TCP buffers and BBR + fq, fix MTU/MSS problems on tunnels, test before and after with iperf3, and roll everything back.

If you only want to switch on BBR, the short guide How to enable BBR is enough. This article goes further and explains the buffers and MTU side as well. The commands work on Debian 12/13, Ubuntu 22.04/24.04 and Rocky Linux/AlmaLinux 9, all of which ship kernels with BBR.

Why distance limits the speed of one connection

TCP can only have a certain amount of unacknowledged data “in flight”. That amount is the bandwidth-delay product (BDP): bandwidth × round-trip time (RTT). If the window allowed by the buffers is smaller than the BDP, the connection waits for acknowledgements and never fills the link, however fast the port is.

Worked example: a 500 Mbps port and an RTT of 180 ms.

  • BDP = 500,000,000 bit/s × 0.18 s = 90,000,000 bits = about 11.25 MB.
  • The usual Linux default maximum receive buffer (tcp_rmem) is 6 MB, and part of it is reserved for overhead. 6 MB ÷ 0.18 s ≈ 280 Mbps at the very best, and in practice often 150–200 Mbps per connection.
  • With a 32 MB ceiling, the theoretical limit at 180 ms rises above 1 Gbps, so the buffer is no longer the bottleneck on a 500 Mbps port.

Packet loss makes this worse: the classic CUBIC algorithm cuts its sending rate sharply on every loss, and on a long path it takes a long time to grow back. BBR estimates the available bandwidth and RTT instead, so it copes much better with the occasional loss of long international routes. Measure your own RTT with ping, because it depends on where your users are.

About the route itself: JUSTG Moscow uses China Telecom CN2 GIA, which gives a cleaner path to mainland China; Tokyo and Seoul use Asia-optimized routes. A good route lowers loss and jitter, but physics still sets the RTT, so buffer tuning helps on every location.

Step 1: Check your current settings

uname -r
sysctl net.ipv4.tcp_congestion_control net.ipv4.tcp_available_congestion_control
sysctl net.core.default_qdisc net.core.rmem_max net.core.wmem_max
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem net.ipv4.tcp_mtu_probing
ip -br link
tc qdisc show

Note the kernel version, the current congestion control (often cubic), the default qdisc (often fq_codel), the buffer values and the name of your network interface (eth0, ens3 and so on; replace eth0 below with yours).

Step 2: Save a backup and look for conflicting files

sudo sysctl -a 2>/dev/null | grep -E 'tcp_(rmem|wmem|congestion_control|mtu_probing|notsent_lowat)|core\.(rmem_max|wmem_max|default_qdisc|somaxconn)' | sudo tee /root/sysctl-before.txt
grep -rnE 'congestion|qdisc|rmem|wmem' /etc/sysctl.conf /etc/sysctl.d/ 2>/dev/null

The first command saves the current values to /root/sysctl-before.txt so you can restore them later. The second shows other files that already set the same keys. Files in /etc/sysctl.d/ are applied in name order and /etc/sysctl.conf (if present) is applied last, so an old line there can silently override your new file. Remove or comment out duplicates.

Debian 13 no longer reads /etc/sysctl.conf by default, which is one more reason to put your settings in a file under /etc/sysctl.d/.

Step 3: Create the tuning profile

sudo tee /etc/sysctl.d/99-network-tuning.conf > /dev/null <<'EOF'
# Queueing discipline and congestion control
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# Socket buffer ceilings (32 MB) for high-RTT paths
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_rmem = 4096 131072 33554432
net.ipv4.tcp_wmem = 4096 16384 33554432

# Recover from PMTU black holes
net.ipv4.tcp_mtu_probing = 1

# Keep unsent data in the send buffer small (lower latency)
net.ipv4.tcp_notsent_lowat = 131072

# Larger accept queue for busy services
net.core.somaxconn = 4096
EOF
echo tcp_bbr | sudo tee /etc/modules-load.d/bbr.conf

What each part does:

  • default_qdisc = fq and bbr: BBR for congestion control, with the fq queue for clean packet pacing. Since kernel 4.13 BBR also works without fq, but fq is still the recommended pair.
  • rmem_max/wmem_max and tcp_rmem/tcp_wmem: the three numbers are minimum, default and maximum bytes per socket. Linux autotuning grows each connection’s buffer only when it needs it, so a 32 MB ceiling does not mean every connection uses 32 MB. On the mini plan (512 MB RAM) you can use 16777216 (16 MB) instead; that still covers 500 Mbps up to about 150 ms RTT.
  • tcp_mtu_probing = 1: when the kernel detects a path MTU “black hole” (large packets silently dropped), it lowers the packet size for that connection instead of stalling.
  • tcp_notsent_lowat: limits how much not-yet-sent data waits in the socket, which keeps latency low for interactive traffic and HTTP/2 without reducing throughput.
  • somaxconn = 4096: the accept queue limit. It is already 4096 on kernels 5.4 and newer; the line only matters on older systems and documents the intent.

The last line makes sure the tcp_bbr module is loaded at boot before the sysctl values are applied.

Step 4: Apply and verify

sudo modprobe tcp_bbr
sudo sysctl --system
sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc net.ipv4.tcp_rmem net.ipv4.tcp_wmem

You should see bbr, fq and the new buffer values. Changing default_qdisc only affects queues created afterwards, so the existing interface keeps its old qdisc until a reboot. To switch it now:

sudo tc qdisc replace dev eth0 root fq
tc -s qdisc show dev eth0

Then open a new connection (for example a new SSH session or download) and look at it with ss, using the IP of the other side:

ss -tin dst 203.0.113.20

The output shows bbr, the measured rtt and the current congestion window for each connection. Connections opened before the change keep their old settings until they reconnect.

Step 5: Find and fix MTU/MSS problems

MTU trouble looks very different from slow buffers. Typical symptoms:

  • ping and small requests work, but large downloads or uploads hang after a few kilobytes;
  • SSH logs in, then freezes when a command prints a lot of output;
  • some HTTPS sites load partly or not at all, mostly through a VPN, WireGuard, GRE or PPPoE link.

The cause is usually a link with a smaller MTU than 1500 where the ICMP “fragmentation needed” messages are blocked. Test with the “don’t fragment” flag. A payload of 1472 bytes plus 28 bytes of headers equals a 1500-byte packet:

ping -c 3 -M do -s 1472 203.0.113.20
ping -c 3 -M do -s 1400 203.0.113.20
tracepath -n 203.0.113.20
ip link show dev eth0

If 1472 fails but 1400 works, lower the size step by step until it passes; the largest working size + 28 is your path MTU. tracepath also reports the pmtu along the route. Do not block all ICMP in your firewall: path MTU discovery depends on it.

If the server routes traffic for others (a WireGuard or other VPN server, a GRE tunnel, a NAT gateway), clamp the TCP MSS on forwarded connections so both ends agree on a segment size that fits the tunnel. With nftables (default on Debian 12/13 and Rocky/Alma 9):

sudo tee /etc/nftables-mssfix.nft > /dev/null <<'EOF'
table inet mssfix
delete table inet mssfix
table inet mssfix {
    chain forward {
        type filter hook forward priority mangle; policy accept;
        tcp flags & (syn | rst) == syn tcp option maxseg size set rt mtu
    }
}
EOF
sudo nft -f /etc/nftables-mssfix.nft
sudo nft list table inet mssfix

The rule rewrites the MSS in SYN packets to match the MTU of the outgoing route. To keep it after a reboot on Debian/Ubuntu, add include "/etc/nftables-mssfix.nft" at the end of /etc/nftables.conf and run sudo systemctl enable nftables. On Rocky/Alma with firewalld, do not enable the separate nftables service; use the WireGuard PostUp method below instead. The same rule with iptables (install the iptables package if needed):

sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

For WireGuard, the simplest persistent setup is in the wg-quick config (for example /etc/wireguard/wg0.conf), so the rule appears and disappears with the tunnel:

[Interface]
MTU = 1420
PostUp = iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
PostDown = iptables -t mangle -D FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

An MTU of 1420 is the WireGuard default and is safe on a normal 1500-byte path. For TCP over IPv4, MSS = MTU − 40, so 1420 gives an MSS of 1380. If the server only sends its own traffic and does not route for anyone, you usually do not need clamping: the kernel picks the MSS from the interface MTU, and tcp_mtu_probing handles the rest.

Step 6: Test before and after with iperf3

Run the same test before the change and after it, at a similar time of day. Install iperf3 on both ends (apt install iperf3 or dnf install iperf3):

# On the VPS (allow TCP 5201 in the firewall only for the test)
iperf3 -s

# On your own computer
iperf3 -c 203.0.113.10 -t 20            # upload: you -> VPS
iperf3 -c 203.0.113.10 -t 20 -R         # download: VPS -> you
iperf3 -c 203.0.113.10 -t 20 -R -P 4    # download, 4 parallel streams

How to read the result:

  • -R is the direction your website visitors and download users see, because the VPS is the sender. The sender’s congestion control and send buffer (on the VPS) matter most here.
  • -P 4 opens four connections. If four streams together are much faster than one, a single connection is limited by window size or loss, which is exactly what the buffers and BBR address. If both are equally slow, look at the route, the client’s own connection or the client’s receive buffer.
  • Your computer is the receiver in -R mode, so its own receive buffer also counts. Modern Windows, macOS and Linux autotune it, but an old router or a VPN on your side can still be the limit.
Keep tests short (20–30 seconds) and stop iperf3 -s with Ctrl+C when you are done, then close port 5201 again. Long full-speed tests count towards your traffic, and using more than 70% of the shared port for long periods is against the fair use rules.

Step 7: Roll back if needed

The cleanest way back is to delete the two files and reboot:

sudo rm /etc/sysctl.d/99-network-tuning.conf /etc/modules-load.d/bbr.conf
sudo reboot

Deleting the file and running sysctl --system alone does not reset the values; they stay active until a reboot. To restore without rebooting, set the values from your backup file. The common defaults on current Debian/Ubuntu kernels are:

cat /root/sysctl-before.txt
sudo sysctl -w net.ipv4.tcp_congestion_control=cubic net.core.default_qdisc=fq_codel
sudo sysctl -w net.ipv4.tcp_rmem="4096 131072 6291456" net.ipv4.tcp_wmem="4096 16384 4194304"
sudo sysctl -w net.core.rmem_max=212992 net.core.wmem_max=212992

If you also added the MSS rule, remove it with sudo nft delete table inet mssfix (or replace -A with -D in the iptables command) and take the include line or PostUp/PostDown lines out of the config.

FAQ

Will these settings make my ping lower?

No. RTT is set by distance and routing. Buffers and BBR let a single connection use more of the available bandwidth over that RTT, and fq with tcp_notsent_lowat helps keep queueing delay low under load, but the base latency stays the same.

Is it safe to use 32 MB buffers on a 1 GB plan?

Yes for normal workloads. The values are per-socket ceilings, autotuning only grows a buffer when the connection actually needs it, and the kernel’s global tcp_mem limit is sized from your RAM. On the 512 MB mini plan, 16 MB is a sensible choice.

The speed is still low after tuning. What should I send to support?

Open a ticket in Technical support with the server IP, the time with time zone, your iperf3 results with and without -R and -P 4, and mtr output in both directions. Our speed test guide explains how to collect them. JUSTG offers 24/7 AI support by ticket, and engineers follow up on issues that need a person.

Was this answer helpful?
Help Center
AlipayUnionPayVISAMastercardPayPalUSDTstripe