Understanding the Linux Filesystem Hierarchy (FHS)
AI generated
$
/etc
Linux · Filesystem · FHS · Administration
Understanding the Linux Filesystem Hierarchy (FHS)
where configuration, logs and programs really belong

Mixing up /etc, /var, /usr and /opt leads to servers that cannot be automated, packaged cleanly, or maintained predictably. The Filesystem Hierarchy Standard defines what every top-level directory on Linux is meant for, and following it gets you systems that work smoothly with package managers, systemd and automation tooling instead of fighting them.

14 min read FHS · /etc · /var · /usr · /opt · /srv Debian · RHEL · systemd

1. What the FHS is and why it matters

The Filesystem Hierarchy Standard, or FHS for short, is not a rule enforced by the Linux kernel. It is a convention maintained by the Linux Foundation that defines what every top-level directory under /, /etc, /var, /usr and others is meant for. The kernel itself would happily accept configuration files living in /home or log files sitting inside an application folder, but doing exactly that leads to servers that can no longer be administered predictably. The FHS is therefore less a technical boundary and more a social contract between distributions, package managers, systemd units, and the administrators who work with these systems every day.

For developers coming from the application layer, the FHS can initially feel like unnecessary bureaucracy. In practice, though, it quickly becomes clear why the convention matters: backup scripts, monitoring agents, log rotation, and deployment automation all implicitly assume that configuration lives under /etc, runtime data under /var, and installed software under /usr or /opt. Breaking that assumption means reconfiguring every single tool by hand instead of relying on sensible defaults.

2. /etc: configuration, centralized and predictable

The name /etc originally stood for "et cetera", but today it is more commonly read as "editable text configuration", and that description captures its purpose well: host-specific, text-based configuration files that an administrator can read and adjust without recompiling a program. Systemd unit overrides live under /etc/systemd/system, webserver vhosts under /etc/apache2/sites-available or /etc/nginx/sites-available, and PHP-FPM pools under /etc/php/8.4/fpm/pool.d. No binaries, no executable programs in the strict sense, with only a handful of exceptions such as init scripts under /etc/init.d.

The rule for /etc is simple: anything that customizes the operation of a specific machine or application belongs here, never in the application code itself. A Magento project that stores database credentials or environment variables directly inside the deployment directory instead of in a file referenced from /etc loses exactly this separation. Permissions here are usually restrictive: root:root with 644 for most files, 640 for anything holding credentials, so only authorized processes can read them.


#!/usr/bin/env bash
# Explore /etc the way an admin would before touching a config file
set -euo pipefail

# List config files for a specific service, sorted by modification time
find /etc/php/8.4/fpm/pool.d -maxdepth 1 -name "*.conf" -printf "%T@ %p\n" \
  | sort -n | cut -d' ' -f2-

# Confirm ownership and permissions before editing anything under /etc
stat -c "%A %U:%G %n" /etc/nginx/sites-available/*.conf

# Tighten permissions on a file that contains credentials
install -o root -g www-data -m 640 .env.example /etc/magento/production.env

# Reload the service after an /etc change, never restart blindly
systemctl reload php8.4-fpm

3. /var: variable data, logs and databases

/var holds everything that changes during operation: log files under /var/log, caches under /var/cache, queues under /var/spool, and persistent application data under /var/lib, including MySQL's data directories under /var/lib/mysql. The key difference from /etc: content under /var grows and shrinks continuously, while configuration files under /etc only change when someone deliberately edits them. This distinction is why many servers put /var on its own, larger storage volume while keeping /etc on the system drive.

For backup strategy this distinction matters a lot: back up /etc small and often, because configuration changes are rare but critical. /var needs different rules, since log rotation and growth monitoring are necessary to keep the partition from filling up. logrotate reads its rules from /etc/logrotate.d but applies them to files under /var/log, a good example of how /etc and /var work together in the FHS without blending into each other.


; /etc/logrotate.d/magento - rules live in /etc, target files live in /var
/var/log/magento/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 640 www-data www-data
    sharedscripts
    postrotate
        systemctl reload php8.4-fpm > /dev/null 2>&1 || true
    endscript
}

; /etc/logrotate.d/nginx-access - same pattern for the webserver
/var/log/nginx/*.log {
    weekly
    rotate 8
    compress
    missingok
    create 640 www-data adm
}

4. /usr: programs, libraries and the split between /usr and /

/usr, historically short for "Unix System Resources", holds the bulk of installed software: programs under /usr/bin, libraries under /usr/lib, documentation under /usr/share/doc, and development header files under /usr/include. Originally /usr was meant to be its own, potentially read-only mountable partition that could even be shared across multiple machines on a network, while /, /etc and /var stayed local. That strict separation is rarer today, but the underlying principle remains: content under /usr is managed by the package manager, not edited by hand.

Modern distributions have used the so-called usrmerge to turn /bin, /sbin and /lib into symlinks pointing to their counterparts under /usr, so /bin/bash actually resolves to /usr/bin/bash. Self-compiled software that does not go through the package manager belongs under /usr/local, never directly under /usr, otherwise it risks conflicting with later package updates. This convention is one of the most frequently overlooked parts of the FHS, especially when developers want to make a quick self-built tool available system-wide.


#!/usr/bin/env bash
set -euo pipefail

# List every file a package installed, all under FHS-compliant paths
dpkg -L php8.4-fpm | grep -E '^/(etc|usr|var)/' | head -20

# Confirm the usrmerge symlink chain on a modern distro
readlink -f /bin/bash        # -> /usr/bin/bash
readlink -f /sbin/iptables   # -> /usr/sbin/iptables

# Self-built software: install into /usr/local, never /usr directly
./configure --prefix=/usr/local
make -j"$(nproc)"
sudo make install

# Verify no accidental writes landed outside the expected prefix
find /usr/local -newer /usr/local/.timestamp -type f 2>/dev/null || true

5. /opt and /srv: optional software and service data

/opt is meant for self-contained third-party software that does not follow the usual FHS-internal split but brings its own directory structure instead, typically under /opt/vendorname/productname, such as /opt/google/chrome or /opt/gitlab. The advantage: such a package can be removed cleanly just by deleting its folder, with no scattered files left behind under /usr/bin or /usr/lib to clean up. That is exactly why many commercial applications prefer /opt over the fine-grained FHS distribution.

/srv, by contrast, is meant for data that this system provides to others, such as /srv/www for web content, /srv/ftp for FTP shares, or /srv/git for hosted repositories. In practice /srv is used less consistently than /opt, with many distributions and hosting setups establishing their own conventions such as /var/www instead. The important distinction to keep in mind: /srv describes data offered to the outside world, /var describes the system's own internal operating state, even though the boundary often blurs in practice.

6. /home, /tmp and other top-level directories

/home holds the personal directories of all users, while /root serves as its own home directory for the superuser, deliberately kept separate from /home so root can still log in even if /home lives on a separate, potentially unmounted partition. /tmp is meant for short-lived, temporary files that get cleared on reboot or regularly by systemd-tmpfiles, while /var/tmp is for temporary files meant to survive a reboot.

/run, firmly established in the FHS for some years now, replaces the older /var/run and holds runtime data since the last boot: PID files, Unix sockets, locks. Since /run lives as a tmpfs in memory, it is automatically wiped on every reboot, a built-in safeguard against orphaned PID files after a crash. /boot holds the kernel and bootloader, /dev holds device files, and /proc and /sys are virtual interfaces generated by the kernel that you should never copy files into by hand, only write to through the mechanisms designed for them.

7. Why the convention matters for package management

dpkg and rpm rely entirely on FHS paths to know where a file belongs during installation and how it can be removed without leaving traces during uninstallation. The command dpkg -L packagename lists exactly which files a package placed under which FHS paths, and apt remove can trust that this list is complete. Without this predictability, every package would need its own cleanup script and would have to hope it truly finds every installed file.

The convention also prevents collisions between packages: binaries end up in /usr/bin, configuration in /etc, documentation in /usr/share/doc, and because every package follows this same layout, hundreds of packages can coexist without overwriting each other. This is exactly why /usr/local exists as an explicit fallback location for manually or self-compiled software, so it never conflicts with package-managed files when a later apt upgrade installs the same software from the repository.

8. Automation: the FHS as a contract for scripts and tools

Ansible playbooks, systemd units, monitoring agents such as Filebeat or the Prometheus Node Exporter, and backup scripts all assume FHS-compliant paths by default: logs under /var/log, configuration under /etc, PID files under /run. Meet that expectation and you can adopt standard configurations essentially unchanged. Break it, for example by putting logs inside the application directory under /opt/myapp/logs, and you must configure a divergent path for every single tool, which noticeably increases maintenance overhead over time.

When automating multiple servers across different distributions, FHS compliance pays off especially well: an Ansible playbook that configures log rotation for /var/log/app works identically on Debian, Ubuntu and RHEL, because all three respect the same FHS path. As soon as an application invents its own, distribution-specific paths, the automation must explicitly account for that deviation, making playbooks more complex and error-prone, particularly across heterogeneous server fleets.


# ansible/roles/magento-server/tasks/main.yml
# Relies entirely on standard FHS paths, no custom locations needed
---
- name: Ensure FHS-compliant log directory exists
  ansible.builtin.file:
    path: /var/log/magento
    state: directory
    owner: www-data
    group: www-data
    mode: "0750"

- name: Deploy application config into /etc, never into the codebase
  ansible.builtin.template:
    src: templates/production.env.j2
    dest: /etc/magento/production.env
    owner: root
    group: www-data
    mode: "0640"
  notify: reload php-fpm

- name: Symlink application var/log to the system-wide FHS location
  ansible.builtin.file:
    src: /var/log/magento
    dest: /srv/www/magento/var/log
    state: link
    force: true

- name: Install logrotate rule from a version-controlled template
  ansible.builtin.template:
    src: templates/logrotate-magento.j2
    dest: /etc/logrotate.d/magento
    mode: "0644"

9. Common FHS violations and placing PHP/Magento applications correctly

The most common violation in PHP and Magento projects: log files, cache and temporary uploads end up inside the application directory itself instead of being managed under /var/log, /var/cache or /run. That might seem convenient locally, but it breaks the moment log shippers or monitoring agents work with their default paths and simply cannot find the application. A second classic mistake: production code ends up in a deploy user's home directory, such as /home/deploy/magento-live, even though /home is meant for personal user data, not server workloads.

On a cleanly organized server, Magento code belongs under /var/www/magento, or under /srv/www/magento if the hosting stack consistently follows the /srv convention. The var/log and var/cache directories inside the Magento project should be symlinks pointing to /var/log/magento and /var/cache/magento respectively, so log rotation and monitoring pick them up automatically. Media uploads under pub/media belong on a separate volume with its own backup strategy, so a code deployment can never accidentally overwrite or discard customer data.

Area FHS violation FHS-compliant solution Why it matters
Application code /home/deploy/magento-live /var/www/magento or /srv/www/magento Home directories are for users, not production code
Log files var/log/ inside the project folder Symlink to /var/log/magento logrotate and monitoring expect logs under /var/log
Uploads/media In the webroot with no separation from code Dedicated volume, separate backup strategy Code deploys must never overwrite customer data
Temporary files Custom tmp/ folder inside the project /var/tmp with tmpfiles.d rules Automatic cleanup via systemd-tmpfiles
PID/lock files Hardcoded, e.g. /home/deploy/app.pid /run/magento/ (tmpfs, managed by systemd) /run is cleared on every boot, no stale PIDs

Applying this mapping consistently pays off twice over: standard tools such as logrotate, systemd-tmpfiles and monitoring agents work without extra configuration, and switching hosting providers or distributions becomes considerably less risky, because the application carries no implicit assumptions about non-standard paths.


{
  "deploy_target": "production",
  "paths": {
    "code": "/var/www/magento",
    "config": "/etc/magento/production.env",
    "log": "/var/log/magento",
    "cache": "/var/cache/magento",
    "media": "/var/www/magento-media",
    "pid": "/run/magento/deploy.pid"
  },
  "post_deploy": [
    "bin/magento setup:upgrade",
    "bin/magento cache:flush",
    "systemctl reload php8.4-fpm"
  ],
  "note": "Every path below is a stock FHS location, no custom mounts required"
}

Mironsoft

Server architecture, deployment automation and Linux administration for Magento

Servers that follow the FHS instead of fighting it?

We bring Magento and PHP deployments into a clean, FHS-compliant structure, set up log rotation, backups and automation along standard paths, and make your servers portable and maintainable as a result.

Server audit

Check the existing path structure against the FHS and prioritize risks

Deployment refactoring

Cleanly separate logs, cache and configuration into /var, /etc and /run

Automation

Build Ansible playbooks and systemd units on standard FHS paths

10. Summary

The Filesystem Hierarchy Standard solves a simple but far-reaching problem: without a shared convention, every tool, every package and every script would need to individually know where a given file lives on a given system. /etc collects configuration, /var collects variable runtime data, /usr collects installed software, /opt collects self-contained third-party packages, /srv collects data offered to the outside world. Respecting this mapping gets you systems that can be administered, backed up and automated with standard tools, without having to document every deviation by hand.

For PHP and Magento projects, FHS compliance pays off especially in logging, backups and deployment automation. A server that cleanly separates code, configuration and variable data along FHS lines can be operated with Ansible, systemd and standard monitoring without special cases, and a later switch of distribution or hosting provider becomes considerably less risky, because no implicit, undocumented path assumptions are baked into the code.

The Linux Filesystem Hierarchy (FHS) - the essentials at a glance

/etc vs. /var vs. /usr

/etc is configuration, /var is variable runtime data, /usr is package-managed software. Never mix them.

/opt vs. /srv

/opt for self-contained third-party packages, /srv for data this system offers to the outside world.

Package management

dpkg/rpm rely on FHS paths for clean installation and traceless removal.

PHP/Magento practice

Code under /var/www or /srv/www, logs symlinked to /var/log, media on a separate volume.

11. FAQ: The Linux Filesystem Hierarchy (FHS)

1What is the Filesystem Hierarchy Standard (FHS)?
A convention maintained by the Linux Foundation that defines what every top-level directory is meant for. The kernel does not enforce it, but package managers and systemd assume it.
2Does every Linux distribution have to follow the FHS?
Not mandatory, but Debian, Ubuntu, RHEL and Fedora follow it closely, because package managers and systemd build on it. Deviations mainly show up in details such as /srv.
3What is the difference between /etc and /var?
/etc holds static, host-specific configuration. /var holds variable runtime data such as logs, cache and database content that changes continuously.
4What is /usr for, and why not edit anything there by hand?
/usr holds package-managed software. Manual changes get overwritten by the next update. Self-compiled software belongs under /usr/local.
5When do I use /opt, and when do I use /srv?
/opt for self-contained third-party packages with their own structure. /srv for data the system offers to the outside world, such as web content or repositories.
6What belongs in /run, as opposed to /var/run?
/run holds runtime data since the last boot as a tmpfs in memory. /var/run is the older path and today is usually just a symlink to /run.
7Why does package management break when you deviate from the FHS?
dpkg/rpm rely on predictable paths for installation and removal. Deviations can cause collisions between packages and orphaned files.
8Where does Magento code belong on a clean server?
Under /var/www/magento or /srv/www/magento, never in a user's home directory. /home is meant for personal user data.
9Where do Magento logs and cache files belong?
var/log and var/cache in the project should be symlinks pointing to /var/log/magento and /var/cache/magento, so standard tools pick them up automatically.
10What is usrmerge, and why is /bin a symlink?
usrmerge turns /bin, /sbin and /lib into symlinks to their /usr counterparts, for a more unified, less fragmented path tree.