FHS#
The Filesystem Hierarchy Standard defines the standard layout of a Linux system. Which directories must exist at the root, what each one is for, and what user space is allowed to write into. Distributions add their own conventions on top, but the top level is portable.
Path |
Purpose |
|---|---|
|
Root of the entire filesystem tree. |
|
Essential user binaries (modern distros symlink to |
|
Essential system binaries for the superuser (symlink to |
|
Kernel images, initramfs, bootloader configuration. |
|
Device nodes ( |
|
System configuration files. |
|
User home directories (one per user). |
|
Essential shared libraries (modern distros symlink to |
|
64-bit shared libraries on multi-arch systems (symlink to |
|
Mount points for removable media (USB, optical). |
|
Mount points for temporarily-mounted filesystems. |
|
Optional, self-contained third-party packages. |
|
Kernel virtual filesystem; one directory per running process. |
|
The root user’s home directory (not the same as |
|
Runtime data since boot (PID files, sockets); cleared at boot. |
|
Data for services this host provides (web, FTP). |
|
Kernel virtual filesystem exposing devices and kernel state. |
|
Temporary files; usually cleared on reboot. |
|
Distribution-provided programs, libraries, docs, headers. |
|
Variable data such as logs, spools, and caches. |
/ (Root)#
The single root of the filesystem tree. Every absolute path begins here; everything else is layered on top via mounts. The root partition is mounted read-only by the kernel during early boot, then remounted read-write once the initramfs has confirmed the disk is healthy.
$ df -h /
$ findmnt /
/bin, /sbin, /lib, /lib64#
Symlinks into /usr on modern distributions. The /usr-merge
transition consolidated essential and non-essential binaries into a
single tree, eliminating the historical split that existed back when
/usr was often a separately-mounted filesystem. Most distros
completed it between 2012 and 2020.
Path |
Target |
|---|---|
|
|
|
|
|
|
|
|
$ readlink /bin
$ ls -l / | grep -E 'bin|lib'
/boot#
Files needed to bring up the kernel. Kernel image, initramfs, and
bootloader configuration such as GRUB or systemd-boot. Often a small
separate partition mounted only at boot, so corruption of the rest of
the disk does not prevent the system from starting up and recovering.
On UEFI systems the EFI System Partition is mounted at /boot/efi.
Path |
Purpose |
|---|---|
|
Compressed kernel image |
|
Initial ramdisk (Debian uses |
|
Kernel build configuration |
|
Kernel symbol map |
|
GRUB bootloader files |
|
GRUB configuration (regenerated by |
|
EFI System Partition mount point (UEFI systems) |
|
Per-OS EFI bootloaders |
|
systemd-boot configuration |
$ ls /boot
$ sudo update-grub
$ uname -r
/dev#
Device nodes. Character devices, block devices, and pseudo-devices the
kernel exposes. Populated dynamically by udev / systemd-udevd
at boot and on hotplug events, replacing the old static /dev
tarballs distributions used to ship with thousands of pre-created
nodes. Today the tree only contains hardware actually present.
Path |
Purpose |
|---|---|
|
First SATA / SCSI / USB block device |
|
Second SATA / SCSI / USB block device |
|
First partition on |
|
First NVMe namespace |
|
First partition of first NVMe namespace |
|
Discards writes; reads return EOF |
|
Endless stream of zero bytes |
|
Always returns |
|
Blocking cryptographic randomness |
|
Non-blocking randomness (use this) |
|
Controlling terminal of the calling process |
|
System console |
|
Pseudo-terminal slaves (per SSH / terminal session) |
|
POSIX shared memory ( |
|
Disks indexed by filesystem UUID |
|
Disks indexed by filesystem label |
|
Disks indexed by stable hardware ID |
|
Disks indexed by physical bus path |
|
Loop device for mounting a file as a block device |
|
Device-mapper nodes (LUKS, LVM, multipath) |
|
Software-RAID array ( |
|
Input devices (keyboards, mice) |
$ ls /dev/disk/by-uuid/
$ head -c 32 /dev/urandom | base64
$ command > /dev/null 2>&1
/etc#
System-wide configuration. Plain text, version-controllable, and the
single source of truth for system identity, accounts, services,
networking, and boot-time behavior. Per-package config typically lives
in a package-named subdirectory; drop-in fragments under *.d/
directories are the modern convention for layering site overrides on
top of distribution defaults.
Path |
Purpose |
|---|---|
|
User accounts (no passwords; identity only) |
|
Password hashes (root-readable only) |
|
Groups and members |
|
Group password hashes |
|
Static hostname → IP mappings (consulted before DNS) |
|
System hostname |
|
DNS resolver config |
|
Order of name-service sources |
|
Persistent mount config |
|
Currently-mounted FS (often → |
|
Sudo policy (edit with |
|
Sudo policy fragments |
|
SSH client and server config |
|
systemd configuration |
|
System cron job fragments |
|
Scripts run daily |
|
Scripts run hourly |
|
Scripts run weekly |
|
Scripts run monthly |
|
System crontab |
|
APT sources and config (Debian / Ubuntu) |
|
yum / dnf repos (RHEL family) |
|
Distribution identity |
|
System-wide login-shell init |
|
Drop-in scripts sourced by |
|
System-wide interactive bash init |
|
Kernel-parameter overrides applied at boot |
|
Drop-in sysctl fragments |
|
PAM limits and security configuration |
|
Per-service PAM config |
|
ifupdown network config (Debian) |
|
Netplan YAML (modern Ubuntu) |
|
NetworkManager profiles |
|
Persistent iptables rules |
|
nftables ruleset |
|
Timezone name (Debian) |
|
Timezone binary (symlink under |
|
Defaults for init / package scripts |
$ cat /etc/os-release
$ sudo visudo
$ sudo systemctl reload sshd
/etc/ssh/#
SSH client and server configuration. Daemon settings, host key pairs, drop-in config fragments, and the moduli file used during Diffie-Hellman key exchange. Editing files here changes how the system authenticates inbound and outbound SSH sessions for every user on the host, so changes are typically version-controlled and reviewed.
Path |
Purpose |
|---|---|
|
SSH daemon configuration |
|
System-wide SSH client defaults |
|
Drop-in daemon config fragments |
|
Host RSA private key |
|
Host Ed25519 private key |
|
Corresponding public keys |
|
DH group exchange parameters |
$ sudo sshd -t
$ ssh-keyscan -t ed25519 localhost
/etc/systemd/#
systemd configuration root. Holds local unit files and overrides,
manager-wide settings, and per-service configuration for journald,
logind, resolved, networkd, and timesyncd. Anything written here takes
precedence over the distribution-shipped equivalents under
/usr/lib/systemd/, which is the recommended place to keep local
customizations isolated from package upgrades.
Path |
Purpose |
|---|---|
|
Local unit files and overrides |
|
Manager-wide systemd configuration |
|
Per-user manager configuration |
|
Journal (logging) configuration |
|
logind configuration |
|
resolved configuration |
|
networkd configuration |
$ systemctl list-unit-files | head
$ sudo systemctl edit nginx.service
/home#
User home directories, one subdirectory per user account. Per-user
configuration lives in dotfiles (filenames starting with .,
hidden from default ls). Modern systems also follow the XDG
base-directory spec, splitting state across .config, .cache,
.local/share, and .local/state.
Path |
Purpose |
|---|---|
|
Individual user’s home |
|
Interactive Bash init |
|
Login-shell init (Bash) |
|
POSIX login-shell environment |
|
Zsh init |
|
Per-user SSH config and keys |
|
GnuPG keyring and config |
|
User-level app config ( |
|
User-level cache ( |
|
User-installed app data ( |
|
User-installed binaries (added to |
|
Standard XDG Documents directory |
|
Standard XDG Downloads directory |
|
Standard XDG Pictures directory |
|
Standard XDG Videos directory |
$ echo "$HOME"
$ getent passwd $(id -un) | cut -d: -f6
~/.ssh/#
Per-user SSH configuration and keys. Contains the user’s key pairs,
the list of host fingerprints they trust, the list of public keys
allowed to log in to this account, and any per-host client options.
Critically permission-sensitive. Most files require mode 0600 and
the directory itself 0700, or sshd will refuse the login.
Path |
Purpose |
|---|---|
|
Private key (mode |
|
Public key |
|
Public keys allowed to log in (mode |
|
Remembered host fingerprints |
|
Per-host client options |
$ ssh-keygen -t ed25519 -C "me@host"
$ ssh-copy-id user@host
/media#
Auto-mount points for removable media (USB sticks, optical disks,
phones, SD cards). Most desktop environments populate
/media/<user>/<label> when media is inserted. udisks2 together
with polkit arbitrates which logged-in user is allowed to mount
what, so users without console sessions cannot silently access
plugged-in devices.
Path |
Purpose |
|---|---|
|
Per-user mount root |
|
Auto-mounted volume by filesystem label |
$ ls /media/$USER
/mnt#
Mount points for filesystems mounted manually or temporarily by an
administrator. No standard subdirectory layout. Conventions vary by
site (/mnt/data, /mnt/backup, /mnt/usb). Distinct from
/media (which is reserved for desktop auto-mounts) by long-standing
FHS convention. Admins reach for /mnt when scripting one-off
mounts during recovery or migration.
Path |
Purpose |
|---|---|
|
Conventional manual data mount |
|
Conventional backup mount |
|
Conventional USB mount |
$ sudo mkdir -p /mnt/usb
$ sudo mount /dev/sdb1 /mnt/usb
$ sudo umount /mnt/usb
/opt#
Self-contained third-party software, installed outside the distribution’s package manager. Each vendor gets its own subdirectory holding its complete tree of binaries, libraries, and data, which keeps proprietary applications isolated from the rest of the system. Common for closed-source bundles such as Chrome, Zoom, and various IDEs that ship as a single drop-in archive.
Path |
Purpose |
|---|---|
|
Vendor’s complete install |
|
Vendor binaries (often added to |
|
Vendor libraries |
|
Common real-world example, Google Chrome |
|
Common real-world example, Zoom client |
$ ls /opt
$ /opt/google/chrome/google-chrome --version
/proc#
Kernel virtual filesystem (procfs). Each running process has a
numbered subdirectory; the top level also exposes kernel state and
tunables. Reads are evaluated by the kernel on each access, so the
contents reflect live system state rather than anything stored on disk.
Path |
Purpose |
|---|---|
|
One directory per running process |
|
Symlink to the caller’s own |
|
CPU topology and flags |
|
Memory statistics |
|
Load average |
|
System uptime |
|
Kernel version |
|
Kernel boot command line |
|
Loaded kernel modules |
|
Per-IRQ counters |
|
Currently mounted filesystems |
|
Per-protocol network tables |
|
Tunable kernel parameters ( |
$ nproc
$ awk '/MemTotal/ {print $2}' /proc/meminfo
$ uptime
/proc/<pid>/#
Per-process kernel-exposed state. Each running process has a numbered directory whose virtual files describe everything the kernel knows about it. Open file descriptors, memory map, environment, command line, namespaces, threads, scheduling stats, and resource limits. Reads return live values evaluated by the kernel at the moment of access.
Path |
Purpose |
|---|---|
|
Human-readable process state |
|
Launch command (NUL-separated) |
|
Environment variables (NUL-separated) |
|
Open file descriptors (symlinks to targets) |
|
Memory regions |
|
Symlink to the executable file |
|
Symlink to the working directory |
|
Namespace handles |
$ ls -l /proc/$$/fd
$ tr '\0' '\n' < /proc/self/cmdline
$ readlink /proc/$(pidof sshd)/exe
/proc/sys/#
Tunable kernel parameters, the backing store for sysctl. Each
file maps one-to-one with a setting. Writing into the file changes
kernel behavior immediately for the running system, while
/etc/sysctl.conf and the /etc/sysctl.d/ drop-in directory make
those changes persistent across reboots by re-applying them at start.
Path |
Purpose |
|---|---|
|
IPv4 stack tunables |
|
IPv6 stack tunables |
|
Kernel-wide tunables |
|
Virtual-memory tunables |
|
Filesystem tunables (e.g. |
$ cat /proc/sys/net/ipv4/ip_forward
$ sudo sysctl -w net.ipv4.ip_forward=1
/root#
The root user’s home directory, not the same as /. It lives on
the root partition rather than under /home so that root can still
log in even when /home is on a separate volume that fails to mount
during boot.
$ sudo ls -la /root
/run#
Volatile runtime state, recreated at every boot. Replaces the older
practice of putting PID files and sockets under /var/run;
/var/run and /var/lock now exist only as compatibility
symlinks. Backed by a tmpfs so the directory starts empty after
each reboot, which keeps stale PID files and lock files from confusing
daemons across power-cycles.
Path |
Purpose |
|---|---|
|
PID files for daemons |
|
systemd runtime state |
|
Per-user runtime dir ( |
|
Lock files |
|
Volatile systemd journal (when persistent storage is off) |
|
udev runtime state |
|
D-Bus system socket |
|
NetworkManager runtime |
$ ls /run/*.pid
$ echo "$XDG_RUNTIME_DIR"
/srv#
Data served by services this host provides (web roots, FTP shares,
hosted git, NFS exports). Less universally adopted than other FHS
directories. Some sites use it heavily, others keep service data in
/var/lib or /opt instead.
Path |
Purpose |
|---|---|
|
Web service data (some distros) |
|
Web service data (other distros) |
|
FTP service data |
|
Hosted git repositories |
|
Exported NFS roots |
$ ls /srv 2>/dev/null
/sys#
Kernel object filesystem (sysfs). Exposes devices, drivers, and
kernel internals as a tree of files. Writable entries here change
kernel state directly, which is how userspace tools toggle module
parameters, reset PCI devices, or tune the scheduler without a reboot.
Path |
Purpose |
|---|---|
|
Block devices and their queues |
|
Device classes ( |
|
Physical device tree |
|
Buses (PCI, USB, I2C, …) and bound drivers |
|
Filesystem-specific tunables |
|
cgroup v2 hierarchy |
|
Kernel internals ( |
|
Loaded kernel modules and their parameters |
|
Firmware-exposed data (ACPI, EFI, device tree) |
|
Power management ( |
$ cat /sys/class/net/eth0/address
$ echo mem | sudo tee /sys/power/state
/sys/class/#
Devices grouped by their kernel-exposed class (network interfaces,
block devices, terminals, batteries, thermal zones, LEDs, input
devices, and so on). Each entry is a symlink into /sys/devices/
with a uniform set of attribute files specific to that class, so
tooling can iterate one directory and discover every device of a
given kind.
Path |
Purpose |
|---|---|
|
Network interfaces |
|
Block devices |
|
Serial / terminal devices |
|
Battery / AC adapter state |
|
Thermal zones and cooling devices |
|
LED devices |
$ cat /sys/class/power_supply/BAT0/capacity
$ ls /sys/class/net/
/tmp#
World-writable temporary storage with the sticky bit set (only
the file’s owner, or root, can delete it). Often a tmpfs mount,
so contents are lost at reboot. Treat it as scratch space and never
store anything important there.
$ tmp=$(mktemp /tmp/myapp.XXXXXX)
$ tmpd=$(mktemp -d /tmp/myapp.XXXXXX)
$ ls -ld /tmp
/usr#
The bulk of the distribution. Programs, libraries, headers, docs, and shared data installed by the package manager. Considered read-only from the OS’s perspective. Package upgrades write here, local administrators don’t.
Path |
Purpose |
|---|---|
|
User binaries (most commands live here) |
|
System binaries (administrative commands) |
|
Shared libraries and per-package data |
|
64-bit libraries on multi-arch systems |
|
Helper binaries not intended for direct invocation |
|
C / C++ header files |
|
Architecture-independent shared data |
|
Local installs outside the package manager |
|
Kernel headers / source (when installed) |
$ which ls
$ ls /usr/lib | head
/usr/local/#
Local installs outside the package manager. Has its own FHS-shaped
sub-tree (bin, sbin, lib, etc, share,
include), so software built from source via
./configure --prefix=/usr/local slots in cleanly without colliding
with distribution packages under /usr. Distribution upgrades will
not touch anything below this directory.
Path |
Purpose |
|---|---|
|
Locally-installed user binaries |
|
Locally-installed system binaries |
|
Locally-installed libraries |
|
Configuration for software under |
|
Local architecture-independent shared data |
|
Locally-installed headers |
$ ls /usr/local/bin
$ echo "$PATH" | tr : '\n' | grep local
/var#
Variable data that grows during normal operation. Logs, package databases, service state, queued jobs, and per-service caches. Often a separate partition so that runaway logs or growing service state don’t fill the root filesystem and take the whole machine down.
Path |
Purpose |
|---|---|
|
System and service logs |
|
Regenerable application caches |
|
Persistent service state |
|
Queued work |
|
Temp files preserved across reboots |
|
Symlink → |
|
Symlink → |
|
Legacy web root |
$ du -sh /var/* 2>/dev/null | sort -h
$ sudo journalctl --disk-usage
/var/log/#
System and service logs. The default destination for rsyslog,
journald (when persistent storage is configured), and most
application loggers. Rotated and compressed by logrotate to bound
disk usage. Failure to bound this directory is a classic root cause of
unexpected service outages on long-lived hosts.
Path |
Purpose |
|---|---|
|
General log (Debian / Ubuntu) |
|
General log (RHEL family) |
|
Kernel log |
|
Authentication log (Debian) |
|
Authentication log (RHEL) |
|
Persistent systemd journal |
|
|
|
APT history (Debian) |
|
dpkg install / remove events |
$ sudo tail -f /var/log/syslog
$ sudo journalctl -u sshd --since "1 hour ago"
/var/lib/#
Persistent service state. Database files, package-manager metadata,
container images and volumes, systemd state, and any other data that
must survive reboots but is not user-editable configuration.
Backup-critical. Losing /var/lib means losing what services know
about the world, which is generally harder to recover than the config
under /etc.
Path |
Purpose |
|---|---|
|
PostgreSQL data directory |
|
MySQL / MariaDB data |
|
Docker images, volumes, containers |
|
containerd state |
|
Debian package database |
|
RPM package database |
|
systemd persistent state |
$ sudo du -sh /var/lib/docker
$ dpkg -l | wc -l
/var/spool/#
Queued work waiting to be processed (mail in flight, cron at-jobs, print jobs, package-manager transactions in progress). Each subdirectory belongs to a specific service whose daemon drains the queue as work completes. Sustained size growth here usually indicates a stalled processor (mail server down, print spooler blocked) rather than runaway producers.
Path |
Purpose |
|---|---|
|
Mail queue |
|
Per-user cron tables |
|
Print jobs |
|
Postfix queue (when running Postfix) |
$ sudo ls /var/spool/cron/crontabs
$ lpstat -o
References#
man hier(filesystem hierarchy overview).man 5 proc,man 5 sysfs(kernel virtual filesystems).man 5 passwd,man 5 shadow,man 5 fstab,man 5 sudoers(system identity and mount config).man 5 nsswitch.conf,man 5 sysctl.conf(name-service and kernel-tunable config).man udev,man 7 random,man null(device nodes and randomness).man sshd_config,man ssh_config,man ssh,man ssh-keygen(OpenSSH config and keys).man systemd,man systemd.unit,man systemd.service(systemd unit model).man udisksctl(removable-media mounts).man mount,man umount,man fstab(mounting filesystems).man 7 namespaces(per-process namespaces under/proc/<pid>/ns/).man sysctl,man sysctl.conf(runtime and persistent kernel tunables).man pid_namespaces(PID-file conventions and namespaces).man mktemp,man tmpfs(temporary storage primitives).man 5 manpath,man 1 desktop-file-validate(shared data under/usr/share).man journalctl,man rsyslog.conf,man logrotate(system logging and rotation).man dpkg,man rpm(package databases under/var/lib).man crontab,man cups(queued work under/var/spool).man grub-mkconfig,man update-grub(bootloader configuration).Permissions for users, groups, sudo, PAM, and capabilities.
Processes for
/proc/<pid>and process inspection.Packages for package manager state under
/var/lib.Services for the systemd unit lifecycle.
SSH for the SSH section.
Filesystems for the comparison of ext4, XFS, Btrfs, ZFS, and others.
Signals and Exit Codes for signal numbers seen in
/proc/<pid>/status.Ports for common service ports referenced in
/etc/services.Bash for shell startup-file ordering and shortcuts.
Bash for common Bash snippets.
Linux for the Linux command quick reference.