Make your own lag: latency, jitter and packet loss lab
0 / 8 steps
The goal
You will make your own lag on purpose, measure it, and take it away again. On one Linux machine you build a pretend second computer, add 100 ms of latencyIn plain words: How long a message takes to get there and back, like the wait between asking a question and hearing the answer.Technical: Round-trip time (RTT) in milliseconds. ping reports min/avg/max for each run.Click for the full glossary entry, then jitterIn plain words: Lag that keeps changing. A bus that is 10 minutes late is annoying; one that is sometimes early and sometimes 20 minutes late is worse, because you can’t plan for it.Technical: Variation in packet delay. RFC 3393 calls it “IP packet delay variation”. ping’s mdev and mtr’s StDev show it., then packet lossIn plain words: Messages that were sent but never arrived, like letters that get lost in the mail.Technical: The percentage of packets with no reply. ping and mtr both report it as a percentage.Click for the full glossary entry, and watch what each one does to ping, mtr and a download speed test. By the end you will know which number to look at when a game, a video call or a download goes wrong.
Your own VM only. Use a Linux virtual machine you own, with a snapshot first (the VM from Lab 00 or any throwaway Ubuntu). Everything in this lab happens on a private pretend link called lab0 that only exists inside the VM. Never add lag to a shared network or someone else’s connection.
Why this lab. “The internet is slow” can mean three different problems, and each one has a different fix. In Why Is My Game Lagging? you used ping to find where lag starts. Here you make each kind of lag yourself, so you learn what it looks like in the numbers. That is the heart of Network+ objective 5.4 (latency, jitter, packet loss, bandwidth).
The official standard for jitter, RFC 3393, avoids the word “jitter” because people use it to mean different things. I avoid it too. I just yell “LAG!” — Nyx
| The problem | What it feels like | Where you see it |
|---|---|---|
| Latency (high, but steady) | Game: you press a button and the action lands a moment late. Call: you keep talking over each other. | ping avg is high, but every line is close to the same number |
| Jitter (lag that jumps around) | Game: rubber-banding, where your character snaps back. Call: robotic or choppy voice. | ping mdev and mtr StDev are big; the times on each line keep changing |
| Packet loss (some messages never arrive) | Call: the picture freezes. Game: players teleport. Download: it crawls. | ping % packet loss, mtr Loss%, and a big drop in iperf3 speed |
Before you start. You need an Ubuntu VM (tested on Ubuntu 24.04) and a user that can run sudo. The lag tool, tc netem, is built into the Linux kernel, so this part only works on Linux; the ping box below shows how to measure lag on Windows, Mac and Android. Every output below is real, from the lab machine.
Steps
01Open a terminal and install the tools
A terminalIn plain words: A window where you type instructions to the computer instead of clicking. You type a command, press Enter, and it answers.Technical: A text shell (bash on Ubuntu). Commands run as your user; sudo runs one command as the administrator (root). is where all of this happens. On Ubuntu Desktop, press Ctrl + Alt + T together, or click Show Apps (the grid of dots, bottom-left) and type Terminal. On Ubuntu Server you are already in one after you log in. Type or paste each line after the $ and press Enter:
$ sudo apt update $ sudo apt install -y iproute2 iputils-ping iputils-tracepath mtr-tiny iperf3
When sudo asks for a password, type your own login password (nothing shows on screen while you type; that is normal). During the install a blue box asks “Start Iperf3 as a daemon automatically?” Choose No (use the arrow keys, then Enter): you will start it by hand only when you need it.
What you just installed. tc (part of iproute2) is the lag maker. ping and tracepath measure delay, mtr measures delay and loss together, and iperf3 measures speed between two machines you control.
02Build a pretend second computer
To measure lag you need two machines. Instead of a second VM, you make a network namespaceIn plain words: A pretend second computer inside your computer, with its own network card and address. Like a room with its own front door inside your house.Technical: A Linux network namespace: a separate copy of the network stack (interfaces, addresses, routes). ip netns manages them. called server and join it to your machine with a virtual cableIn plain words: A network cable made of software. Whatever goes in one end comes out the other.Technical: A veth pair: two linked virtual interfaces. Here lab0 stays on your machine and lab1 goes into the namespace.:
$ sudo ip netns add server $ sudo ip link add lab0 type veth peer name lab1 netns server $ sudo ip addr add 10.99.0.1/24 dev lab0 $ sudo ip link set lab0 up $ sudo ip netns exec server ip addr add 10.99.0.2/24 dev lab1 $ sudo ip netns exec server ip link set lab1 up $ ip -br addr show lab0 $ sudo ip netns exec server ip -br addr show lab1
What we saw (output from the test run):
$ sudo ip netns add server $ sudo ip link add lab0 type veth peer name lab1 netns server $ sudo ip addr add 10.99.0.1/24 dev lab0 $ sudo ip link set lab0 up $ sudo ip netns exec server ip addr add 10.99.0.2/24 dev lab1 $ sudo ip netns exec server ip link set lab1 up $ ip -br addr show lab0 lab0@if2 UP 10.99.0.1/24 fe80::f441:67ff:fe7a:2396/64 $ sudo ip netns exec server ip -br addr show lab1 lab1@if2 UP 10.99.0.2/24 fe80::1068:aaff:fe48:4ac8/64
Your machine is now 10.99.0.1 and the pretend server is 10.99.0.2, joined by a private cable. Both say UP. The long fe80:: part is an automatic IPv6 address; yours will be different, and you can ignore it here.
Why this matters. All the lag you add goes on lab0, a link that only exists inside your VM. Your real network connection is never touched, so you can’t lock yourself out.
03Measure normal first (your baseline)
Before you break anything, write down what “good” looks like. First check that no lag rule is on the link yet, then pingIn plain words: A quick “are you there?” knock on another device’s door that times how fast the answer comes back.Technical: Sends ICMP echo requests and reports each round-trip time, then min/avg/max/mdev and % loss. -c 10 means stop after 10.Click for the full glossary entry the server 10 times:
$ tc qdisc show dev lab0 $ ping -c 10 10.99.0.2 $ mtr -r -c 10 10.99.0.2
What we saw (output from the test run):
$ tc qdisc show dev lab0 qdisc noqueue 0: root refcnt 2 $ ping -c 10 10.99.0.2 PING 10.99.0.2 (10.99.0.2) 56(84) bytes of data. 64 bytes from 10.99.0.2: icmp_seq=1 ttl=64 time=16.9 ms 64 bytes from 10.99.0.2: icmp_seq=2 ttl=64 time=0.355 ms 64 bytes from 10.99.0.2: icmp_seq=3 ttl=64 time=0.271 ms ... 64 bytes from 10.99.0.2: icmp_seq=10 ttl=64 time=0.732 ms --- 10.99.0.2 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 9118ms rtt min/avg/max/mdev = 0.257/2.139/16.905/4.927 ms $ mtr -r -c 10 10.99.0.2 Start: 2026-10-24T02:18:00+0000 HOST: lab Loss% Snt Last Avg Best Wrst StDev 1.|-- 10.99.0.2 0.0% 10 1.1 0.8 0.4 2.0 0.5
noqueue means no lag rule is set. Read the last ping line from left to right: the fastest reply (min), the avg, the slowest (max), and mdev, which is how far the replies stray from the average (your jitter number). Under 1 ms is normal for a cable this short. The first reply is often slower (16.9 ms here), which pulls avg and mdev up on a short run; one slow first ping is normal, so judge by the rest. mtr gives the same story as a table: Loss%, Avg and StDev (jitter again).
Why this matters. “Is 40 ms bad?” has no answer without a baseline. Network teams record normal numbers so they can spot when something changes; Network+ calls these baseline metrics.
04Add latency: 100 ms on every message
Now make lag. This tells Linux to hold every message going out of lab0 for 100 milliseconds (a tenth of a second) before sending it, using netemIn plain words: A “bad internet” simulator built into Linux. You tell it how slow or unreliable to be, and it does exactly that.Technical: The network emulator queueing discipline (qdisc). tc qdisc add dev lab0 root netem ... attaches it to the outgoing side of lab0. It can add delay, jitter, loss, duplication, reordering and rate limits.:
$ sudo tc qdisc add dev lab0 root netem delay 100ms $ tc qdisc show dev lab0 $ ping -c 10 10.99.0.2 $ tracepath -n 10.99.0.2
What we saw (output from the test run):
$ sudo tc qdisc add dev lab0 root netem delay 100ms $ tc qdisc show dev lab0 qdisc netem 8001: root refcnt 3 limit 1000 delay 100ms $ ping -c 10 10.99.0.2 PING 10.99.0.2 (10.99.0.2) 56(84) bytes of data. 64 bytes from 10.99.0.2: icmp_seq=1 ttl=64 time=102 ms 64 bytes from 10.99.0.2: icmp_seq=2 ttl=64 time=101 ms 64 bytes from 10.99.0.2: icmp_seq=3 ttl=64 time=101 ms ... 64 bytes from 10.99.0.2: icmp_seq=10 ttl=64 time=101 ms --- 10.99.0.2 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 9019ms rtt min/avg/max/mdev = 100.870/101.218/102.249/0.407 ms $ tracepath -n 10.99.0.2 1?: [LOCALHOST] pmtu 1500 1: 10.99.0.2 104.978ms reached 1: 10.99.0.2 101.728ms reached Resume: pmtu 1500 hops 1 back 1
The average jumped from about 0.4 ms to 101 ms, but mdev is still tiny (0.4 ms): every reply is slow by the same amount. That is pure latency. Only the outgoing direction is delayed here (the reply comes straight back), so the round trip is 100 ms plus the normal fraction of a millisecond. tracepath shows the delay sits on hop 1, the only hop on this cable; on a real network it shows which hop the delay starts at, like tracert on Windows.
If you run the add line a second time you get Error: Exclusivity flag on, cannot modify. That only means a rule is already there. Use change to edit it, as the next step does.
What it feels like. 100 ms of steady delay: a game feels a beat behind your button presses, and on a video call you start talking over each other. Many online games show this number as “ping” in a corner of the screen.
05Add jitter: make the delay jump around
Keep the 100 ms but let each message be up to about 30 ms earlier or later than that:
$ sudo tc qdisc change dev lab0 root netem delay 100ms 30ms $ ping -c 20 10.99.0.2 $ mtr -r -c 20 10.99.0.2
What we saw (output from the test run):
$ sudo tc qdisc change dev lab0 root netem delay 100ms 30ms $ ping -c 20 10.99.0.2 PING 10.99.0.2 (10.99.0.2) 56(84) bytes of data. 64 bytes from 10.99.0.2: icmp_seq=1 ttl=64 time=106 ms 64 bytes from 10.99.0.2: icmp_seq=2 ttl=64 time=130 ms 64 bytes from 10.99.0.2: icmp_seq=3 ttl=64 time=96.6 ms 64 bytes from 10.99.0.2: icmp_seq=4 ttl=64 time=121 ms 64 bytes from 10.99.0.2: icmp_seq=5 ttl=64 time=77.3 ms 64 bytes from 10.99.0.2: icmp_seq=6 ttl=64 time=107 ms ... 64 bytes from 10.99.0.2: icmp_seq=20 ttl=64 time=125 ms --- 10.99.0.2 ping statistics --- 20 packets transmitted, 20 received, 0% packet loss, time 19046ms rtt min/avg/max/mdev = 73.602/100.830/130.455/16.826 ms $ mtr -r -c 20 10.99.0.2 Start: 2026-10-24T02:18:48+0000 HOST: lab Loss% Snt Last Avg Best Wrst StDev 1.|-- 10.99.0.2 0.0% 20 97.1 107.0 81.3 131.0 15.8
The average barely moved (still about 100 ms), but now replies swing from 74 to 130 ms, and mdev went from 0.4 to 16.8 ms. mtr agrees: StDev 15.8, best 81.3, worst 131.0. That spread is jitter. This is why you look at mdev and not only avg: two connections with the same average can feel completely different.
What it feels like. Jitter is what makes a game rubber-band (your character snaps back to where the server last saw it) and a call go robotic, because sound and movement arrive in uneven bursts. Apps hide a little jitter by waiting a moment before playing (a buffer), which adds latency. A steady 100 ms is often easier to live with than a connection that jumps between 20 and 130.
06Add packet loss: throw away 1 in 10
Swap the delay for loss: netem now drops each outgoing message with a 10% chance. Send 100 pings, five a second, and show only the summary (-q):
$ sudo tc qdisc change dev lab0 root netem loss 10% $ ping -c 100 -i 0.2 -q 10.99.0.2 $ tc -s qdisc show dev lab0 $ mtr -r -c 30 10.99.0.2
What we saw (output from the test run):
$ sudo tc qdisc change dev lab0 root netem loss 10% $ ping -c 100 -i 0.2 -q 10.99.0.2 PING 10.99.0.2 (10.99.0.2) 56(84) bytes of data. --- 10.99.0.2 ping statistics --- 100 packets transmitted, 88 received, 12% packet loss, time 20562ms rtt min/avg/max/mdev = 0.257/0.438/1.853/0.190 ms $ tc -s qdisc show dev lab0 qdisc netem 8001: root refcnt 3 limit 1000 loss 10% Sent 16546 bytes 147 pkt (dropped 12, overlimits 0 requeues 0) backlog 0b 0p requeues 0 $ mtr -r -c 30 10.99.0.2 Start: 2026-10-24T02:19:37+0000 HOST: lab Loss% Snt Last Avg Best Wrst StDev 1.|-- 10.99.0.2 6.7% 30 0.5 0.8 0.4 2.0 0.4
12 of 100 pings never came back, and tc -s (s for statistics) confirms netem dropped 12. The times of the pings that did come back are fast again (0.4 ms): loss and latency are separate problems. 10% is a chance, not an exact count, so each run differs: you will see 12%, 10%, 7%. That is also why short tests mislead; mtr’s 30 tries showed 6.7%. Count at least 100 before you believe a loss number.
Small print you will hit. ping -i 0.2 (five a second) works as a normal user. mtr -i 0.2 does not: it says non-root users cannot request an interval < 1.0 seconds, so mtr stays at one a second here.
What it feels like. On a call, lost packets mean frozen video and words that drop out. In a game, players teleport. For downloads it is worse than it looks, as the next step shows.
07See what lag does to download speed
ThroughputIn plain words: How much data actually gets through each second. Bandwidth is the width of the road; throughput is how many cars really get through.Technical: The real data rate achieved, in bits per second (Mbits/sec, Gbits/sec). Network+ 5.4 lists it as “bandwidth: throughput capacity”. is what you notice when a download crawls. Clear the lag rule, start an iperf3 speed-test server inside the pretend server, then test four times: clean, 50 ms delay, 50 ms delay plus 2% loss, and 10% loss on its own (outputs trimmed to the last lines):
$ sudo tc qdisc del dev lab0 root $ sudo ip netns exec server iperf3 -s -D $ iperf3 -c 10.99.0.2 -t 5 $ sudo tc qdisc add dev lab0 root netem delay 50ms $ iperf3 -c 10.99.0.2 -t 5 $ sudo tc qdisc change dev lab0 root netem delay 50ms loss 2% $ iperf3 -c 10.99.0.2 -t 5 $ sudo tc qdisc change dev lab0 root netem loss 10% $ iperf3 -c 10.99.0.2 -t 5
What we saw (output from the test run):
$ sudo tc qdisc del dev lab0 root $ sudo ip netns exec server iperf3 -s -D $ iperf3 -c 10.99.0.2 -t 5 [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-5.02 sec 1.50 GBytes 2.58 Gbits/sec 0 sender [ 5] 0.00-5.02 sec 1.50 GBytes 2.57 Gbits/sec receiver $ sudo tc qdisc add dev lab0 root netem delay 50ms $ iperf3 -c 10.99.0.2 -t 5 [ 5] 0.00-5.02 sec 312 MBytes 522 Mbits/sec 0 sender [ 5] 0.00-5.07 sec 312 MBytes 517 Mbits/sec receiver $ sudo tc qdisc change dev lab0 root netem delay 50ms loss 2% $ iperf3 -c 10.99.0.2 -t 5 [ 5] 0.00-5.02 sec 13.2 MBytes 22.2 Mbits/sec 248 sender [ 5] 0.00-5.07 sec 10.4 MBytes 17.2 Mbits/sec receiver $ sudo tc qdisc change dev lab0 root netem loss 10% $ iperf3 -c 10.99.0.2 -t 5 [ 5] 0.00-5.02 sec 4.62 MBytes 7.74 Mbits/sec 362 sender [ 5] 0.00-5.02 sec 4.50 MBytes 7.52 Mbits/sec receiver
| Setting on lab0 | Speed (receiver) | What happened |
|---|---|---|
| None (clean) | 2.57 Gbits/sec | Baseline. 0 retries. |
| delay 50ms | 517 Mbits/sec | About a fifth: each “got it” reply now takes longer to come back |
| delay 50ms loss 2% | 17.2 Mbits/sec | 2% loss cut it another 30 times. 248 retries. |
| loss 10% | 7.52 Mbits/sec | Over 300 times slower than clean, with no delay at all |
Downloads use TCPIn plain words: The delivery service that guarantees every piece arrives, in order. If one goes missing, it is sent again.Technical: Transmission Control Protocol. The receiver acknowledges data; the sender limits how much is unacknowledged at once (the congestion window, Cwnd in iperf3 output) and cuts it sharply when it sees loss., which treats a lost packet as a sign the road is jammed and slows right down, then speeds up again carefully. The Retr column counts packets that had to be sent again. That is why a small loss number causes a big slowdown, and why loss plus delay together is the worst case: the slow-down and the speed-up both take longer.
Your numbers will differ. The clean speed depends on your VM’s CPU (it is all happening inside one machine). The pattern is what matters: delay hurts speed, loss hurts it a lot, and both together hurt it most (in an earlier run on the same VM, delay 50ms loss 10% fell to 394 Kbits/sec, against 9.40 Mbits/sec for 10% loss alone). A speed-test website measures the same thing over the internet; Network+ 5.5 calls it a speed tester.
08Bad Wi-Fi, then put everything back
Real bad connections mix all three. This is roughly a crowded Wi-Fi network: some delay, lots of jitter, a little loss. Then remove every lab change:
$ sudo tc qdisc change dev lab0 root netem delay 60ms 40ms loss 3% $ ping -c 30 -q 10.99.0.2 $ sudo tc qdisc del dev lab0 root $ tc qdisc show dev lab0 $ ping -c 3 10.99.0.2 $ sudo ip netns exec server pkill iperf3 $ sudo ip netns del server $ ip -br link show lab0
What we saw (output from the test run):
$ sudo tc qdisc change dev lab0 root netem delay 60ms 40ms loss 3% $ ping -c 30 -q 10.99.0.2 PING 10.99.0.2 (10.99.0.2) 56(84) bytes of data. --- 10.99.0.2 ping statistics --- 30 packets transmitted, 29 received, 3.33333% packet loss, time 29106ms rtt min/avg/max/mdev = 24.104/69.621/98.421/19.416 ms $ sudo tc qdisc del dev lab0 root $ tc qdisc show dev lab0 qdisc noqueue 0: root refcnt 2 $ ping -c 3 10.99.0.2 ... 3 packets transmitted, 3 received, 0% packet loss, time 2005ms rtt min/avg/max/mdev = 0.344/0.454/0.604/0.109 ms $ sudo ip netns exec server pkill iperf3 $ sudo ip netns del server $ ip -br link show lab0 Device "lab0" does not exist.
Read the bad-Wi-Fi line like a pro: avg 70 ms (latency), mdev 19 ms (jitter), 3.3% loss. All three problems at once, in one line. After tc qdisc del the link is back to noqueue and well under 1 ms. Deleting the namespace also removes the virtual cable, which is why lab0 no longer exists. If tc qdisc del says Cannot delete qdisc with handle of zero, there was no lag rule left to delete, so you are already clean. Rebooting the VM also clears everything: none of these changes are saved.
Try it. Before deleting the namespace, set your own mix with change (for example delay 20ms 5ms loss 1%) and ask someone to read the ping summary and tell you which problem you added. That is the troubleshooting skill this lab is for.
Measure lag on Windows, Mac and Android
The lag maker (tc netem) is Linux-only, but you can measure lag on any device. Ping your router first, then something on the internet, and compare (the game-lag guide shows how to find your router’s address).
- Windows (open a Command Prompt): ping -n 20 8.8.8.8. The summary gives Lost = (% loss) and Minimum / Maximum / Average. There is no jitter number, so use the gap between Minimum and Maximum as a rough guide. pathping shows loss hop by hop (see Traceroute on Windows).
- Mac (Terminal app, see Network Tools on a Mac): ping -c 20 8.8.8.8. The last line is min/avg/max/stddev; stddev is the jitter number, like mdev on Linux.
- Android: there is no built-in ping screen. Use a ping app from the Play Store, or the free Termux terminal app. Look for the same three things: average time, how much the times jump around, and % loss.
Tip: 8.8.8.8 is Google’s public DNS server and answers ping. Some servers ignore ping on purpose, so “no reply” from a big website is not always a fault.
What you learned
- Latency — high ping avg with small mdev: everything is late by the same amount (feels like delay)
- Jitter — big mdev / StDev even when the average looks fine (feels like rubber-banding and robotic voices)
- Packet loss — ping % packet loss and mtr Loss%; count at least 100 before trusting the number (feels like freezes and teleporting)
- Throughput — iperf3 showed 2% loss plus 50 ms delay cutting speed from 2.57 Gbits/sec to 17.2 Mbits/sec, because TCP slows down every time a packet goes missing
- tc qdisc add / change / del ... netem — add, edit and remove simulated lag; tc -s counts what netem dropped
- Baseline first — measure normal before you change anything, or you can’t tell what changed
Network+ N10-009 map
| Objective | Topic | Where in this lab |
|---|---|---|
| 5.4 Troubleshoot common performance issues | Latency, jitter, packet loss, bandwidth (throughput capacity) | Steps 4–8 |
| 5.5 Use the appropriate tool or protocol | ping, traceroute, ip, speed tester | Steps 2–7 (tracepath and mtr are Linux relatives of traceroute; iperf3 is a speed tester you run on both ends) |
| 3.2 Use network monitoring technologies | Baseline metrics | Step 3 |
| 1.4 Ports, protocols, services and traffic types | ICMP, TCP | ping uses ICMP (steps 3–6); iperf3 uses TCP (step 7) |
Objective numbers are from the CompTIA N10-009 exam objectives (version 4.0). tc, mtr and iperf3 are not named in the objectives; they are the hands-on way to see the ideas the exam asks about.
Lab note: containers. We first tried this lab in two Docker containers. tc qdisc add ... netem failed with Error: Specified qdisc kind is unknown. because a container uses its host’s Linux kernel, and that host kernel was built without netem. A normal Ubuntu 24.04 VM has it (it is in the standard linux-modules package and loads by itself the first time you use it), which is why this lab asks for a VM. If you see that error, you are probably in a container or on a cut-down kernel.
Put it on your resume. Simulated latency, jitter and packet loss with Linux tc/netem on an isolated virtual link, measured the effects with ping, mtr, tracepath and iperf3 against a recorded baseline, and showed 2% packet loss on a 50 ms link cutting TCP throughput by more than 95%. Change it to match what you actually did.
Tried it? Tell us
Nova built this lab from Steven’s ideas, and every command above was run on a real machine before it went up. If a step behaved differently for you, or you know a better way, tell us. Ideas for the next lab are welcome too — no promises on timing.
NextWireGuard VPN: build a tunnel and see what it hides→
Go deeper
- Why Is My Game Lagging? Find Out With One Command — use ping to find whether lag starts on your device, your Wi-Fi, your internet or the game server
- Traceroute on Windows (tracert) — see every hop, and pathping for loss per hop
- Linux for Defenders 09 — network, DNS and listening ports — more of the ip command on a real server
- RFC 3393 — IP Packet Delay Variation Metric for IP Performance Metrics (IPPM) — the standard behind “jitter”, and why it prefers the term “delay variation”
Original walkthrough. Every command and output above was run as a normal sudo user on an Ubuntu 24.04.5 virtual machine (kernel 6.8.0-147-generic, iproute2 6.1.0, iputils 20240117, mtr 0.95, iperf3 3.16). Outputs are trimmed where marked with .... Only tested on Ubuntu so far; the Windows, Mac and Android box describes the built-in tools and was not part of this test run. If something does not match your machine, tell me and I’ll fix it.