TCP vs UDP: What's the Difference?
A practical look at how TCP and UDP trade reliability for speed, and how to pick the right one for your service.
Key takeaways
- TCP is connection-oriented and guarantees every byte arrives once, in order, with flow and congestion control, at the cost of a handshake and head-of-line blocking.
- UDP sends independent datagrams with an 8-byte header and no delivery promise.
- Use TCP by default; use UDP for real-time media, DNS-style single exchanges, or QUIC.
On this page
TCP gives you a reliable, ordered byte stream: a handshake first, then every byte delivered exactly once and in sequence, with retransmission and congestion control. UDP gives you fire-and-forget datagrams with an 8-byte header and no promises. Use TCP unless stale data is worthless to you; then use UDP and own reliability yourself, or use QUIC.
Verified against
TCP per RFC 9293, UDP per RFC 768, QUIC per RFC 9000 and HTTP/3 per RFC 9114, plus the iproute2 ss(8) man page. Checked October 2026.
Both sit on top of IP and move bytes between machines. The difference is what they promise about those bytes, which is why your database uses one and your video call uses the other.
If you want the wider context around addressing, routing, and the layers below this, start with our networking fundamentals guide. This post zooms in on the transport layer.
The core trade-off
TCP is connection-oriented. Before any data moves, the two sides agree to talk, and then TCP guarantees that every byte arrives, in order, exactly once. If a packet goes missing, TCP notices and resends it. If packets arrive out of order, TCP holds them and reassembles the stream before handing it up. It also manages flow control, so a fast sender does not overwhelm a slow receiver, and congestion control, so the network itself does not collapse under load. That is a lot of machinery, and it costs you overhead and latency.
UDP is connectionless. It takes your data, wraps it in a small header, and fires it at the destination. There is no handshake, no acknowledgment, no reordering, and no promise that anything arrives at all. What you gain is speed and simplicity. RFC 768 defines a fixed 8-byte UDP header; TCP's header is 20 bytes minimum and up to 60 with options, and there is a round trip of setup before the first byte of real data.
Here is the shape of it side by side, with QUIC included because it is now the third answer:
| Protocol | Connection setup | Delivery | Ordering | Congestion control | Header | Typical uses |
|---|---|---|---|---|---|---|
| TCP | 3-way handshake (1 RTT) | Reliable, retransmits | Single ordered stream | Yes, in the kernel | 20-60 bytes | HTTP/1.1, HTTP/2, databases, SSH |
| UDP | None | Best effort | None | None, app's job | 8 bytes | DNS, DHCP, VoIP, games, telemetry |
| QUIC (over UDP) | Combined with TLS 1.3, 0-RTT on resume | Reliable per stream | Per stream | Yes, in userspace | UDP + QUIC headers | HTTP/3 |
The three-way handshake
TCP's connection setup is the famous three-way handshake. The client sends a SYN (synchronize) with an initial sequence number. The server replies with SYN-ACK, acknowledging the client and sending its own sequence number. The client sends a final ACK, and the connection is open. That is one full round trip before any application data moves, which matters when you are opening thousands of short connections.
Teardown is similar. Each side sends a FIN and the other acknowledges it, and the side that closes first then sits in TIME-WAIT for twice the maximum segment lifetime. This is why connection reuse and keep-alive settings matter so much for busy services: setup and teardown are pure overhead you want to amortize.
What reliable and ordered actually costs
The guarantees are not free. Every segment TCP sends must be acknowledged, and if an ACK does not come back in time, TCP retransmits. That adds latency, and it means a single lost packet can stall everything behind it. This is head-of-line blocking: because the stream must be delivered in order, packet number 5 sits in the buffer waiting even after packets 6 through 10 have already arrived, all because packet 4 got dropped and has to be resent first.
For a file transfer or an API response, this is exactly what you want. You would rather wait than receive corrupt or scrambled data. For a live voice call, waiting on a retransmit is worse than just skipping the lost audio, which is the whole reason UDP exists.
What UDP gives up and gains
UDP hands the hard problems back to you. There is no retransmission, so if you need reliability, your application builds it. There is no ordering, so if sequence matters, you number your own packets. There is no congestion control, so a badly behaved UDP application can flood a link.
In exchange you get the lowest possible latency and full control. For real-time media, dropping a stale packet and moving on beats waiting. For a single request-response like a DNS lookup, opening a TCP connection would cost more than the query itself. UDP lets you send one packet, get one back, and be done. (DNS does fall back to TCP for responses too large for a datagram and for zone transfers, which is a classic firewall gotcha covered in our DNS troubleshooting guide.)
Real use cases
TCP: HTTP/1.1 and HTTP/2, REST and gRPC APIs, database connections, SSH, and email (SMTP, IMAP). Anything where correctness and completeness beat raw speed.
UDP: DNS and DHCP for quick single-shot exchanges, VoIP and video conferencing, live video streaming, and online gaming where fresh data matters more than complete data. QUIC, the transport under HTTP/3, is also built on UDP.
QUIC and HTTP/3 moving the web to UDP
The web itself is drifting toward UDP. QUIC (RFC 9000), which carries HTTP/3 (RFC 9114), runs on top of UDP and then rebuilds reliability, ordering, and congestion control in userspace rather than in the kernel's TCP stack. Why bother? Because building on UDP lets QUIC fix TCP's head-of-line blocking by giving each stream its own ordering, fold the TLS 1.3 handshake into connection setup to cut round trips (with optional 0-RTT data on resumption, which RFC 9000 notes has no replay protection), and evolve without waiting for operating system kernels to update. UDP is not more reliable. Starting from a blank slate simply let the designers make better trade-offs than a decades-old TCP implementation allows.
How to see them
On a running host, ss shows both. The -t flag lists TCP sockets, -u lists UDP, and -n skips name resolution:
# TCP sockets that are established
$ ss -tn
# UDP sockets
$ ss -un
# Listening TCP and UDP sockets with the owning process
$ sudo ss -tulnp
# Watch actual packets on the wire for a given port
$ sudo tcpdump -n -i any port 53
$ sudo tcpdump -n -i any tcp port 443
Watch a DNS lookup with tcpdump and you will see a single request and a single reply. Watch an HTTPS load and you will see the SYN, SYN-ACK, ACK handshake before any encrypted data flows. If the HTTPS load shows UDP 443 instead, the browser negotiated HTTP/3.
Which should you use?
Ask one question: does missing or reordered data break your application? If yes, use TCP and let the protocol do the hard work. Databases, APIs, file transfers, and remote shells all fall here. If stale data is worthless and you would rather drop it than wait, use UDP and accept that reliability is your job. Real-time media, telemetry, and discovery protocols fall here.
If you land on TCP and need to squeeze more throughput out of it, our guide to Linux TCP tuning covers the kernel knobs worth turning.
Questions people ask
Is UDP faster than TCP?
UDP has less overhead: no handshake, no acknowledgments, an 8-byte header instead of 20 or more. On a clean network the throughput difference is often small, but latency differs when packets are lost, because TCP stalls the whole stream while it retransmits. UDP lets the application skip the lost packet. That is why voice, video, and games prefer it, and why "faster" really means "never waits."
Does HTTP/3 use TCP or UDP?
HTTP/3 runs over QUIC, and QUIC runs over UDP, usually on port 443. QUIC rebuilds reliability, per-stream ordering, and congestion control in userspace, and combines the TLS 1.3 handshake with connection setup. Clients typically discover HTTP/3 support from an Alt-Svc header on an HTTP/1.1 or HTTP/2 response, so a firewall that blocks UDP 443 silently pushes everything back to TCP.
Does DNS use TCP or UDP?
Both. Ordinary queries go over UDP port 53 because one request and one reply fit in single datagrams. DNS switches to TCP port 53 when a response is too large for UDP (common with DNSSEC), and zone transfers always use TCP. Blocking TCP 53 on a firewall is a frequent cause of intermittent resolution failures for large records.
The call we'd make
Default to TCP. For the overwhelming majority of services, the reliability is worth the overhead, and modern connection reuse makes the handshake cost mostly disappear. Reach for UDP only when you have a concrete real-time or single-shot reason, and when you are ready to own the reliability logic yourself. And if you are building something new on the web, look hard at HTTP/3 and QUIC: it gives you UDP's flexibility with the reliability rebuilt in for you.
Sources
Cite this article
Kiril Urbonas (2026). TCP vs UDP: What's the Difference?. DevOpsNess. Updated 2026-10-31. https://www.devopsness.com/blog/tcp-vs-udp
[TCP vs UDP: What's the Difference?](https://www.devopsness.com/blog/tcp-vs-udp) (Kiril Urbonas, DevOpsNess, updated 2026-10-31)
More from Infrastructure
Explore more articles in this category
Terraform Plan Wants to Recreate Your Production Database? Read This First
The plan says -/+ next to your RDS instance and one attribute ends in "forces replacement". Why it happens, how to undo it without data loss, and the CI gate that stops it reaching apply.
How to Stop Anyone From Deleting Your Production Database
GitLab lost six hours of data to one command on the wrong host. Linear lost five hours to one TRUNCATE CASCADE in a reviewed migration. The six guardrails that make that kind of mistake hard to make and cheap to recover from.
Kubelet Won't Start After Upgrading to Kubernetes 1.35? It's cgroup v1
Since Kubernetes 1.35 the kubelet refuses to start on cgroup v1 nodes by default. How to check every node in one command, the safe migration, and the temporary override with an expiry date.
You might have missed
Evergreen posts worth revisiting.