Linux Filesystem Basics: What Every Directory Is For

Linux Filesystem Basics: What Every Directory Is For

Every Linux system arranges its files in the same basic structure, defined by the Filesystem Hierarchy Standard (FHS). This is not arbitrary: each directory exists for a reason rooted in how Unix was designed in the 1970s and how those design decisions have evolved into modern Linux. Understanding the layout means understanding where to find things, where to put things, and why the system behaves the way it does.

The single tree

On Windows, each storage device gets a drive letter: C:, D:, E:. On Linux, there is one tree, and it starts at / (called “root” or “slash”). Every file on every device on the system lives somewhere under /.

# The root of everything
ls /
# bin   boot  dev  etc  home  lib  lib64  lost+found
# media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

# On modern systems, several of these are symlinks
ls -la / | grep '\->'
# lrwxrwxrwx   bin -> usr/bin
# lrwxrwxrwx   lib -> usr/lib
# lrwxrwxrwx   lib64 -> usr/lib64
# lrwxrwxrwx   sbin -> usr/sbin

# See the full tree (first two levels)
find / -maxdepth 2 -type d 2>/dev/null | head -40

When you plug in a USB drive or mount a second hard disk, it appears as a directory inside this tree, not as a new root. The directory it appears at is called its mount point.

# See what is mounted where
mount | column -t
df -h

# Example: USB drive mounted at /media/colton/USB-DRIVE
ls /media/colton/USB-DRIVE/

/ (root)

The root directory is the starting point for all paths. It has no parent — cd / takes you to the top and .. from / stays at /.

# Absolute path: starts from /
/etc/ssh/sshd_config

# Relative path: starts from current directory
# (if you are in /etc)
ssh/sshd_config

# / is not the home directory of the root user
# The root user's home is /root
echo ~root
# /root

# Who owns /?
ls -la / | head -2
# total 72
# dr-xr-xr-x. 17 root root 4096 Jun 28 12:00 .

The root directory itself contains only subdirectories. Ordinary files do not live directly in /.

/home — user data

/home contains a subdirectory for each regular user account on the system. Your personal files, configuration, and everything that belongs to you as a user lives under ~/ (which expands to /home/yourusername).

# List home directories
ls /home/
# colton  alice  bob

# Your home directory
echo $HOME
# /home/colton

ls ~/
# Desktop  Documents  Downloads  Music  Pictures  Videos
# .bashrc  .bash_history  .config  .local  .ssh

# Hidden directories in your home (dot-prefixed)
ls -la ~ | grep '^\.'
# .bashrc       -- bash shell configuration
# .bash_profile -- login shell configuration
# .config/      -- application configs (XDG standard)
# .local/       -- local application data and binaries
# .ssh/         -- SSH keys and known_hosts
# .gitconfig    -- git user configuration

Why /home exists separately

/home is almost always on its own partition or at least treated as logically separate from the OS. This matters because:

  • You can reinstall Linux (wiping /) without touching /home, keeping all your files and application settings
  • Disk quotas can be applied per-user in /home
  • On multi-user systems, /home can be a network share (NFS) that follows users across machines

The root user is the exception: the root account’s home is /root, not /home/root. This means /root is always on the root partition and cannot accidentally be on a network share that becomes unavailable during boot.

# Root user's home
ls /root/
# (contents of root's home directory -- requires sudo to list)
sudo ls /root/

# The /home for root is deliberately separate from /home
# because /home might not be available at early boot

/etc — system configuration

/etc holds system-wide configuration files. Every service, daemon, and system component that needs configuration stores its config files here. All the files are plain text.

# What is in /etc
ls /etc/
# apt/         cron.d/       hosts        network/      resolv.conf
# bash.bashrc  default/      hostname     nginx/        shadow
# crontab      environment   locale.conf  passwd        ssh/
# ...

# Key files to know
cat /etc/hostname           # this machine's hostname
cat /etc/hosts              # static hostname-to-IP mappings
cat /etc/resolv.conf        # DNS resolver configuration
cat /etc/fstab              # filesystem mount table
cat /etc/passwd             # user account information (not passwords)
sudo cat /etc/shadow        # hashed passwords (root only)
cat /etc/group              # group definitions
cat /etc/os-release         # distribution identity
cat /etc/locale.conf        # system locale setting
cat /etc/timezone           # timezone (or: timedatectl show)

Important /etc subdirectories

ls /etc/ssh/                # SSH daemon and client config
# ssh_config   sshd_config  ssh_host_*_key

ls /etc/apt/                # APT package manager config (Debian/Ubuntu)
# sources.list  sources.list.d/  apt.conf.d/

ls /etc/nginx/              # Nginx web server config (if installed)
# nginx.conf  sites-available/  sites-enabled/  conf.d/

ls /etc/systemd/            # systemd configuration
# system/  user/  network/  resolved.conf  logind.conf

ls /etc/cron.d/             # system cron jobs
ls /etc/sudoers.d/          # sudo access rules (fragments)
ls /etc/profile.d/          # shell environment scripts run at login

Why /etc exists

Unix designers needed one place for configuration files that were system-wide (as opposed to per-user config in ~/.config). The naming is historical (“et cetera”), but the function is precise: /etc is where the OS and all its services read their configuration. The convention that everything here is plain text makes the entire system configuration auditable, diffable, and versionable with git.

# Many sysadmins track /etc with git
sudo git -C /etc init
sudo git -C /etc add .
sudo git -C /etc commit -m "initial state"
# Then commit after every significant change

/var — variable data

/var holds data that changes during normal system operation. Logs grow here. Package manager databases live here. Mail spools, print queues, database files, and application state all live under /var.

ls /var/
# backups  cache  lib  lock  log  mail  opt  run  spool  tmp

# Log files
ls /var/log/
# auth.log      syslog        kern.log
# dpkg.log      apt/          nginx/
# journal/      (systemd journal)

# Read system logs
sudo tail -f /var/log/syslog          # Debian/Ubuntu
sudo journalctl -f                    # systemd (all distributions)
sudo tail -f /var/log/auth.log        # authentication events

# Package manager databases
ls /var/lib/dpkg/                     # APT/dpkg state (Ubuntu/Debian)
ls /var/lib/rpm/                      # RPM state (Fedora/RHEL)
ls /var/lib/pacman/                   # pacman state (Arch)
# These track which packages are installed and at what version

# Package cache (downloaded .deb or .rpm files)
ls /var/cache/apt/archives/           # Ubuntu/Debian: cached .deb files
sudo du -sh /var/cache/apt/archives/  # how much space the cache uses
sudo apt clean                        # remove cached packages

# Application state and data
ls /var/lib/
# apt/  dpkg/  mysql/  postgresql/  docker/  systemd/
# databases, application state, etc.

Why /var is separate from /usr

/usr contains files that do not change during normal operation — programs, libraries, documentation. /var contains files that do change. This distinction matters for a few reasons:

  • /usr can be mounted read-only (common in some embedded or high-security setups)
  • /var can be on a separate partition, preventing runaway log growth from filling the root filesystem
  • Backups of /usr need not happen often; backups of /var (especially /var/lib) should happen frequently because it contains application state
# Check /var disk usage by subdirectory
sudo du -sh /var/*  2>/dev/null | sort -hr | head -10
# Find what is eating /var disk space

# Clean up old systemd journal logs
sudo journalctl --vacuum-size=500M    # keep last 500MB of logs
sudo journalctl --vacuum-time=7d      # keep last 7 days of logs

/usr — programs and libraries

/usr (Unix System Resources, or historically “user”) is the largest directory on most systems. It holds the majority of installed software: executables, libraries, header files, documentation, and shared data.

ls /usr/
# bin  include  lib  lib64  local  sbin  share

# /usr/bin: executable programs for all users
ls /usr/bin/ | head -20
# bash  cat  chmod  cp  curl  date  dd  df  dig  du
# echo  find  git  grep  gzip  htop  ls  man  mv ...

which python3
# /usr/bin/python3

# /usr/lib: libraries (.so files) used by programs
ls /usr/lib/ | head -10
# gcc  python3  systemd  x86_64-linux-gnu  ...

# /usr/share: architecture-independent data (docs, icons, locale data)
ls /usr/share/ | head -10
# applications  bash-completion  doc  fonts  icons  locale  man ...

# /usr/include: C/C++ header files
ls /usr/include/ | head -5
# stdio.h  stdlib.h  string.h  unistd.h  ...

# /usr/local: software installed manually (not by the package manager)
ls /usr/local/
# bin  etc  include  lib  sbin  share  src

/usr/local: manually installed software

/usr/local is the conventional location for software you compile and install yourself, outside the package manager. When you run ./configure && make && sudo make install, the files go to /usr/local/bin, /usr/local/lib, and so on.

This keeps manually installed software separate from package-manager-installed software, so apt remove or dnf remove never accidentally removes something you compiled yourself.

# Example: manually installed software
ls /usr/local/bin/
# node  npm  hugo  custom-tool

# vs package manager installed software
ls /usr/bin/
# bash  python3  git  curl  ...

# /usr/local takes priority in PATH on most systems
echo $PATH
# /usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin

The /usr merge

On modern distributions, /bin, /sbin, /lib, and /lib64 are all symbolic links into /usr:

ls -la / | grep usr
# lrwxrwxrwx  bin -> usr/bin
# lrwxrwxrwx  lib -> usr/lib
# lrwxrwxrwx  lib64 -> usr/lib64
# lrwxrwxrwx  sbin -> usr/sbin

This “UsrMerge” happened on Fedora in 2012 and spread to Debian, Ubuntu, and Arch. The historical split between /bin (essential binaries for early boot) and /usr/bin (the rest) became meaningless on systems where /usr is on the same partition as /. With all binaries in /usr/bin, there is one canonical location for executables.

/opt — optional/third-party software

/opt is for self-contained software packages that are not distributed through the system package manager. Software that installs here keeps all its files together under one subdirectory rather than scattering them across /usr/bin, /usr/lib, and /etc.

ls /opt/
# google/      jetbrains/    cisco/     containerd/

ls /opt/google/
# chrome/

ls /opt/google/chrome/
# chrome  chrome-sandbox  libffmpeg.so  locales/  resources/
# (all Chrome files in one place)

# Compare to how apt-installed software is spread out:
dpkg -L firefox | head -10
# /usr/bin/firefox
# /usr/lib/firefox/firefox
# /usr/lib/firefox/libxul.so
# /usr/share/applications/firefox.desktop
# /usr/share/pixmaps/firefox.png
# (spread across /usr/bin, /usr/lib, /usr/share)

Why /opt exists

The FHS says /opt is for “add-on application software packages.” The intent is that a package in /opt is entirely self-contained: you can install it by extracting an archive to /opt/pkgname/ and uninstall it by deleting that directory. No files outside /opt/pkgname/ are touched.

This is the convention used by:

  • Proprietary software that ships a tarball (JetBrains IDEs, some database vendors)
  • Software installed by enterprise configuration management tools (Ansible, Chef, Puppet)
  • Some container runtimes and cloud agents that want isolation from system packages
# Typical /opt layout for a third-party app
/opt/myapp/
  bin/myapp          <- executable
  lib/               <- libraries
  conf/              <- configuration files
  data/              <- application data
  logs/              <- application logs

# Add to PATH so the executable is findable
echo 'export PATH="/opt/myapp/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

/tmp — temporary files

/tmp is for temporary files that are needed only during the current session or a short period. On most modern systems it is a tmpfs (a RAM-based virtual filesystem), making it very fast but clearing it on every reboot.

# Check if /tmp is tmpfs (RAM-backed)
df -hT /tmp
# Filesystem     Type   Size  Used Avail Use% Mounted on
# tmpfs          tmpfs  3.9G  1.2M  3.9G   1% /tmp
# (tmpfs = in RAM; size is usually half of system RAM)

# Or on systems without tmpfs:
# /dev/sda3      ext4   50G   ...       /tmp
# (disk-backed /tmp persists across reboots but is usually cleared by the OS)

# Create a temporary file
tmpfile=$(mktemp)
echo "scratch data" > "$tmpfile"
echo "$tmpfile"
# /tmp/tmp.XkLm3n9Q

# mktemp for directories
tmpdir=$(mktemp -d)
echo "$tmpdir"
# /tmp/tmp.aB7cDe2F

# Cleanup: either delete manually or let the system handle it
rm -f "$tmpfile"
rm -rf "$tmpdir"

/tmp vs /var/tmp

# /tmp: cleared on reboot (or after a few days on some distros)
# /var/tmp: persists across reboots, cleared after ~30 days

# systemd-tmpfiles handles the cleanup rules
cat /usr/lib/tmpfiles.d/tmp.conf
# d /tmp 1777 root root 10d
# d /var/tmp 1777 root root 30d

# In scripts: use /tmp for short-lived scratch files
# Use /var/tmp when you need the file to survive a service restart

/tmp permissions

/tmp uses the sticky bit (1777), which means anyone can create files and directories in it, but only the owner of a file can delete it. This prevents users from deleting each other’s temporary files.

ls -la / | grep tmp
# drwxrwxrwt  ...  tmp
# The 't' at the end = sticky bit set

# Create a file in /tmp
touch /tmp/myfile
ls -la /tmp/myfile
# -rw-r--r-- 1 colton colton 0 Jun 28 10:00 /tmp/myfile

# Another user cannot delete it (only you or root can)
sudo -u alice rm /tmp/myfile
# rm: cannot remove '/tmp/myfile': Operation not permitted

Other important directories

A complete picture of the Linux filesystem includes several other directories worth knowing:

# /boot -- kernel, initramfs, and bootloader
ls /boot/
# vmlinuz-6.8.0-45-generic    <- Linux kernel
# initrd.img-6.8.0-45-generic <- initial RAM disk
# grub/                        <- GRUB bootloader files
# efi/                         <- EFI bootloader (on UEFI systems)

# /dev -- device files
ls /dev/ | head -10
# sda   sda1  sda2       <- SATA/NVMe drives and partitions
# nvme0 nvme0n1          <- NVMe drives
# tty0  tty1  pts/       <- terminals
# null  zero  random     <- special devices
# /dev/null: discard output (> /dev/null)
# /dev/zero: source of zero bytes
# /dev/random: source of random bytes

# /proc -- virtual filesystem: kernel and process info (not on disk)
cat /proc/cpuinfo | head -10   # CPU information
cat /proc/meminfo | head -5    # memory information
cat /proc/version              # kernel version
ls /proc/ | head -5            # numbered dirs = running process IDs

# /sys -- virtual filesystem: hardware and driver info (not on disk)
cat /sys/class/net/eth0/speed  # network interface speed
cat /sys/block/sda/queue/rotational  # 0=SSD, 1=HDD

# /run -- runtime data (tmpfs, cleared each boot)
ls /run/
# lock/  user/  systemd/  NetworkManager/  ...
# PID files, sockets, and runtime state live here

# /media -- auto-mount point for removable media
ls /media/
# colton/   <- subdirectory per user
ls /media/colton/
# USB-DRIVE  MyDisk      <- auto-mounted USB drives and optical discs

# /mnt -- manual temporary mounts
# Convention: use /mnt for mounts you manage manually
sudo mount /dev/sdb1 /mnt
ls /mnt/

# /srv -- data served by this system
# Web server files: /srv/www/ or /var/www/ (varies by distro)
# FTP files: /srv/ftp/

# /root -- home directory of the root user
sudo ls /root/

Finding where things live

# Find an executable
which python3
# /usr/bin/python3

whereis nginx
# nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/doc/nginx

# Find all files belonging to a package (Debian/Ubuntu)
dpkg -L nginx | head -10
# /etc/nginx
# /etc/nginx/nginx.conf
# /usr/sbin/nginx
# /usr/share/doc/nginx

# Find which package a file belongs to
dpkg -S /usr/bin/python3
# python3-minimal: /usr/bin/python3

# Fedora equivalent
rpm -qf /usr/bin/python3
# python3-3.12.4-1.fc40.x86_64

# Search for a file anywhere on the system
sudo find / -name "sshd_config" 2>/dev/null
# /etc/ssh/sshd_config

# Use locate for fast searching (if mlocate is installed)
locate sshd_config

Quick reference

DirectoryPurposeChanges during operation?
/Root of the filesystem treeNo
/homeUser files and personal configYes (user data)
/etcSystem-wide configurationOn admin changes
/varVariable data: logs, caches, databasesConstantly
/usrPrograms, libraries, documentationOn package changes
/usr/localManually installed softwareOn manual installs
/optSelf-contained third-party softwareOn manual installs
/tmpShort-lived temporary filesConstantly, cleared at boot
/var/tmpTemporary files that survive rebootsFrequently, cleared after 30 days
/bootKernel and bootloaderOn kernel updates
/devDevice files (virtual)Dynamically by kernel
/procProcess and kernel info (virtual)Real-time
/sysHardware and driver info (virtual)Real-time
/runRuntime data (cleared at boot)During operation
/rootRoot user’s home directoryOn root user activity

Frequently Asked Questions

Why does Linux use a single directory tree instead of drive letters like Windows?

Linux (and Unix before it) was designed around the idea that everything is a file. Physical drives, network shares, virtual filesystems, hardware devices, and running processes are all represented as files somewhere in the directory tree. Rather than assigning separate root namespaces per device (C:, D:, E:), Linux mounts all storage under a single tree rooted at /. A second hard drive or USB stick is mounted at a directory like /mnt/usb or /media/username/drive, and it appears as a folder inside the same tree. This design makes scripting and system administration more consistent: a path is always a path, regardless of which physical device it lives on.

What is the Filesystem Hierarchy Standard?

The Filesystem Hierarchy Standard (FHS) is a document maintained by the Linux Foundation that specifies what should go in each directory of a Linux system. It defines the purpose of /, /usr, /var, /etc, /home, /tmp, /opt, /srv, /proc, /sys, and others. Following the FHS means that a system administrator familiar with one Linux distribution can navigate another distribution’s filesystem predictably. Most major distributions (Debian, Fedora, Arch, and their derivatives) follow the FHS with minor variations. The FHS exists because Linux inherited a filesystem layout from Unix in the 1970s, and standardizing it makes software that expects files in specific locations work across distributions.

What is the difference between /usr/bin and /bin?

Historically, /bin contained binaries needed for basic system operation during early boot (before /usr was mounted), while /usr/bin contained the majority of user programs. This distinction dates from when /usr could live on a separate disk partition from /. On modern Linux systems, this separation is obsolete: almost all distributions now make /bin a symbolic link to /usr/bin, and /sbin a symbolic link to /usr/sbin. Everything is in /usr. You can verify this with ls -la /bin — on Ubuntu, Fedora, Arch, and Debian, it shows /bin -> usr/bin. The FHS still documents both directories for compatibility, but in practice they are unified on all current distributions.

Why is /etc called “etc” and what goes in it?

The name /etc comes from early Unix where it was literally the “et cetera” directory for files that did not fit elsewhere. Over time it became specifically the directory for system-wide configuration files. Today /etc contains: network configuration (/etc/hosts, /etc/resolv.conf, /etc/network/), user and group databases (/etc/passwd, /etc/shadow, /etc/group), service configuration (/etc/ssh/sshd_config, /etc/nginx/, /etc/mysql/), system settings (/etc/fstab, /etc/locale.conf, /etc/timezone), and package manager configuration (/etc/apt/, /etc/dnf/). Configuration in /etc is plain text, which makes it version-controllable with git and human-readable. The rule is: if a daemon or system service needs a configuration file, it goes in /etc.

What is the difference between /tmp and /var/tmp?

/tmp is for temporary files that do not need to survive a reboot. On most modern Linux systems, /tmp is a tmpfs (a RAM-backed filesystem), so it is fast but limited in size and cleared on every reboot. /var/tmp is for temporary files that should persist across reboots — files that need to survive a system restart but are not permanent enough to belong in a permanent location. In practice: use /tmp for scratch space in scripts and applications where the data is only needed during the current session; use /var/tmp for files that need to persist but will eventually be cleaned up. The systemd-tmpfiles service manages cleanup of both: /tmp files are removed after a short time (10 days by default on some distributions), /var/tmp files are removed after 30 days.

What should go in /opt and who uses it?

/opt is for “optional” or third-party software packages that are self-contained and not distributed through the system package manager. Software installed in /opt typically stores all its files under its own subdirectory: /opt/appname/bin/, /opt/appname/lib/, /opt/appname/etc/. This keeps the software isolated from the system directories and makes it easy to remove (just delete /opt/appname). Examples of software that commonly installs to /opt: Google Chrome (/opt/google/chrome/), JetBrains IDEs (/opt/jetbrains/), some proprietary software (Cisco AnyConnect, some gaming clients). Contrast this with software installed via apt or dnf, which distributes its files across /usr/bin, /usr/lib, /etc, and other standard locations following the FHS.