UDP: fire and forget, on purpose
What a datagram is, why some protocols choose no guarantees at all, what the sender has to do itself, the MTU trap, NAT and firewalls, and where UDP shows up.
On this page
UDP is what you get when you take IP and add just enough to reach a program instead of a machine: two port numbers and a checksum. No connection, no ordering, no retry. That sounds like a worse TCP. It is not. It is the right tool whenever a late answer is worth less than no answer, or when you want to build the guarantees yourself. Most of the internet's timing-sensitive traffic, and the newest version of HTTP, run on it.
The map
Read this first when short on time. Every branch is a section below.
A datagram: one message, one packet, no promises
A UDP datagram is a single message. You hand the kernel some bytes and an address, and the kernel puts them in one IP packet and sends it. That is all. The other side either gets the whole message in one read, or gets nothing.
The header is eight bytes:
| Field | Size | Meaning |
|---|---|---|
| Source port | 2 bytes | Which program sent it, so a reply can come back. May be zero if no reply is wanted. |
| Destination port | 2 bytes | Which program on the receiving machine gets it. |
| Length | 2 bytes | Header plus data, so the receiver knows where the message ends. |
| Checksum | 2 bytes | Detects corruption. Optional on IPv4, required on IPv6. |
Compare that with TCP's 20-byte header full of sequence numbers, ACK numbers, flags and a window. UDP has none of them because it does none of those things.
| TCP | UDP | |
|---|---|---|
| Before sending | Three-way handshake | Nothing. The first packet carries data. |
| Lost packet | Retransmitted, everything behind it waits | Gone. The next one arrives on its own. |
| Order | Delivered in order, always | Delivered in whatever order they arrive |
| Duplicates | Removed | Delivered twice |
| Boundaries | None: a byte stream | Kept: one send is one recv |
| Speed limit | Window and congestion control | None. You can flood a link in one line of code. |
| Header | 20 bytes or more | 8 bytes |
| One to many | No | Broadcast and multicast |
The one thing UDP gives you that TCP does not is the boundary. If you send three datagrams of 100 bytes, the receiver does three reads of 100 bytes. TCP would give it 300 bytes in whatever chunks the network felt like, and the TCP article explains why you then have to frame messages yourself. UDP frames them for you, because each one is a packet.
The theoretical maximum is 65,507 bytes of data. In practice anything above about 1,400 bytes gets split into IP fragments, which is a trap described below, so real protocols keep datagrams small.
- A DNS query is one datagram of about 40 bytes, and its answer is one datagram back. Two packets, no setup, done in one round trip.
- Wireshark shows a UDP conversation as a list of independent packets; there is no "stream" to follow because there is no connection.
ss -ulists UDP sockets. Most show no peer, because a UDP socket does not have one unless the program asks.
- Datagram
- One self-contained message in one packet. Delivered whole or not at all.
- Port
- A 16-bit number identifying a program on a machine. UDP and TCP have separate port spaces.
Why anyone chooses no guarantees
UDP is chosen for three reasons, and they are all versions of the same idea: TCP's guarantees have a cost, and sometimes the cost is worse than the problem it solves.
A late packet can be worthless. In a voice call, a packet holds 20 ms of audio. If it is lost and TCP retransmits it, it arrives 200 ms later, after the packets behind it have already been played. Worse, TCP would hold those later packets back until the lost one arrived, so the whole call stutters. UDP just drops the 20 ms, the codec papers over the gap, and the call continues. The same goes for a game's position updates and a video call's frames: the newest data matters, the old data does not.
The handshake is a whole round trip. DNS needs one question and one answer. On TCP that is a handshake, a request, a reply and a close: three round trips of packets for one question. On UDP it is two packets. When a page load starts with a DNS lookup, that round trip is on the critical path, as the packet journey shows.
You want to build something TCP is not. TCP is one ordered stream with the kernel's congestion control. If you want many independent streams so one loss does not stall the others, or a handshake that also does encryption, or a connection that survives a change of IP address, you cannot get there by tuning TCP. You can get there by building on UDP, in user space, and shipping the result in your app. That is exactly what QUIC is.
And there is a fourth: UDP can talk to many machines at once. A broadcast reaches everything on the local network, which is how a laptop with no address finds a DHCP server. Multicast sends one packet that the network copies to every subscriber. TCP, being a conversation between two ends, cannot do either.
sequenceDiagram autonumber participant C as Caller participant R as Listener C->>R: audio 0 to 20 ms C->>R: audio 20 to 40 ms C-xR: audio 40 to 60 ms (lost) C->>R: audio 60 to 80 ms Note over R: plays 0 to 40, hides the gap, plays 60 to 80 C->>R: audio 80 to 100 ms
- Zoom, Teams and WhatsApp calls are UDP, using RTP, and they fall back to TCP only when UDP is blocked, at which point call quality drops noticeably.
- Online games send the player's position 20 to 60 times a second over UDP; a lost update is replaced by the next one 16 ms later.
- HTTP/3 is UDP because Google wanted streams that do not block each other and a faster handshake, and could ship that in Chrome without waiting for every operating system to change TCP.
- Broadcast
- A packet addressed to every machine on the local network.
- Multicast
- A packet addressed to a group; the network copies it to every member that subscribed.
What the sender has to do itself
Everything TCP would have done for you is now your problem, and the first surprise is that "nothing" is not always an acceptable answer.
Reliability, if you need it. A protocol that must not lose data adds sequence numbers to its datagrams, has the receiver acknowledge them, and resends what is missing. That is TCP's job done again, but you get to choose which messages matter. A game retries the "you picked up the key" message and never retries a position update. RTP numbers every packet so the receiver can notice a gap and decide whether to care.
Congestion control, always. This one is not optional. TCP slows down when the network drops packets; UDP does not, so a program that sends as fast as it can will keep sending into a link that is already overflowing, and its packets and everyone else's will be dropped. A UDP sender on the open internet has to watch its own loss and back off. Real-time protocols do this by lowering the bitrate; QUIC reimplements TCP's algorithms. A UDP flood is how you get yourself blocked by an ISP.
The API. A UDP socket is socket(AF_INET, SOCK_DGRAM). There is no listen and no accept. A server just binds a port and calls recvfrom, which returns one datagram and the address it came from. It replies with sendto to that address. One socket serves every client, because there is no connection to keep per client.
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", 9999))
while True:
data, addr = sock.recvfrom(2048) # one datagram, whole, or truncated if bigger than 2048
sock.sendto(b"pong " + data, addr) # reply to whoever sent it, no connection needed
Two details bite. Each recvfrom returns exactly one datagram; if your buffer is smaller than the datagram, the rest is silently discarded, not saved for the next read. And a client can call connect on a UDP socket, which does not send anything on the wire; it just fixes the peer address so plain send and recv work, and lets the kernel report ICMP errors for that peer, such as "port unreachable".
- RTP puts a sequence number and a timestamp in every packet. The receiver uses them to reorder, detect loss, and play at the right pace. It never asks for a resend.
- QUIC carries ACK frames listing exactly which packet numbers arrived, and runs the same congestion algorithms as the Linux TCP stack, in the application.
- A DNS resolver does reliability the simplest way: if no answer comes in a second or two, it asks again, possibly a different server.
A UDP sender never learns that a packet was lost. There is no error, no return value, nothing. If your protocol needs to know, the receiver has to say so. Silence from the other end means "lost", "dropped by a firewall", "the process is dead", and "the machine is off", and you cannot tell which.
- Congestion control
- Slowing down when the network drops packets. TCP does it in the kernel; a UDP protocol must do it itself.
- recvfrom / sendto
- The UDP calls: receive one datagram with its sender's address, send one datagram to an address.
The MTU trap: keep datagrams small
A datagram bigger than the path can carry does not fail. It gets cut into IP fragments, and that is worse than failing.
flowchart TD
A["send 4,000 bytes in one datagram"] --> B["IP splits it into 3 fragments of about 1,480 bytes"]
B --> C{"All three arrive?"}
C -- yes --> D["Receiver reassembles, delivers 4,000 bytes"]
C -- no --> E["Receiver waits, gives up after 30 s, delivers nothing"]
E --> F["3 packets of bandwidth spent, 0 bytes delivered"]
An Ethernet frame holds 1,500 bytes. Take away 20 for IP and 8 for UDP and a datagram of 1,472 bytes fits in one packet. Anything larger is split into fragments by the sending kernel, or by a router on the way if some link is narrower. Fragments are reassembled only at the destination. If any one of them is lost, the whole datagram is lost. Many firewalls drop fragments outright because they cannot see the port numbers in the second and later pieces. And some paths, VPNs and tunnels especially, have an MTU well below 1,500, so a 1,472-byte datagram that was fine on your desk fragments in the field.
The rule every serious UDP protocol follows: keep datagrams at or below about 1,200 bytes, which fits through nearly every path on the internet without fragmentation, and use several datagrams for anything bigger. QUIC requires the path to carry 1,200 bytes and refuses to run otherwise. DNS historically capped answers at 512 bytes and switched to TCP for anything larger; modern resolvers advertise 1,232 as their limit for the same reason.
- DNS falls back to TCP when an answer does not fit, which is why port 53 TCP must be open too and why some DNSSEC-signed answers arrive over TCP.
- WireGuard sets its interface MTU to 1,420 so that after its own header the packet still fits a 1,500-byte link.
- A NFS mount over UDP with 8 KB reads fragmented every request; NFS moved to TCP largely for this reason.
- Fragmentation
- IP splitting one datagram into several packets because it exceeds a link's MTU. Reassembled only at the destination.
- MTU
- Maximum transmission unit: the largest packet a link carries. 1,500 on Ethernet, less through tunnels.
NAT and firewalls: no connection, but still a state
UDP has no connection, but every NAT and firewall on the path pretends it does, because that is the only way they can let replies back in. Understanding that fake state explains most "UDP works here but not there" problems.
When your first datagram leaves the home router, the router creates a mapping, exactly as it does for TCP in the packet journey: your private address and port become its public address and a chosen port. A reply to that public port is translated back. The difference is how the router decides the "connection" is over. TCP has FIN; UDP has nothing, so the router uses a timer, typically 30 seconds to a couple of minutes of silence, and then forgets the mapping. The next reply is dropped, and the far end thinks you vanished.
So every long-lived UDP protocol sends keepalives, small packets every 15 to 25 seconds, just to keep the mapping alive. WireGuard has a setting for it. Video calls do it automatically.
The same mapping makes peer-to-peer connections possible. If two laptops behind two home routers both send a datagram to each other's public address and port at the same time, each router sees an outgoing packet, creates a mapping, and then lets the other's packet in. This is hole punching. To do it they first need to learn their own public address and port, which a STUN server tells them by simply reporting where the packet came from. When it fails, because a router picks a new port for every destination, the call goes through a relay instead.
sequenceDiagram autonumber participant A as Laptop A participant NA as NAT A participant NB as NAT B participant B as Laptop B A->>NA: datagram to B's public address Note over NA: mapping created, packet forwarded B->>NB: datagram to A's public address Note over NB: mapping created, packet forwarded NA->>NB: A's packet arrives, NAT B now has a mapping, lets it in NB->>NA: B's packet arrives, NAT A has a mapping, lets it in Note over A,B: direct path open, keepalives every 20 s
Finally, some networks block UDP altogether: hotel Wi-Fi, some corporate networks, some mobile carriers. Every UDP-based application needs a plan for that day, which usually means falling back to TCP. Browsers try HTTP/3 and quietly use HTTP/2 over TCP if the UDP packets do not come back.
- WebRTC in every browser does STUN, hole punching and relay fallback (TURN) for video calls; that is what the ICE negotiation at the start of a call is.
- Tailscale and WireGuard use the same hole punching to connect two machines behind NATs directly, and fall back to a relay when it fails.
- Linux conntrack tracks UDP "connections" too;
conntrack -L -p udpshows them with their remaining timeout.
- NAT mapping
- The router's note that public port X belongs to private address and port Y. Created on the first outgoing packet, expired after idle time.
- Keepalive
- A small packet sent regularly to stop a NAT mapping from expiring.
- STUN
- A server that tells a client what public address and port its packets appear to come from.
- Hole punching
- Two machines behind NATs sending to each other at once so both routers open a mapping.
Where UDP lives
Most of the internet's plumbing runs on UDP. These are the ports and protocols worth recognising on sight.
| Protocol | Port | Why UDP |
|---|---|---|
| DNS | 53 | One question, one answer. Falls back to TCP for big answers. |
| DHCP | 67, 68 | A machine with no address yet has to broadcast; TCP cannot. |
| NTP | 123 | Time sync needs the packet's own timing to be predictable, not buffered by TCP. |
| SNMP | 161 | Polling thousands of devices; a connection each would be absurd. |
| syslog | 514 | Fire and forget log lines. A lost line is acceptable; a blocked logger is not. |
| RTP / RTCP | dynamic | Voice and video: late is worthless. |
| QUIC / HTTP/3 | 443 | A better transport, built in user space, shipped in the browser. |
| WireGuard | 51820 | A VPN tunnel; TCP inside TCP performs badly, so tunnels use UDP. |
| VXLAN | 4789 | Carrying Ethernet frames between hosts in a data centre; every overlay network in Kubernetes. |
| TFTP | 69 | Tiny file transfer for booting devices with no TCP stack yet. See the FTP article. |
| Games | various | Position updates at 60 Hz; the newest one wins. |
The pattern in the table: UDP wins when the message is small and self-contained, when timing matters more than completeness, when one machine talks to many, or when the protocol on top wants to own its transport. TCP wins when you want a stream of bytes delivered correctly and do not want to think about it.
- Kubernetes overlay networks (Flannel, Calico in VXLAN mode) wrap every pod-to-pod packet in a UDP datagram to carry it between nodes.
- Cloud provider load balancers support UDP as a separate listener type because there is no connection to balance; they hash on addresses and ports instead.
- Prometheus's statsd exporter and the original statsd receive metrics as UDP datagrams so a slow metrics server can never block the application.
The dangers: amplification and floods
Two properties of UDP make it the internet's favourite attack tool: the source address is not verified, and there is no handshake to prove the sender is who it says.
With TCP, an attacker who fakes a source address never completes the handshake, so no data flows. With UDP, a single spoofed datagram gets a full reply, sent to the faked address. If the reply is bigger than the question, the attacker has an amplifier: a 60-byte DNS query with a spoofed source can produce a 4,000-byte answer aimed at the victim. Thousands of open resolvers and NTP servers answering thousands of such queries per second is how the largest denial-of-service attacks of the 2010s were built. The defences are on the server side: rate limit responses, refuse to answer strangers, and keep replies small until the client proves it can receive them.
The other danger is simpler. UDP has no backpressure. A sender can emit datagrams faster than the receiver's socket buffer drains, and the kernel silently drops the overflow; netstat -su shows the count under receive errors. A burst from a fast producer to a slow consumer loses data with no error on either side.
- QUIC limits how much a server sends before the client has proved its address, to at most three times what the client sent, so it cannot be used as an amplifier.
- DNS resolvers implement response rate limiting and refuse recursion for anyone outside their own network, because open resolvers were the main amplifiers.
- Cloudflare and AWS Shield absorb UDP floods at the edge by having more bandwidth than the attacker; an origin server cannot.
Never write a UDP service that replies with more bytes than it received to a client it has not seen before. That is an amplifier waiting to be found. Answer small, or make the client send a cookie back first.
- Amplification attack
- Sending small spoofed requests to servers that send large replies to the victim's address.
- Spoofing
- Sending a packet with a false source address. Possible with UDP because nothing checks it.
Recap
- UDP is IP plus ports and a checksum: an 8-byte header, one message per packet, no connection, no ordering, no retry, no speed limit.
- It keeps message boundaries: one
sendtois onerecvfrom. TCP does not. - Choose it when a late packet is worthless (voice, video, games), when a handshake would cost more than the exchange (DNS, NTP), when you need one-to-many, or when you are building your own transport (QUIC).
- Reliability is yours to add if needed. Congestion control is yours to add always; a UDP sender that does not back off harms the whole link.
- A read returns one datagram; a small buffer silently truncates it. A UDP sender never learns of a loss unless the receiver says so.
- Keep datagrams around 1,200 bytes. Bigger ones fragment, and one lost fragment loses the whole message.
- NATs invent a connection with a timer. Send keepalives every 20 seconds or so, and expect some networks to block UDP entirely, so keep a TCP fallback.
- Hole punching plus STUN lets two machines behind NATs talk directly; WebRTC, Tailscale and WireGuard use it.
- DNS, DHCP, NTP, RTP, WireGuard, VXLAN and HTTP/3 all run on UDP.
- Spoofed sources and big replies make amplifiers. Reply small to strangers.
Questions
Try answering each one out loud before opening it. Lead with the one-line answer, then a couple of points, then one extra detail.
What does UDP add to IP, and what does it leave out?
It adds an 8-byte header with source and destination ports, a length and a checksum, so a packet reaches a program rather than just a machine; it leaves out connections, sequence numbers, acknowledgements, retransmission, ordering and congestion control.
- Each datagram is one message, delivered whole or not at all.
- Datagrams can be lost, duplicated or reordered and the sender is never told.
The checksum is optional on IPv4 and mandatory on IPv6.
Why do voice and video calls use UDP rather than TCP?
Because a late packet is worthless: TCP would retransmit a lost 20 ms of audio and hold back everything behind it for a full round trip, causing a stall, while UDP drops that slice and the call continues with a tiny glitch.
- RTP adds sequence numbers and timestamps so the receiver can reorder and detect loss without ever asking for a resend.
- Bitrate is lowered on loss, which is congestion control done the media way.
Calls fall back to TCP only when UDP is blocked, and quality drops when they do.
Why does DNS use UDP?
A lookup is one small question and one small answer, so UDP does it in two packets and one round trip, whereas TCP would need a handshake, the exchange and a close.
- The resolver handles reliability by simply asking again after a timeout.
- Answers too large for one datagram are fetched over TCP instead.
Modern resolvers cap UDP answers at 1,232 bytes to avoid fragmentation.
What is the one thing UDP guarantees that TCP does not?
Message boundaries: one send becomes exactly one receive, because each datagram is its own packet, whereas TCP delivers a byte stream that may split or merge writes.
- Protocols on TCP must add length prefixes or delimiters; protocols on UDP get framing for free.
- The price is that a datagram larger than the receive buffer is truncated silently.
Broadcast and multicast are the other things only UDP can do.
Which of TCP's jobs must a UDP application always do itself, even if it does not need reliability?
Congestion control: a UDP sender that does not slow down when packets are dropped keeps flooding an overloaded link, harming its own traffic and everyone else's.
- Media protocols lower their bitrate on loss; QUIC reimplements TCP's algorithms in user space.
- Reliability and ordering, by contrast, are optional and added per message type.
Networks and ISPs rate limit or block UDP senders that ignore this.
How does a UDP server handle many clients with no connections?
One socket bound to a port receives every client's datagrams with recvfrom, which returns the sender's address alongside the data, and replies with sendto to that address; there is no listen, no accept and no per-client socket.
- State per client, if any, is the application's own table keyed by address.
- A client may
connecta UDP socket to fix the peer address, which sends nothing but enables ICMP error reporting.
Load balancers spread UDP by hashing addresses and ports because there is no connection to pin.
Why should UDP datagrams stay under about 1,200 bytes?
Larger datagrams are split into IP fragments, and losing any one fragment loses the whole datagram with no notification; many firewalls also drop fragments, and tunnels lower the path MTU below 1,500.
- 1,200 bytes fits through almost every path on the internet unfragmented.
- QUIC requires 1,200 and DNS resolvers advertise 1,232 for the same reason.
NFS moved from UDP to TCP largely because 8 KB reads fragmented on every request.
How does a NAT handle UDP if there is no connection, and what does that mean for long-lived flows?
It creates a mapping on the first outgoing datagram, exactly as for TCP, but with no FIN to end it the mapping is dropped after a timeout of roughly 30 seconds to a few minutes of silence; long-lived UDP flows therefore send keepalives every 15 to 25 seconds.
- After expiry, replies from the far end are silently dropped.
- Linux conntrack shows UDP entries with their remaining timeout.
WireGuard exposes the keepalive interval as a setting because it matters behind home routers.
Explain hole punching and STUN.
Two machines behind NATs learn their public address and port from a STUN server, which simply reports where their packet came from, then send datagrams to each other at the same time; each NAT sees an outgoing packet and creates a mapping, so the other side's incoming packet is allowed through.
- The first packet in each direction may be dropped; the retry succeeds.
- NATs that choose a new port per destination defeat it, so a relay (TURN) is the fallback.
WebRTC, Tailscale and WireGuard-based tools all do this at connection start.
What is a UDP amplification attack and how does a server avoid being an amplifier?
An attacker sends small requests with the victim's address as the spoofed source to servers whose replies are much larger, so the victim receives many times the attacker's bandwidth; a server avoids it by replying with no more than it received until the client proves it can receive, by rate limiting, and by refusing to serve strangers.
- Open DNS resolvers and NTP servers were the classic amplifiers.
- QUIC caps a server's first reply at three times the client's bytes.
Spoofing works on UDP because there is no handshake to prove the source address.
Why did HTTP/3 move to UDP?
To build a transport (QUIC) that TCP could not become: independent streams so one loss does not stall the others, a handshake merged with encryption for fewer round trips, and connection migration across IP changes, all implementable in user space and shipped inside the browser instead of waiting for operating systems.
- UDP was the only way past middleboxes that only understand TCP and UDP.
- Browsers fall back to HTTP/2 over TCP where UDP is blocked.
QUIC does its own reliability and congestion control on top of UDP's nothing.
How can a UDP program detect that a packet was lost?
Only through its own protocol: sequence numbers that reveal a gap at the receiver, acknowledgements the sender waits for, or a timeout with no reply; the kernel reports nothing, and silence is indistinguishable from a dead peer or a firewall.
- A connected UDP socket can at least receive ICMP port unreachable errors.
- Receive buffer overflows show as receive errors in
netstat -su.
Whether to do anything about a loss is the protocol's choice; RTP notes it and moves on.