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, /bin and /usr/bin are 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, use journalctl instead.

Common /var subdirectories:

DirectoryPurpose
/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/local versus /opt? The convention is: /usr/local for software that follows the traditional Unix layout (bin, lib, share), and /opt for 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 /boot unless 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/sda reads raw bytes from your disk. It is safe (read-only). But never write to /dev/sda directly unless you want to destroy data. Commands like dd if=/dev/zero of=/dev/sda will 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

  1. Map your system. Run du -sh /* 2>/dev/null | sort -rh and identify which three directories consume the most space. Can you explain why?

  2. Track down a service. Pick a service running on your system (e.g., sshd or cron). Find its binary (using which), its configuration file (in /etc), its log file (in /var/log), and its PID file (in /run).

  3. Explore /proc. Find your shell's PID with echo $$, then explore /proc/<PID>/. Read status, cmdline, environ, and list fd/. What do you discover?

  4. Find the largest log file. Using find and du, locate the single largest file in /var/log. What service is writing it?

  5. Predict the location. Without searching, predict where each of these lives, then verify:

    • The timezone configuration
    • The system's DNS resolver settings
    • The top command 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 /tmp is a tmpfs or a regular directory
  • Checks whether /bin is 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.