TCP Congestion Control Explained

Why does internet speed swing up and down even on a stable connection? TCP is quietly negotiating with the network the whole time.

Why congestion control exists

The internet is a shared resource -- many connections pass through the same routers and links. If every sender pushed data as fast as possible, router buffers would overflow, causing a spike in packet loss and retransmissions that can collapse throughput for everyone. Congestion control is the mechanism TCP uses to throttle itself before that happens.

The congestion window (cwnd)

The congestion window is the amount of data a sender can put on the wire before waiting for acknowledgment. A larger window means higher throughput, but push it too far and the network can't keep up, causing loss. Nearly every congestion control algorithm boils down to a rule for growing and shrinking this one number.

Slow start: probing for capacity

A new connection starts with almost no information about the network, so TCP begins with a small window and doubles it every time acknowledgments arrive successfully. This exponential growth quickly finds a rough estimate of available capacity, until a loss event or a preset threshold ends slow start and hands off to the more cautious congestion-avoidance phase.

Congestion avoidance: back off on loss

Classic TCP treats packet loss as a signal that the network is congested. When loss is detected, the window is cut sharply (often by half), then grown slowly and linearly as long as no more loss occurs. Repeating this cut-and-grow cycle produces the characteristic sawtooth pattern seen in throughput graphs.

Reno and New Reno: the classic loss-based algorithms

TCP Reno, dating to the 1990s, combines slow start, congestion avoidance, and fast retransmit/fast recovery to react quickly to loss. New Reno improved recovery when multiple packets are lost in one window. Both struggle on very high-bandwidth links, where recovering slowly after a loss leaves a lot of capacity unused.

Cubic: Linux's default for high-bandwidth links

Cubic, the default algorithm in the Linux kernel, grows the window along a cubic (third-degree) curve over time. After a loss it recovers quickly toward the previous window size, then increases more cautiously near the point where the last loss occurred. This lets it use high-bandwidth, long-delay links far more efficiently than Reno-style algorithms.

BBR: measuring bandwidth and delay directly

Loss-based algorithms like Reno and Cubic only react once a router's buffer is already full -- a limitation tied to the bufferbloat problem. BBR, developed at Google, instead continuously estimates the real bottleneck bandwidth and the lowest recent round-trip time, aiming to hold the ideal sending rate before loss ever happens. It powers many of Google's own services and is available as an option in the Linux kernel.

Where it came from: the 1986 slowdowns and Van Jacobson's fix

In the mid-1980s the early internet suffered episodes called congestion collapse, in which throughput on some links fell dramatically because senders kept retransmitting lost data and made the overload worse. Van Jacobson's 1988 paper, Congestion Avoidance and Control, introduced the slow start and congestion avoidance ideas that became the foundation of TCP's behavior. Modern specifications such as RFC 5681 describe these mechanisms in standard form.

The slow start threshold (ssthresh)

ssthresh is the window size at which TCP stops doubling and switches to linear growth. After a loss, it is typically set to about half of the window in use at the time, so the connection restarts growth quickly up to a safer level and then proceeds carefully. Think of it as TCP's memory of roughly where trouble started on this path.

Bandwidth-delay product: how much data fits in the pipe

The amount of data in flight needed to keep a link fully used equals the link's bandwidth multiplied by its round-trip time. For example, a 100 Mbit/s path with a 50 ms round-trip time can hold about 625,000 bytes in flight (100,000,000 bits per second divided by 8, times 0.05 seconds). If the window is smaller than that, the sender sits idle waiting for acknowledgments; that is why long-distance, high-speed links need large windows and why older algorithms struggle there.

Initial window: how many packets to send first

A new connection cannot wait for information about the network, so it starts with a small number of segments. RFC 6928, published in 2013, allows an initial window of up to 10 segments, which lets small web pages and API responses finish in one or two round trips instead of several. Larger initial windows are a trade-off, since a burst of ten packets from many senders at once can still overload a small buffer.

Bufferbloat and active queue management

Routers and modems with very large buffers hold packets instead of dropping them, so a loss-based algorithm sees no signal until the queue is already long, and latency for everyone sharing the link rises sharply. Active queue management schemes such as CoDel and fq_codel drop or mark packets early and can keep delay low even when the link is busy. If your video calls stutter when someone is uploading a large file, bufferbloat is a likely cause.

Congestion control is just one part of TCP's reliability toolkit

Alongside congestion control, TCP relies on sequence numbers, acknowledgments, and retransmission timers to guarantee that data arrives complete and in order. Congestion control specifically addresses network-wide fairness and stability -- without it, a handful of aggressive senders could degrade a shared link for everyone else.

You can often see which algorithm you're using -- but rarely change it

Most consumer operating systems and browsers don't expose a control for switching congestion algorithms, since the setting is usually more consequential on servers than on client devices. Linux servers can typically list and switch available algorithms (such as Cubic or BBR) through kernel parameters, which is why the choice matters most for the operators of busy websites, video platforms, and CDNs.

How to see what your Linux machine is doing

On Linux, the command sysctl net.ipv4.tcp_congestion_control shows the default algorithm, and net.ipv4.tcp_available_congestion_control lists the ones loaded. The ss tool with the -i option prints per-connection details such as the current congestion window, round-trip time, and retransmission counts, which is useful when diagnosing a slow transfer. Changing the default usually requires administrator rights and is applied system-wide, so test on a non-critical machine first and measure with the traffic you actually serve.

QUIC and the move of congestion control into applications

QUIC, standardized as RFC 9000 in 2021, runs on top of UDP and implements its own loss recovery and congestion control inside the application library, rather than relying on the operating system. This lets browsers and servers update algorithms without waiting for a kernel upgrade, and the specification describes a default controller similar to New Reno, while implementations commonly offer Cubic or BBR as well. For users it means the same principles described on this page apply to HTTP/3 traffic, even though the code is in a different place.

Frequently Asked Questions

Can I switch which congestion control algorithm my device uses?

On Linux, yes -- the kernel exposes a setting to choose between algorithms like Cubic and BBR. Windows, macOS, and most mobile operating systems don't expose this to end users, and in practice the server side of a connection usually has more influence over perceived speed than the client side.

Does congestion control apply to video calls and game traffic sent over UDP?

UDP itself has no built-in congestion control, so a poorly-behaved UDP application can keep pushing data even when the network is congested, hurting everyone sharing that link. Modern protocols built on top of UDP, such as QUIC, implement their own congestion control logic to behave more responsibly.

Is packet loss always a sign of congestion?

No. Loss can come from congestion, but also from wireless interference, faulty cables, or overloaded devices. Classic TCP assumes loss means congestion, which is why it can slow down needlessly on poor wireless links.

Why does a download start slowly and then speed up?

That is slow start at work. The connection begins with a small window and grows it quickly, so small files may finish before the transfer has reached its full speed. This is also one reason why many short connections are often slower than a few long ones, and why connection reuse matters in protocols such as HTTP/2.

Does a faster internet plan fix slow single downloads?

Not always. A single connection can be limited by the round-trip time, the window size, or loss on the path, rather than by the plan's bandwidth. A download from a distant server might not fill your connection even if the plan is large, while a nearby server or a CDN could.

What does the sawtooth pattern in a throughput graph mean?

It is the visual result of additive growth followed by a sharp cut after a loss. A smoother or flatter line usually indicates a different algorithm, such as one that adjusts based on delay or measured bandwidth, or a path with plenty of spare capacity.