Linux Filesystem Hierarchy: What Each Directory Is For
Diagnosing a server problem quickly often starts with one question: what kind of data is this? Host configuration usually belongs in /etc, a service's changing state in /var/lib, its logs in /var/log, and anything that only needs to survive the current boot in /run. This layout is not something to memorize for its own sake — it exists so that packages, administrators, and running services do not end up overwriting each other's files.
The previous article, Disk and File System Basics, showed how different file systems attach to one / tree. This one covers what the major directories in that tree actually mean. The examples assume a current Ubuntu Server LTS install; the Filesystem Hierarchy Standard (FHS) sets general direction, but the exact layout on any given machine is decided by distribution policy and package documentation. You will learn to separate / from the root account and from /root; understand the major top-level directories through the lifecycle of the data they hold; choose correctly between /etc, /usr, /var, /run, /tmp, /opt, and /srv; recognize that /dev, /proc, and /sys are not ordinary disk directories; understand why paths like /bin are often symbolic links because of usr-merge; and finally locate a real program's binary, configuration, persistent state, and logs with evidence rather than guesswork.
What the FHS Standardizes
The Filesystem Hierarchy Standard (FHS) sets requirements and recommendations for where files and directories belong on Unix-like systems. The goal is that applications, administrators, documentation, and management tools can all rely on the same meaning for a given path. The FHS does not define a kernel file system format — it says nothing about how ext4 or XFS store data on disk.
FHS 3.0 is a useful baseline, but modern Linux systems layer additional rules on top of it from systemd, containers, immutable images, Snap and Flatpak packaging, usr-merge, and distribution-specific policy. Because of that:
- learn a directory's general meaning from the FHS;
- confirm the actual state on the real host with a command;
- check the specific package's or service's documentation for that distribution.
A safe way to look at the top-level tree:
find / -maxdepth 1 -mindepth 1 -printf '%f\n' 2> "$HOME/root-find-errors.txt" | sort
printf 'find errors: '
wc -l < "$HOME/root-find-errors.txt"
The result varies by distribution, mounted file systems, and installed software. Seeing a name under / does not mean the FHS mandates it.
The Root: /
/ is the starting point of the directory hierarchy a process sees. An absolute path is resolved from /; a relative path is resolved from the current directory:
A representative result:
Do not conflate three different meanings of "root":
/: the root of the directory tree.root: the administrative account, normally UID0./root: that account's default home directory.
A process inside a container, chroot, or a separate mount namespace can see a different / altogether. An absolute path is only absolute relative to the tree that process happens to see, not necessarily to the whole physical host.
Reading Directories by Data Lifecycle
These questions are more useful than memorizing directory names when you encounter an unfamiliar path:
- Did the OS or a package provide this, or did an administrator install it locally?
- Is the data specific to this host, or could it be shared across hosts?
- Is the content permanent, tied to the current boot, or purely temporary?
- Is it configuration, persistent state, cache, or a log?
- Who manages it — the package manager, a service, or a user?
For example, /usr/bin/ssh is an executable provided by a package, /etc/ssh/ssh_config is host configuration, /var/lib/<service> is likely persistent state, and /run/<service> is current-boot runtime state.
System Programs: /usr, /bin, /sbin, and /lib
/usr
/usr is not a user's home directory. It is a large hierarchy of programs and mostly static resources provided by the distribution:
/usr/bin: most user-facing commands./usr/sbin: executables more oriented toward system administration./usr/liband its architecture-specific variants: libraries and internal application files./usr/share: architecture-independent data, documentation, locale files, and man pages./usr/include: development headers./usr/local: a separate hierarchy for software and data an administrator installs locally, outside the package manager.
Checking where a command actually comes from:
A representative final path:
Package ownership on Debian/Ubuntu:
The equivalent on RHEL-family systems:
Use only the command that matches the installed distribution. Manually editing a file that a package manages can be lost on the next upgrade, or can break the package manager's integrity checks.
usr-merge and Legacy Paths
On many current distributions, /bin, /sbin, and /lib* are not separate trees — they are symbolic links into /usr. A symbolic link is a special file type that stores another path as its target; its internal mechanics are covered in Hard and Symbolic Links. Here, the goal is only to read the actual state on a real host:
ls -ld shows the link itself and its stored target; readlink -f resolves any links and prints the final canonical path. A path that does not exist on this system can produce diagnostic noise, which is why standard error is redirected away in this example — do not read an unexpectedly empty result as "no link exists" without checking further.
A representative view on Ubuntu:
On an older or different distribution, these can be real directories instead. Do not write a script that assumes either "/bin is always a symlink" or "/bin is always its own file system." Historical paths like #!/bin/sh keep working precisely because of this compatibility symlink.
/usr/local
/usr/local separates software the site administrator manages from what the distribution's packages provide:
A small tool built from source or an internal build often lands in /usr/local/bin. Do not paper over a problem by moving a package-managed file into /usr/local — keep ownership and the update model clear.
Host Configuration: /etc
/etc holds host-specific system configuration. Examples:
Executable binaries or large application data do not belong in /etc. Secrets are not automatically safe just because they sit in /etc — ownership, permissions, backup, audit, and secret lifecycle still need to be managed deliberately.
Before changing a file here:
A production change usually follows: backup → edit → the application's own validator → diff → reload → health check → rollback plan. Do not edit /etc/fstab, SSH configuration, or service configuration casually just as a demonstration.
Where a package's shipped defaults live versus an administrator's own change is distribution-dependent. On Debian/Ubuntu, some package samples live under /usr/share/doc/<package>/; the RHEL family may use a different path or point to the package's own documentation instead.
Changing and Persistent Data: /var
/var holds data that changes while the system runs. Its subdirectories do not all mean the same thing:
/var/lib: persistent state for a service or package, surviving a reboot./var/log: log files, if a service is not sending its output to journald or a remote logging system instead./var/cache: cache data that can be regenerated./var/spool: queued data waiting to be processed./var/tmp: temporary files meant to persist longer than/tmp./var/opt: variable data for add-on software installed under/opt.
Database state such as /var/lib/postgresql or /var/lib/mysql is not "just a big file" you can delete casually. The service controls the format, transaction handling, and its own backup and restore process.
Checking usage without leaving the mount:
df -hT /var
du -xhd1 /var > "$HOME/var-usage.txt" 2> "$HOME/var-usage-errors.txt"
sort -h "$HOME/var-usage.txt" | tail -n 15
The existence of /var/log does not mean every service writes there. A systemd service's stdout and stderr can be captured by journald instead; identifying the actual source is covered later in the logging module using journalctl.
Runtime and Temporary Data: /run, /tmp, /var/tmp
/run
/run holds runtime data needed only for the current boot, and it is typically a tmpfs:
PID files, Unix sockets, locks, and per-service runtime directories live here. Content in /run is not expected to survive a reboot. Do not put a persistent database or user documents here.
/var/run and /var/lock are, on many modern systems, compatibility links into /run:
/tmp
/tmp is shared temporary space for various processes. Its permissions usually end with the sticky bit shown as t:
A representative permission string:
The sticky bit restricts users from deleting each other's files in a shared, world-writable directory. It does not provide confidentiality: a safe program does not create a file under a predictable name in a shared directory — it uses a tool such as mktemp to create a private, uniquely named temporary file.
Do not rely on /tmp always being cleared on every reboot. The cleanup policy depends on the distribution and mechanisms such as systemd-tmpfiles. For data that must survive, neither /tmp nor /var/tmp is a substitute for an actual backup.
User Directories: /home and /root
An ordinary account's home directory is often /home/<user>, but a directory service, automount configuration, or site policy can place it elsewhere. Check the account database instead of assuming:
/root is the default home directory for the root account. Do not assume it is /home/root. Dotfiles inside a home directory, such as .bashrc and .ssh, are user-level configuration; they are a different scope from system-wide configuration.
Hiding production service data inside an administrator's home directory confuses ownership, backup, and service isolation. A service's data belongs under whatever the distribution's policy assigns — typically /var/lib/<service>, /srv/..., or a dedicated application directory.
Boot Files: /boot
/boot holds the kernel image, initramfs, and bootloader-related static files, and it is often its own file system:
On UEFI systems, the EFI System Partition is commonly mounted at /boot/efi. The exact layout depends on the distribution and installation method.
Danger
Do not manually delete a kernel or initramfs file from /boot just because it "looks old." Doing so can break the bootloader's configuration and the package manager's own bookkeeping, leaving the system unable to boot. Investigate a space problem first with df, the list of installed kernel packages, and the distribution's official removal procedure — have a snapshot and recovery console ready before touching anything here.
Device and Kernel Interfaces: /dev, /proc, /sys
/dev
/dev holds device nodes and some runtime objects. For example:
/dev/nvme0n1: likely a block device node./dev/tty: the controlling terminal./dev/null: a special device that discards anything written to it./dev/urandom: the kernel's random-number interface.
This does not mean "all device data is stored in /dev" — a device node is a reference point to a kernel driver. udev manages many of these nodes and symlinks dynamically.
Writing incorrectly to a device node can damage the device or its data. Do not create experimental files inside /dev.
/proc
/proc is a pseudo file system that exposes process and kernel information:
findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS --target /proc
head -n 5 /proc/meminfo
printf 'current shell PID=%s\n' "$$"
sed -n '1,12p' "/proc/$$/status"
Many entries look read-only, but writing to certain files under /proc/sys changes a kernel parameter immediately. Do not run an echo ... > /proc/sys/... command copied from the internet in production — that belongs to sysctl policy, with backup and rollback planned in advance.
/sys
sysfs, mounted at /sys, presents kernel objects, devices, drivers, and their attributes hierarchically:
findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS --target /sys
find /sys/class/net -maxdepth 1 -mindepth 1 -printf '%f\n'
Some sysfs attributes are writable and change hardware or kernel behavior directly. Do not edit them as if they were an ordinary configuration directory.
Add-On Software and Server Data: /opt and /srv
/opt is used for self-contained add-on software packages. In the FHS model, a program lives under /opt/<provider>/<package>, its host configuration may go in /etc/opt, and its variable data in /var/opt. The actual vendor layout is defined by that vendor's own documentation.
/srv is meant for site-specific data a server provides, such as /srv/www/example. In practice, though, an Ubuntu package's default Nginx document root has historically been /var/www/html. Do not confuse the FHS's intent with a distribution's actual default — check the running configuration.
A possible internal layout for a backend service:
/opt/acme/api/ release code or vendor bundle
/etc/acme-api/ host configuration
/var/lib/acme-api/ persistent application state
/var/log/acme-api/ log files written to disk, if used
/run/acme-api/ PID, sockets, and current-boot runtime data
/srv/acme-api/public/ site data the service serves, where policy allows it
This is not a mandatory universal template. Distribution packaging, the systemd unit, container volumes, and organizational policy all decide the exact layout. Ownership and permissions are covered separately in the next module.
Directories for Mounting: /mnt and /media
/mnt: the traditional place for an administrator's manual, temporary mounts./media: often used for automatic mounts of removable media such as USB drives or optical discs.
An empty directory here is not an error. Do not guess mount status from directory contents:
Actually mounting something is outside the scope of this article. Mounting the wrong device, or mounting onto a busy mount point, can hide data and break a running service.
Practical Scenario: Finding an Unfamiliar File's Owner and Purpose
Problem: /usr/bin/ssh exists, but you need to determine which layer manages it, where its configuration lives, and where its runtime data goes.
-
Resolve the executable path:
-
On Debian/Ubuntu, check package ownership:
On the RHEL family:
-
Confirm the client configuration exists:
-
Compare which file system each path belongs to:
-
Conclusion:
/usr/bin/sshis a package-managed executable./etc/ssh/ssh_configis system-wide client configuration.~/.ssh/configis user-specific configuration, a different scope entirely.- the SSH server's state and logs are not necessarily stored anywhere near the client binary.
This method replaces the assumption that "everything with a related name lives together" with actual evidence. If the package is not installed, or a path differs, the result will reflect that.
Common Mistakes
- Treating
/, therootaccount, and/rootas one and the same concept. - Assuming
/usris a directory for user documents. - Assuming
/bin,/sbin, and/libare always separate real directories on every system. - Manually editing a package-managed file under
/usr. - Storing configuration, persistent state, cache, and runtime data all in the same directory.
- Relying on data in
/tmpor/runsurviving a reboot. - Expecting every service to have its own log file under
/var/log. - Treating
/procand/sysas ordinary, read-only disk directories. - Declaring
/opt,/srv, or/var/wwwa universal location for every application without checking policy. - Assuming a directory is a mount point without verifying whether it is empty or occupied.
- Copying a path from another distribution's documentation without checking this distribution's own policy.
Exercises
- Compare the type, owner, and mount of
/,/root, and your current$HOMEusingstatandfindmnt. - Run
ls -ldandreadlink -fon/bin,/sbin, and/lib, and determine whether this host hasusr-merge. - For
/run,/tmp, and/var/tmp, write a short note (no table needed) on file system type, permissions, and expected lifecycle. - Check
/dev,/proc, and/syswithfindmnt, and explain why none of them can be treated as an ordinary directory on an SSD. - Trace one installed command using
command -v,readlink -f, and your distribution's package query tool. - For a hypothetical
inventory-apiservice, choose paths for its code, configuration, persistent state, runtime socket, and logs, and justify each choice by lifecycle and ownership.
Verification criterion: given an unfamiliar path, you should be able to answer, with evidence, "what belongs here, who manages it, how long does it live, and which file system does it belong to?"
References
- Linux Foundation: Filesystem Hierarchy Standard 3.0: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
- Linux man-pages:
hier(7): https://man7.org/linux/man-pages/man7/hier.7.html - systemd:
file-hierarchy(7): https://www.freedesktop.org/software/systemd/man/latest/file-hierarchy.html - Linux man-pages:
proc(5): https://man7.org/linux/man-pages/man5/proc.5.html - Linux kernel documentation: sysfs: https://www.kernel.org/doc/html/latest/filesystems/sysfs.html
- Linux man-pages:
tmpfs(5): https://man7.org/linux/man-pages/man5/tmpfs.5.html - Debian wiki: UsrMerge: https://wiki.debian.org/UsrMerge
- Ubuntu manual pages:
systemd-tmpfiles(8): https://manpages.ubuntu.com/manpages/noble/man8/systemd-tmpfiles.8.html