🩺 Lesson 9: Disk, Memory and Network Checks
A five-minute health check you can run on any Linux machine: is there room, is there memory, and is the network alive?
The five-minute health check
When a machine feels slow, or you have just been handed one you do not know, you do not guess. You ask a short list of questions and let the commands answer:
- How long has it been running, and how busy is it? uptime
- Is the disk full? df -h
- Is it out of memory? free -h
- Does it have a network address? ip -br address
- What is listening for connections? ss -tln
This lesson goes through each one, and then shows you how to work out whether a problem is "me" or "the network". Everything below, and the video, was run on a full Ubuntu 24.04 (a systemd container, the same kind of machine as in lesson 8). Two screens, the failed ping and the ssh login, are marked as coming from an earlier lab machine. (The tiny container from lesson 1 can run df, du and free, but it lacks the network tools.) The names and numbers on your machine will be different, and that is how it should be.
Disk space: df and du
ubuntu@practice:~$ df -h Filesystem Size Used Avail Use% Mounted on overlay 915G 229G 640G 27% / tmpfs 64M 0 64M 0% /dev shm 64M 0 64M 0% /dev/shm tmpfs 16G 52K 16G 1% /run /dev/nvme0n1p2 915G 229G 640G 27% /etc/hosts tmpfs 16G 0 16G 0% /run/lock ubuntu@practice:~$ df -h / Filesystem Size Used Avail Use% Mounted on overlay 915G 229G 641G 27% / ubuntu@practice:~$ du -sh /usr/share 30M /usr/share ubuntu@practice:~$ du -h --max-depth=1 /usr 4.0K /usr/lib64 4.0K /usr/games 37M /usr/bin 916K /usr/libexec 52K /usr/local 158M /usr/lib 4.0K /usr/src 30M /usr/share 11M /usr/sbin 24K /usr/include 236M /usr ubuntu@practice:~$ du -sh /var/log du: cannot read directory '/var/log/private': Permission denied 17M /var/log ubuntu@practice:~$ sudo du -sh /var/log [sudo] password for ubuntu: 17M /var/log ubuntu@practice:~$ df -i / Filesystem Inodes IUsed IFree IUse% Mounted on overlay 60981248 1901873 59079375 4% /
df means "disk free" and shows every filesystem that is mounted. The -h makes the sizes human readable (915G instead of a long number of kilobytes). The columns are Size, Used, Avail, Use% (how full it is) and Mounted on (where it appears in the folder tree).
Only a few rows matter. The first, mounted on /, is the main disk: 915 GB in total, 229 GB used, 27 percent full. (The numbers come from the computer the container runs on; a container shares its host's disk, which is why the filesystem is called overlay.) Rows like tmpfs and shm are memory pretending to be a disk, used for temporary files (/run, /dev/shm), and you can ignore them. df -h / asks about just one place. When the Use% of the main disk reaches 100, odd things break: programs cannot save, logs cannot be written, and you may not be able to log in.
So when it is getting full, which folder is to blame? That is the job of du, "disk usage":
- du -sh /usr/share prints one total for a folder: s for summary and h for human sizes. That folder holds 30 MB.
- du -h --max-depth=1 /usr shows the size of each folder one level down, plus the total on the last line: /usr is 236 MB, and lib (158 MB) and bin (37 MB) are the biggest parts. (The list is not sorted. Lesson 11 shows you how to sort it, which makes the biggest folder jump out.)
- du -sh /var/log printed a warning first: "cannot read directory '/var/log/private': Permission denied". du cannot add up what you are not allowed to read, so the total may be too small. Running it with sudo gets the complete number, which is the habit for system folders.
A disk can also be "full" in another way: every file uses one inode (a small record), and a filesystem has a fixed number of them. df -i / shows that this disk has 60,981,248 inodes and uses 1,901,872, so 4 percent. Millions of tiny files can use up the inodes while there is still free space. The disk full runbook on this site covers that and more.
Memory: free, uptime and friends
ubuntu@practice:~$ free -h
total used free shared buff/cache available
Mem: 31Gi 7.2Gi 971Mi 132Mi 22Gi 23Gi
Swap: 8.0Gi 0B 8.0Gi
ubuntu@practice:~$ nproc
8
ubuntu@practice:~$ uptime
15:42:54 up 2 days, 22:57, 0 user, load average: 1.67, 1.60, 1.42
ubuntu@practice:~$ head -3 /proc/meminfo
MemTotal: 32541184 kB
MemFree: 996408 kB
MemAvailable: 25024772 kB
ubuntu@practice:~$ hostnamectl
Static hostname: practice
Icon name: computer-container
Chassis: container ☐
Machine ID: 98ba59ae60964816acb85feb0c9fcff8
Boot ID: 0a165fb5983f4b1caba9cb1c9081b6d4
Virtualization: docker
Operating System: Ubuntu 24.04.5 LTS
Kernel: Linux 7.0.0-38-generic
Architecture: x86-64
- free -h shows memory. total is how much there is (31 GiB here), used is what programs hold, and **available** is the number that matters: how much programs could still get. The free column looks small on a long-running machine and that is normal, because Linux uses spare memory as a cache (buff/cache) to make things faster, and it hands that memory back instantly when a program needs it. Swap is disk space used as overflow memory. If swap is heavily used, the machine is short of memory and will feel slow. Here it is 0B.
- nproc says how many processor cores there are: 8.
- uptime prints the time, how long the machine has been up, how many users are logged in, and the load average: how many programs were waiting for the processor, on average, in the last 1, 5 and 15 minutes. If it is lower than your core count, the machine is not overloaded.
- head -3 /proc/meminfo shows where free gets its numbers: the file /proc/meminfo, which is not a real file but a window into the running system. MemTotal is 32541184 kB, which is about 31 GiB.
- hostnamectl is a one-screen summary of the machine: its name, operating system, kernel and architecture. The Virtualization: docker line says this machine is itself a container, which is why the kernel is the host's.
Your network address: ip
ubuntu@practice:~$ ip -br address
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0@if210 UP 172.17.0.16/16
ubuntu@practice:~$ ip address show eth0
2: eth0@if210: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 6a:cf:2f:b0:da:00 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.16/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
ubuntu@practice:~$ ip route
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.16
ubuntu@practice:~$ hostname -I
172.17.0.16
- ip -br address (br for brief) lists your network interfaces. lo is the loopback: a pretend network where a machine talks to itself, always 127.0.0.1. eth0 is the real network connection. UP means it is working, and 172.17.0.16/16 is its IP address.
- An IP address is the machine's number on the network. The /24 says how much of the number identifies the network rather than the machine: here the /16 means the first two numbers, 172.17., are the network, and the last two identify the machine, so this network holds 172.17.0.0 to 172.17.255.255. (A /24, which you will meet often, uses the first three numbers.) (The site's subnet calculator does this arithmetic for you.) Addresses starting with 10., 192.168. or 172.16. to 172.31. are private: they work inside a home or office network, and not on the open internet.
- The container has no IPv6 address except the loopback ::1. A machine with IPv6 shows a long address starting with 2 or fe80:: there, the newer, much longer kind (not shown here).
- ip address show eth0 gives the full detail for one interface: its state, its hardware (MAC) address after link/ether, and its addresses after inet (and inet6 for IPv6).
- ip route shows where traffic goes. The first line, default via 172.17.0.1, is the gateway: the router that handles everything that is not on your own network. If this line is missing, the machine cannot reach the internet.
- hostname -I just prints the addresses, handy in scripts.
ping: is anybody there?
ubuntu@practice:~$ ping -c 3 localhost PING localhost (::1) 56 data bytes 64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.034 ms 64 bytes from localhost (::1): icmp_seq=2 ttl=64 time=0.068 ms 64 bytes from localhost (::1): icmp_seq=3 ttl=64 time=0.046 ms --- localhost ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2078ms rtt min/avg/max/mdev = 0.034/0.049/0.068/0.014 ms ubuntu@practice:~$ ping -c 3 172.17.0.1 PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data. 64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.088 ms 64 bytes from 172.17.0.1: icmp_seq=2 ttl=64 time=0.112 ms 64 bytes from 172.17.0.1: icmp_seq=3 ttl=64 time=0.081 ms --- 172.17.0.1 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2072ms rtt min/avg/max/mdev = 0.081/0.093/0.112/0.013 ms
For comparison, here is the same kind of ping on a different practice network (an earlier lab machine, not the one in the video), where it failed:
ubuntu@practice:~$ ping -c 3 archive.ubuntu.com PING archive.ubuntu.com (185.125.190.82) 56(84) bytes of data. From ubuntu-mirror-2.ps5.canonical.com (185.125.190.82) icmp_seq=1 Destination Port Unreachable From ubuntu-mirror-2.ps5.canonical.com (185.125.190.82) icmp_seq=2 Destination Port Unreachable From ubuntu-mirror-2.ps5.canonical.com (185.125.190.82) icmp_seq=3 Destination Port Unreachable --- archive.ubuntu.com ping statistics --- 3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2002ms
ping sends a small test message and waits for the answer, over and over (we added -c 3: send exactly 3). Each reply line shows how long the round trip took, in milliseconds, and the summary at the end shows how many were lost.
- ping -c 3 localhost talks to the machine itself (::1 is its IPv6 loopback address): very fast, under 0.1 ms, no loss. If that failed, something would be badly wrong with the machine's own network software.
- ping -c 3 172.17.0.1 pinged the gateway, the router: 0 percent loss, so the local network works.
- ping -c 3 archive.ubuntu.com is the one to be careful with. On the earlier practice network (the second screen above) it found the address but got 100% packet loss and "Destination Port Unreachable". Is the site down? No! The next section shows that its website works fine from the very same machine. ping uses a special kind of message that many networks and firewalls block on purpose, and that practice network blocked it. (From the machine in the video the same ping works, so your result depends on your network.) A failed ping does not prove a site is down. Always try the thing you actually want, in this case a web request, before you decide.
Names: DNS and curl
ubuntu@practice:~$ getent hosts techcommune.org 47.204.6.48 techcommune.org ubuntu@practice:~$ getent hosts nosuchname.invalid ubuntu@practice:~$ curl -sI https://techcommune.org HTTP/1.1 200 OK Server: nginx Date: Thu, 22 Oct 2026 18:49:15 GMT Content-Type: text/html Content-Length: 8281 Last-Modified: Thu, 22 Oct 2026 12:18:07 GMT Connection: keep-alive Vary: Accept-Encoding ETag: "6ad9feff-2059" Strict-Transport-Security: max-age=31536000 X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=() Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'; object-src 'none'; frame-src https://www.youtube-nocookie.com Cache-Control: no-cache Accept-Ranges: bytes ubuntu@practice:~$ curl -sI http://archive.ubuntu.com | head -n 3 HTTP/1.1 200 OK Date: Thu, 22 Oct 2026 18:49:16 GMT Server: Apache/2.4.58 (Ubuntu)
Computers use numbers, but people use names, so something has to translate. That is DNS, the phone book of the internet.
- cat /etc/resolv.conf shows which DNS server your machine asks (not shown here, because the lab's address is private). On a typical Ubuntu desktop you will see nameserver 127.0.0.53, a small local helper that passes your questions on.
- getent hosts techcommune.org looked the name up and got the number: 47.204.6.48. A name that does not exist, like nosuchname.invalid, prints nothing. No answer means DNS could not find it.
- curl -sI https://techcommune.org asked this very website for its headers, a live answer from the real server. HTTP/1.1 200 OK means success. The lines that follow are instructions for your web browser about how to treat the page: Strict-Transport-Security says "always use the secure version", X-Frame-Options and Content-Security-Policy say what the page may and may not do, and Cache-Control: no-cache says "check with me for fresh copies". (You can learn more about curl in the curl guide on this site.)
- curl -sI http://archive.ubuntu.com | head -n 3 (the pipe keeps the first three lines) also got 200 OK. So the web is reachable, even though ping to the same site failed. That is the point made above.
What is listening: ss
ubuntu@practice:~$ ss -tln
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 4096 [::]:22 [::]:*
LISTEN 0 511 [::]:80 [::]:*
ubuntu@practice:~$ sudo ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:* users:(("systemd-resolve",pid=71,fd=17))
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("systemd",pid=1,fd=56))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=401,fd=5),("nginx",pid=400,fd=5),("nginx",pid=399,fd=5),("nginx",pid=398,fd=5),("nginx",pid=397,fd=5),("nginx",pid=396,fd=5),("nginx",pid=395,fd=5),("nginx",pid=394,fd=5),("nginx",pid=391,fd=5))
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=71,fd=15))
LISTEN 0 4096 [::]:22 [::]:* users:(("systemd",pid=1,fd=57))
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=401,fd=6),("nginx",pid=400,fd=6),("nginx",pid=399,fd=6),("nginx",pid=398,fd=6),("nginx",pid=397,fd=6),("nginx",pid=396,fd=6),("nginx",pid=395,fd=6),("nginx",pid=394,fd=6),("nginx",pid=391,fd=6))
A service listens for connections on a numbered port, like a numbered door on the machine. ss shows the doors that are open. The options: -t is TCP connections, -l is listening ones, and -n shows plain port numbers. The Local Address:Port column tells you who may knock:
- 0.0.0.0:80 and [::]:80 mean "on every address, port 80": the web server (nginx), open to anyone who can reach the machine. Port 80 is plain web traffic. Secure web traffic uses port 443.
- 127.0.0.53:53 and 127.0.0.54:53 mean "only this machine itself, port 53": the local DNS helper, deliberately kept private.
- 0.0.0.0:22 and [::]:22 mean "everywhere, port 22": the SSH server, which lets people log in remotely. In the -p output its owner shows as systemd, not sshd: on Ubuntu 24.04 systemd listens on port 22 for ssh and starts the server when someone connects.
Adding -p shows which program owns each port, and it needs sudo to see other users' programs: users:(("nginx",pid=401,...)) names nginx and its process numbers (lesson 7). This is a powerful security check: if something is listening that you do not recognise, find out what it is. The site's [ss guide](site:docs/sysadmin/ss.html) goes much deeper.
ssh: logging in to another machine
Port 22 above is for SSH, the secure way to open a terminal on another computer, and the tool most administrators use every day. The form is ssh USER@MACHINE. To practise without a second machine, connect to yourself (this screen is from the earlier lab machine; the video did not run ssh):
ubuntu@practice:~$ ssh ubuntu@localhost The authenticity of host 'localhost (::1)' can't be established. ED25519 key fingerprint is SHA256:rCqweWI6ISgbEY69mRRgoBrf/K2RGsaHBQniSnoVxXY. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'localhost' (ED25519) to the list of known hosts. ubuntu@localhost's password: Welcome to Ubuntu 24.04 LTS (GNU/Linux 7.0.2-6-pve x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/pro The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. ubuntu@practice:~$ hostname practice ubuntu@practice:~$ exit logout Connection to localhost closed.
- The first time you connect to any machine, ssh shows its fingerprint and asks "Are you sure you want to continue connecting?". It is saying: "I have never met this machine, please confirm it is the right one." Typing yes saves it in a file called known_hosts, and from then on ssh recognises the machine. If the fingerprint ever changes it shouts a big warning, because that could mean someone is impersonating the machine (from the documentation, not run here).
- It then asked for the password. Typing a password shows nothing on the screen, as with sudo.
- The welcome message appeared, and hostname printed practice: it is the same machine, as expected. exit closed the connection and returned to the first shell.
Passwords work, but real servers use key pairs instead, which are far safer. When you are ready, the site has guides on creating SSH keys and on hardening SSH.
Is it me, or is it the network?
When "the internet is not working", walk down this ladder from the bottom of the stack to the top. The first step that fails tells you where the problem is:
- **ip -br address**: do I have an address? If eth0 shows DOWN or has no address, the problem is the cable, the Wi-Fi or the network's address assignment, not the internet.
- **ip route**: is there a default via line? Without a gateway, nothing leaves your network.
- **ping -c 3 GATEWAY**: can I reach my router? If not, the problem is between you and the router.
- **getent hosts NAME: does a name turn into a number? If the numbers work but the names do not, the problem is **DNS. (This is why the old joke says it is always DNS.)
- **curl -sI https://NAME**: can I reach the real service? Trust this over ping.
- **sudo ss -tlnp** (on the server): is the service even listening? If you are the one running the service, this tells you whether it is up.
"Can I join you?"
🧪 Try it yourself
On your full Ubuntu machine:
1. Find the Use% of your main disk with one df command. 2. Which folder directly under /usr is the biggest? (Use du -h --max-depth=1 /usr.) 3. How much memory is available, and is any swap used? 4. What are your machine's IP address and its default gateway? 5. Ping the gateway exactly twice. 6. Find the IP address of techcommune.org, then ask it for its headers with curl. 7. Which ports are open for the whole network (addresses starting with 0.0.0.0 or *)?
The cheat sheet
- df -h free space per disk. du -sh FOLDER size of a folder. du -h --max-depth=1 FOLDER the sizes of what is inside. df -i inodes.
- free -h memory (read available). uptime load. nproc cores. hostnamectl machine summary.
- ip -br address addresses. ip route the gateway. hostname -I just the addresses.
- ping -c 3 HOST test reachability, but a failure does not prove it is down.
- getent hosts NAME DNS lookup. curl -sI URL ask a website for its headers.
- ss -tln open ports. sudo ss -tlnp with the program that owns each.
- ssh USER@HOST log in remotely. Answer yes once to the fingerprint of a machine you trust.
← Lesson 8 · ↑ All lessons · Lesson 10: Surviving a Text Editor →