§9.2
Chapter 09 · Real-Time Traffic Optimization for Low Latency and Jitter

§9.2Packetization, MTU Considerations, and Avoiding Fragmentation

RFC 9000

Learning objective. Understand the datagram-size budget QUIC works within: the 1200-byte floor, the real usable payload after headers, and why avoiding IP fragmentation matters, so a real-time application can size its messages to fit one packet, since unreliable datagrams cannot be fragmented.

The size budget of one datagram #

Every QUIC packet travels inside one UDP datagram inside one IP packet, and each layer takes its cut. The floor is fixed by the spec: the maximum datagram size is the largest UDP payload the path can carry in a single datagram, and "QUIC MUST NOT be used if the network path cannot support a maximum datagram size of at least 1200 bytes" ([RFC 9000 §14]). QUIC assumes a minimum IP packet of 1280 bytes, which after headers leaves 1232 bytes for IPv6 (or 1252 for IPv4) of UDP payload ([RFC 9000 §14]).

where the bytes go in one unfragmented IPv6 datagram (min IP packet = 1280 B) IP packet — 1280 B IPv6 40 UDP 8 QUIC datagram (UDP payload) = 1232 B · the maximum datagram size inside it: one 1-RTT (short-header) packet carrying a DATAGRAM frame hdr ~11 type 0x30 usable Datagram Data ≈ 1200 B · must fit here — DATAGRAM frames cannot be fragmented AEAD tag 16 1200 B floor QUIC MUST NOT use a path that can't carry 1200 B; Initials are padded to it (RFC 9000 §14). DF bit set — no IP fragmentation a packet above the PMTU is dropped, not split; DPLPMTUD probes discover the real limit (§14.3). larger PMTUs (Ethernet ~1500 → ~1452 usable) raise the payload; the floor never drops below 1200.
Fig. 9.2-1The datagram size budget. A 1280-byte IPv6 packet gives 1232 bytes of UDP payload (the maximum datagram size); inside it, a 1-RTT packet's header and 16-byte AEAD tag plus the DATAGRAM frame's type byte leave roughly 1200 bytes of usable payload. That payload cannot be fragmented, and the whole scheme rests on a 1200-byte floor with IP fragmentation disabled.RFC 9000 §14, §14.2

Working down the budget: from the 1232-byte datagram, a 1-RTT short-header packet spends about a dozen bytes on its header and 16 bytes on the AEAD authentication tag (Chapter 2), and a DATAGRAM frame adds a one-byte type. What remains (roughly 1200 bytes) is what the application actually gets in a single unfragmented datagram. A larger path MTU (Ethernet's ~1500 bytes gives ~1452 usable) raises that, but the 1200-byte floor never drops.

Why fragmentation is the enemy #

QUIC works hard to keep each datagram inside the path MTU because IP-layer fragmentation is banned: "UDP datagrams MUST NOT be fragmented at the IP layer. In IPv4 the Don't Fragment (DF) bit MUST be set if possible" ([RFC 9000 §14]). The reason is reliability and performance — a fragmented datagram is lost entirely if any fragment is dropped, and many middleboxes discard IP fragments outright. So a QUIC packet larger than the path MTU is not split; it is simply dropped. Every non-probe packet "SHOULD be sized to fit within the maximum datagram size to avoid the datagram being fragmented or dropped" ([RFC 9000 §14.2]).

To find how large it can safely go, QUIC uses Datagram Packetization Layer PMTU Discovery (DPLPMTUD, [RFC 9000 §14.3]): it sends occasional PMTU probes (padded packets larger than the current size) and watches whether they are acknowledged ([RFC 9000 §14.4]). A probe that is lost is not treated as congestion ("Loss of a QUIC packet that is carried in a PMTU probe... SHOULD NOT trigger a congestion control reaction", [RFC 9000 §14.4]); it just means the path won't carry that size. Endpoints also "MUST ignore an ICMP message that claims the PMTU has decreased below QUIC's smallest allowed maximum datagram size" and "MUST NOT increase the PMTU based on ICMP messages" ([RFC 9000 §14.2.1]). ICMP is advisory and spoofable, so QUIC trusts its own probing.

Two size limits, both endpoint-declared #

Beyond the path MTU, two transport parameters bound datagram size, and it is worth keeping them straight (§7.1):

  • max_udp_payload_size (0x03, default 65527). The largest UDP payload an endpoint is willing to receive. It "is a property of the endpoint and not the path" ([RFC 9000 §18.2]): it is buffer space the receiver dedicates to incoming packets, not a statement about what the network can carry. It caps datagram size the same way the path MTU does, from the receiver's side.
  • max_datagram_frame_size (0x20, default 0). From the DATAGRAM extension: the largest DATAGRAM frame an endpoint will accept, and the switch that enables the feature at all ([RFC 9221 §3], and §9.3). Zero means "no datagram support."

The effective usable datagram payload is the minimum of all three constraints (the path's current PMTU, the peer's max_udp_payload_size, and the peer's max_datagram_frame_size) less the QUIC packet and frame overhead ([RFC 9221 §5]).

Why this matters most for real-time #

For a bulk stream, packetization is invisible: QUIC chops the byte stream into whatever fits and reassembles it. For unreliable datagrams it is a hard application constraint, because a DATAGRAM frame "cannot be fragmented" ([RFC 9221 §5]) — it must fit whole in a single QUIC packet. A media codec that emits a 3 KB video frame cannot hand it to QUIC as one datagram on a 1200-byte path; the application must split it into ~1200-byte pieces and carry its own reassembly and loss-concealment, because QUIC will not. This is the price of skipping reliable streams (§9.3): you take back packetization along with loss handling.

Worked example: fitting a media frame #

Suppose an audio-plus-video real-time app on a path with a discovered PMTU of 1400 bytes (usable datagram payload after headers ≈ 1360 bytes). Audio packets at ~400 bytes fit comfortably, one codec frame per datagram — ideal, since each is independently decodable and a loss drops exactly one frame. A 4 KB video keyframe does not fit: the application must split it into four ~1000-byte application-level fragments across four datagrams, tag them so the receiver can reassemble, and decide what to do when one is lost (conceal, or request a new keyframe). Had the path MTU been the 1200-byte floor instead, the fragments would simply be smaller and more numerous. The transport gives a fixed-size envelope; sizing the payload to it is the application's job.

Note

The 16-byte AEAD tag is easy to forget in the budget. Every 1-RTT packet is authenticated, so 16 bytes of every datagram are the integrity tag, not payload (§2.2). On the 1200-byte floor that is more than 1% of the datagram spent on the tag. That is usually negligible, but for tiny, high-rate real-time packets (say 100-byte audio frames at hundreds per second) the fixed per-packet overhead (headers plus tag) becomes a real fraction of the bytes on the wire, which is an argument for not making packets too small.

In practice

editorial Let the library run DPLPMTUD and read the discovered PMTU rather than assuming 1500. Many real paths (tunnels, VPNs, some mobile carriers) carry less, and a packet sized for Ethernet will be silently dropped on them, appearing as mysterious loss. For a datagram-based real-time protocol, size your application messages to the discovered usable payload minus a safety margin, keep your own fragmentation/reassembly for anything larger, and prefer one-codec-frame-per-datagram so a single loss damages exactly one frame. Never rely on IP fragmentation to bail you out. It is disabled, and where it isn't, it makes loss worse.

Takeaways #

QUIC lives within a datagram-size budget floored at 1200 bytes, yielding ~1200 usable payload bytes after headers and the 16-byte AEAD tag, discovered upward by DPLPMTUD probing and never reliant on IP fragmentation, which is disabled. Three limits (path PMTU, max_udp_payload_size, and max_datagram_frame_size) jointly cap the usable size. For real-time traffic this is a first-class design constraint because unreliable DATAGRAM frames cannot be fragmented, so the application must size and split its own messages. Those unreliable datagrams, and when to prefer them over reliable streams, are §9.3.