The Linux Filesystem Hierarchy
Why This Matters
You have just joined a team as a junior systems administrator. Your first ticket reads: "Application logs are eating up disk space on the web servers. Clean up and figure out where they're writing." You SSH in and stare at the root of the filesystem. There are twenty-odd directories -- /var, /tmp, /usr, /opt, /etc -- and you have no idea where to look.
This is not a hypothetical situation. Every Linux professional hits this wall on day one. Unlike Windows, where programs scatter files across C:\Program Files, C:\Users, and the Registry, Linux follows a well-defined plan called the Filesystem Hierarchy Standard (FHS). Learn it once, and you will always know where to find configuration files, log files, binaries, libraries, and temporary data -- on any distribution, on any server, anywhere in the world.
By the end of this chapter, the Linux directory tree will feel like your own neighborhood.
Try This Right Now
Open a terminal and run these commands. Do not worry about understanding every detail yet -- we will cover each one shortly.
# See the top-level directory structure
ls /
# Get a tree view (install tree if needed)
tree -L 1 /
# How big is each top-level directory?
du -sh /* 2>/dev/null | sort -rh | head -15
# Where is the 'ls' command actually stored?
which ls
file $(which ls)
You should see something like this from ls /:
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
Each of those directories has a specific purpose. Let us walk through every single one.
The Root of Everything: /
In Linux, there is one single directory tree. Everything -- every file, every device, every running process -- lives somewhere under / (called "root"). There are no drive letters like C: or D:. If you plug in a USB drive, it appears as a directory under /mnt or /media. If you add a second hard disk, it gets mounted somewhere inside this same tree.
/ <-- The root of the entire filesystem
├── bin/ <-- Essential user commands
├── boot/ <-- Bootloader and kernel
├── dev/ <-- Device files
├── etc/ <-- System configuration
├── home/ <-- User home directories
├── lib/ <-- Essential shared libraries
├── media/ <-- Removable media mount points
├── mnt/ <-- Temporary mount points
├── opt/ <-- Optional/third-party software
├── proc/ <-- Process and kernel info (virtual)
├── root/ <-- Root user's home directory
├── run/ <-- Runtime variable data
├── sbin/ <-- Essential system binaries
├── srv/ <-- Service data (web, FTP)
├── sys/ <-- Kernel and device info (virtual)
├── tmp/ <-- Temporary files
├── usr/ <-- User programs and data
└── var/ <-- Variable data (logs, mail, spool)
This layout is not arbitrary. It is defined by the Filesystem Hierarchy Standard (FHS), maintained by the Linux Foundation. Most major distributions (Debian, Ubuntu, Fedora, RHEL, SUSE, Arch) follow it, with minor variations.
Think About It: Why does Linux use a single unified tree instead of drive letters? What advantages does this give you as a sysadmin managing servers with dozens of disks?
Walking the Tree: Directory by Directory
/bin -- Essential User Binaries
This directory contains the commands that every user needs, even in single-user (rescue) mode. Think of the absolute basics: ls, cp, mv, rm, cat, echo, mkdir, chmod, grep.
ls /bin | head -20
On modern systems (Fedora, Ubuntu 20.04+, Arch), /bin is actually a symbolic link to /usr/bin. This is called the UsrMerge -- we will explain why shortly.
# Check if /bin is a symlink
ls -ld /bin
# Likely output: lrwxrwxrwx 1 root root 7 ... /bin -> usr/bin
Distro Note: On older CentOS 6 / Debian 8 systems,
/binand/usr/binare separate directories. On modern Fedora, Ubuntu 20.04+, and Arch, they have been merged. If you are working on an older system, the distinction matters for rescue scenarios.
/sbin -- System Binaries
Similar to /bin, but these are commands that typically require root privileges: fdisk, mkfs, iptables, reboot, shutdown, ip.
ls /sbin | head -20
# Try running one as a regular user
fdisk -l
# You'll likely get "Permission denied" or empty output
# Now with sudo
sudo fdisk -l
On merged systems, /sbin is a symlink to /usr/sbin.
/etc -- Configuration Files
This is where the system-wide configuration lives. Every major service stores its config here. The name historically comes from "et cetera" -- it was originally a catch-all, but today it is strictly for configuration.
# List the top-level config files and directories
ls /etc
# Some important files you should know about:
cat /etc/hostname # System hostname
cat /etc/os-release # Distribution info
cat /etc/passwd # User accounts (not actually passwords!)
cat /etc/fstab # Filesystem mount table
ls /etc/ssh/ # SSH server/client config
ls /etc/systemd/ # systemd unit overrides
Key principle: configuration files in /etc are plain text. You can read, edit, back up, and version-control them. This is a major advantage of Linux -- there is no opaque binary registry.
# Find all config files modified in the last 24 hours
find /etc -type f -mtime -1 2>/dev/null
/home -- User Home Directories
Every regular user gets a directory here. Your personal files, shell configuration (.bashrc, .profile), SSH keys (.ssh/), and application settings all live under your home directory.
ls /home
# Your home directory
echo $HOME
ls -la ~
# Important dot-files
ls -la ~/.bashrc ~/.profile ~/.ssh/ 2>/dev/null
The ~ (tilde) character is a shortcut for your home directory. ~alice means /home/alice.
/root -- Root User's Home
The root user does not live in /home/root. Instead, root's home directory is /root. Why? Because /home might be on a separate partition that has not mounted yet during rescue operations. The root user needs a home directory available on the root filesystem.
sudo ls -la /root
/var -- Variable Data
This is where data that changes during normal system operation lives. This is where you look when logs are eating disk space (remember our opening scenario?).
# Key subdirectories
ls /var
# System logs -- your first stop for troubleshooting
ls /var/log/
sudo tail -20 /var/log/syslog # Debian/Ubuntu
sudo tail -20 /var/log/messages # RHEL/Fedora
# Package manager cache
du -sh /var/cache/apt 2>/dev/null # Debian/Ubuntu
du -sh /var/cache/dnf 2>/dev/null # Fedora
# Mail spool
ls /var/mail/ 2>/dev/null
# Variable runtime data
ls /var/run # Usually a symlink to /run
Distro Note: On Debian/Ubuntu the main system log is
/var/log/syslog. On RHEL/Fedora/CentOS it is/var/log/messages. On systems using only journald, usejournalctlinstead.
Common /var subdirectories:
| Directory | Purpose |
|---|---|
/var/log/ | System and application log files |
/var/cache/ | Application cache data |
/var/lib/ | Variable state information (databases, etc) |
/var/spool/ | Queued data (print jobs, mail, cron) |
/var/tmp/ | Temporary files preserved across reboots |
/var/run/ | Runtime data (PID files, sockets) |
/tmp -- Temporary Files
Any user or process can create files here. Files in /tmp are typically cleared on reboot (and on many systems, periodically by systemd-tmpfiles or tmpwatch).
ls /tmp
# Create a temp file
echo "test" > /tmp/mytest.txt
cat /tmp/mytest.txt
# Check if /tmp is a tmpfs (RAM-based filesystem)
df -h /tmp
mount | grep /tmp
Safety Warning: Never store anything important in
/tmp. It may be cleared at any time. Also, because it is world-writable, be cautious about security -- other users can see filenames (though not necessarily contents) in/tmp.
/usr -- User Programs (The Big One)
/usr is typically the largest directory on the system. Despite its name, it is not about users -- it stands for "Unix System Resources" (retroactively). It contains the bulk of installed software.
ls /usr
du -sh /usr
/usr/
├── bin/ <-- Most user commands (merged with /bin)
├── sbin/ <-- Most admin commands (merged with /sbin)
├── lib/ <-- Libraries for /usr/bin and /usr/sbin
├── lib64/ <-- 64-bit libraries
├── include/ <-- C/C++ header files
├── share/ <-- Architecture-independent data (docs, man pages)
├── local/ <-- Locally installed software (not from package manager)
└── src/ <-- Source code (kernel headers, etc.)
The key subdirectory to know is /usr/local/. When you compile and install software from source (using make install), it goes here by default. This keeps it separate from package-manager-installed software.
# Software from your package manager
which nginx # /usr/bin/nginx or /usr/sbin/nginx
# Software you compiled yourself
ls /usr/local/bin/
/opt -- Optional Software
Third-party commercial or self-contained software packages often install here. Examples: Google Chrome (/opt/google/chrome), some monitoring agents, vendor-specific tools.
ls /opt 2>/dev/null
# Each application gets its own subdirectory
# /opt/vendor_name/application_name
Think About It: When should software go in
/usr/localversus/opt? The convention is:/usr/localfor software that follows the traditional Unix layout (bin, lib, share), and/optfor self-contained packages that keep everything in one directory.
/boot -- Boot Files
Contains the Linux kernel, initial RAM disk (initramfs/initrd), and bootloader configuration (GRUB).
ls /boot
# You'll typically see:
# vmlinuz-* -- The kernel
# initramfs-* or initrd-* -- Initial RAM filesystem
# grub/ or grub2/ -- GRUB bootloader config
Safety Warning: Do not delete files in
/bootunless you know exactly what you are doing. Deleting the wrong kernel image will make your system unbootable.
/dev -- Device Files
This is where the "everything is a file" philosophy shines. Hardware devices are represented as files here. Your hard disk is /dev/sda. Your terminal is /dev/tty. Random numbers come from /dev/urandom.
ls /dev | head -30
# Your disks
ls /dev/sd* 2>/dev/null # SATA/SCSI disks
ls /dev/nvme* 2>/dev/null # NVMe drives
ls /dev/vd* 2>/dev/null # Virtual disks (VMs)
# Special devices
echo "Hello" > /dev/null # The black hole -- discards everything
head -c 16 /dev/urandom | xxd # Random bytes
# Your terminal
tty # Shows your terminal device
echo "Hello" > $(tty) # Writes to your own terminal
We will dive deep into /dev in Chapter 8 when we discuss device files and the VFS.
/proc -- Process Information (Virtual)
This directory does not exist on disk. It is a virtual filesystem generated by the kernel in real time. Each numbered directory corresponds to a running process (by PID). It also exposes kernel parameters.
# List running processes
ls /proc | head -20
# Info about process 1 (init/systemd)
sudo cat /proc/1/cmdline | tr '\0' ' ' ; echo
sudo ls /proc/1/fd # File descriptors
# System information
cat /proc/cpuinfo | head -20
cat /proc/meminfo | head -10
cat /proc/version
cat /proc/uptime
# Kernel tunable parameters
cat /proc/sys/net/ipv4/ip_forward
/sys -- System and Device Info (Virtual)
Another virtual filesystem, introduced in Linux 2.6. While /proc focuses on processes, /sys exposes a structured view of the kernel's device model: hardware, drivers, buses, and kernel subsystems.
# Block devices
ls /sys/block/
# CPU information
ls /sys/devices/system/cpu/
# Brightness control (on laptops)
cat /sys/class/backlight/*/brightness 2>/dev/null
# Network interface info
ls /sys/class/net/
cat /sys/class/net/eth0/address 2>/dev/null
/mnt and /media -- Mount Points
/mnt is traditionally used for temporarily mounting filesystems (an NFS share, an extra disk). /media is used by desktop environments for automatically mounting removable media (USB drives, CDs).
ls /mnt
ls /media
# Manually mount an ISO
sudo mkdir -p /mnt/iso
sudo mount -o loop some-image.iso /mnt/iso
/srv -- Service Data
Meant for data served by the system: web server document roots, FTP files, etc. In practice, many administrators use /var/www for web content instead, but the FHS recommends /srv.
/run -- Runtime Data
A tmpfs filesystem that holds runtime information: PID files, sockets, lock files. It is cleared on every boot. On many systems, /var/run is a symlink to /run.
ls /run
# You'll see: systemd/, user/, lock, utmp, etc.
# PID files for running services
cat /run/sshd.pid 2>/dev/null
The "Everything Is a File" Philosophy
This is one of the most important ideas in Unix and Linux. In Linux:
- A regular file is a file
- A directory is a file (that contains a list of other files)
- A hardware device is a file (
/dev/sda) - A running process's info is a file (
/proc/1234/status) - A network socket can be a file
- A pipe between two processes is a file
- Even kernel parameters are files (
/proc/sys/*)
This means you can use the same tools -- cat, echo, read, write -- to interact with hardware, processes, and kernel settings, not just text files.
# Read CPU info like a file
cat /proc/cpuinfo | grep "model name" | head -1
# Write to a kernel parameter like a file
cat /proc/sys/net/ipv4/ip_forward # Read it
sudo sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward' # Write to it
# Read from a device like a file
sudo head -c 512 /dev/sda | xxd | head # Read raw disk bytes
Safety Warning: The command
head -c 512 /dev/sdareads raw bytes from your disk. It is safe (read-only). But never write to/dev/sdadirectly unless you want to destroy data. Commands likedd if=/dev/zero of=/dev/sdawill wipe your disk instantly with no confirmation.
Hands-On: Exploring the Filesystem
Let us put this knowledge to work. Follow along on your own system.
Exercise 1: Finding Where Things Live
# Where is the bash binary?
which bash
# /usr/bin/bash
# Where does bash keep its config?
ls /etc/bash*
# /etc/bash.bashrc /etc/bash_completion etc.
# Where are bash's man pages?
man -w bash
# /usr/share/man/man1/bash.1.gz
Notice the pattern: binary in /usr/bin, config in /etc, docs in /usr/share.
Exercise 2: Using find to Search the Tree
# Find all files named "sshd_config"
sudo find / -name "sshd_config" 2>/dev/null
# Find all log files larger than 100MB
sudo find /var/log -size +100M 2>/dev/null
# Find config files modified in the last hour
sudo find /etc -type f -mmin -60 2>/dev/null
# Find all SUID binaries (security audit)
sudo find / -perm -4000 -type f 2>/dev/null
Exercise 3: Using tree for Visual Exploration
# Install tree if needed
# Debian/Ubuntu: sudo apt install tree
# Fedora: sudo dnf install tree
# Arch: sudo pacman -S tree
# Two levels deep from root
tree -L 2 / 2>/dev/null | head -50
# Show only directories
tree -d -L 2 /etc | head -40
# Show with file sizes
tree -sh /var/log 2>/dev/null | head -30
Exercise 4: Checking Disk Usage by Directory
# Top-level disk usage
du -sh /* 2>/dev/null | sort -rh
# What's eating space in /var?
sudo du -sh /var/*/ 2>/dev/null | sort -rh
# What's eating space in /var/log?
sudo du -sh /var/log/* 2>/dev/null | sort -rh | head -10
The UsrMerge: Why /bin Points to /usr/bin
On modern distributions, you will notice that /bin, /sbin, /lib, and /lib64 are symlinks:
ls -ld /bin /sbin /lib
# lrwxrwxrwx 1 root root 7 ... /bin -> usr/bin
# lrwxrwxrwx 1 root root 8 ... /sbin -> usr/sbin
# lrwxrwxrwx 1 root root 7 ... /lib -> usr/lib
Historically, the split between /bin and /usr/bin existed because /usr might be on a separate disk that was not available during early boot. The essential boot commands had to live in /bin on the root filesystem. Modern systems mount all filesystems before user space starts (via initramfs), so the split is no longer necessary. Merging simplifies packaging and avoids confusing questions like "should this binary go in /bin or /usr/bin?"
Debug This
A colleague tells you: "I installed a custom-compiled application yesterday but I can't find it. I used ./configure && make && sudo make install with default settings. Where did it go?"
Try to answer before reading the solution.
Solution
When you compile from source with default settings, make install puts binaries in /usr/local/bin, libraries in /usr/local/lib, and config files in /usr/local/etc. Check:
ls /usr/local/bin/
ls /usr/local/lib/
ls /usr/local/etc/
If the binary is not in $PATH, it is likely because /usr/local/bin is not in the user's PATH (rare but possible). Check with:
echo $PATH | tr ':' '\n' | grep local
What Just Happened?
+------------------------------------------------------------------+
| CHAPTER 5 RECAP |
+------------------------------------------------------------------+
| |
| - Linux has ONE directory tree, rooted at / |
| - The FHS defines where everything goes |
| - /bin, /sbin -> Essential commands (often merged to /usr) |
| - /etc -> System-wide configuration (plain text!) |
| - /home -> User home directories |
| - /var -> Variable data: logs, caches, spool |
| - /tmp -> Temporary files (cleared on reboot) |
| - /usr -> Bulk of installed programs and libraries |
| - /usr/local -> Locally compiled software |
| - /opt -> Self-contained third-party software |
| - /proc, /sys -> Virtual filesystems (kernel info) |
| - /dev -> Device files (everything is a file!) |
| - /boot -> Kernel and bootloader |
| - /mnt, /media -> Mount points for extra filesystems |
| |
| Tools: ls, find, tree, du, which, file, cat, mount |
| |
+------------------------------------------------------------------+
Try This
Exercises
-
Map your system. Run
du -sh /* 2>/dev/null | sort -rhand identify which three directories consume the most space. Can you explain why? -
Track down a service. Pick a service running on your system (e.g.,
sshdorcron). Find its binary (usingwhich), its configuration file (in/etc), its log file (in/var/log), and its PID file (in/run). -
Explore
/proc. Find your shell's PID withecho $$, then explore/proc/<PID>/. Readstatus,cmdline,environ, and listfd/. What do you discover? -
Find the largest log file. Using
findanddu, locate the single largest file in/var/log. What service is writing it? -
Predict the location. Without searching, predict where each of these lives, then verify:
- The timezone configuration
- The system's DNS resolver settings
- The
topcommand binary - The kernel's view of mounted filesystems
Bonus Challenge
Write a shell script called fs-audit.sh that:
- Lists each top-level directory under
/with its size - Counts the number of files in
/etc - Shows the 5 largest files in
/var/log - Reports whether
/tmpis a tmpfs or a regular directory - Checks whether
/binis a symlink or a real directory
#!/bin/bash
echo "=== Filesystem Audit ==="
echo ""
echo "--- Top-level directory sizes ---"
du -sh /* 2>/dev/null | sort -rh
echo ""
echo "--- Number of files in /etc ---"
find /etc -type f 2>/dev/null | wc -l
echo ""
echo "--- 5 largest files in /var/log ---"
sudo find /var/log -type f -exec du -sh {} + 2>/dev/null | sort -rh | head -5
echo ""
echo "--- /tmp filesystem type ---"
df -T /tmp | tail -1
echo ""
echo "--- Is /bin a symlink? ---"
if [ -L /bin ]; then
echo "Yes: $(ls -l /bin | awk '{print $NF}')"
else
echo "No, /bin is a real directory"
fi
Next up: Chapter 6 -- Files, Permissions & Ownership. Now that you know where everything lives, you need to understand who can access it and how.