Opening a Linux server for the first time can feel like landing in the middle of somebody else’s project. There are directories everywhere, familiar files are missing, and changing the wrong thing can stop a website or service from working. The layout becomes much easier to understand once you know what each part of the filesystem is intended to contain.
Most Linux distributions follow the Filesystem Hierarchy Standard, commonly shortened to FHS. It defines conventional locations for system configuration, installed software, user files, logs and changing application data. Individual distributions and packages sometimes use different paths, but the overall structure remains broadly consistent.
/Linux uses a single directory tree beginning at /, known as the root directory.
This is different from Windows, where separate disks are usually represented by drive letters such as C: and D:. On Linux, additional disks and filesystems are mounted somewhere within the same directory tree.
A typical server might contain:
/
├── etc
├── home
├── opt
├── root
├── run
├── srv
├── tmp
├── usr
└── var
You do not need to memorise every directory. For day-to-day web development, a relatively small group of locations covers most of what you will use.
| Path | What it usually contains | Examples |
|---|---|---|
/etc |
System and service configuration | Nginx, Apache, PHP, SSH and database configuration |
/var |
Data that changes while the system is running | Logs, databases, caches, queues and websites |
/home |
Files belonging to normal users | SSH keys, shell settings and user-owned projects |
/srv |
Data served by the system | Websites, file servers and other hosted content |
/opt |
Self-contained third-party software | Monitoring agents and vendor applications |
/tmp |
Short-lived temporary files | Uploads, generated files and temporary archives |
/var/tmp |
Temporary files that may survive a reboot | Longer-running temporary work |
/usr |
Installed programs, libraries and shared resources | Commands, libraries and package files |
/usr/local |
Software installed by the administrator | Locally compiled programs and custom scripts |
/run |
Temporary runtime state | Sockets, process IDs and service state |
/root |
The root user’s home directory | Root’s SSH keys and shell configuration |
/var/log |
System and application logs | Nginx errors, authentication logs and package logs |
/var/lib |
Persistent state managed by services | Database data and package state |
The exact contents depend on the distribution, installed packages and how the server was configured.
/etc: system and service configurationThe /etc directory contains configuration for the operating system and installed services.
When you need to change how Nginx, PHP, SSH, MySQL or another service behaves, /etc is usually the first place to look.
Common examples include:
/etc/nginx/
/etc/apache2/
/etc/httpd/
/etc/php/
/etc/mysql/
/etc/postgresql/
/etc/redis/
/etc/ssh/
/etc/systemd/
/etc/supervisor/
Some files apply to the whole server. Others configure a particular website, PHP version, database instance or background worker.
For example, an Ubuntu server running Nginx might use:
/etc/nginx/nginx.conf
/etc/nginx/sites-available/example.com
/etc/nginx/sites-enabled/example.com
The main nginx.conf file contains global settings. Individual website configurations are commonly stored in sites-available, then enabled using symbolic links in sites-enabled. Ubuntu documents this layout for its packaged version of Nginx.
Apache uses a similar arrangement on Ubuntu and Debian:
/etc/apache2/apache2.conf
/etc/apache2/sites-available/
/etc/apache2/sites-enabled/
/etc/apache2/mods-available/
/etc/apache2/mods-enabled/
On Red Hat family distributions, Apache is normally called httpd and its configuration is commonly stored under:
/etc/httpd/conf/httpd.conf
/etc/httpd/conf.d/
Red Hat’s current documentation uses /etc/httpd for Apache configuration, while Ubuntu uses /etc/apache2.
A guide written for Ubuntu may not match Rocky Linux. A package installed from a third-party repository may use a different structure from the distribution package. Software compiled from source may use completely different locations.
Before editing a file, confirm that the running service actually reads it.
Useful commands include:
nginx -T
apachectl -V
php --ini
systemctl cat nginx
systemctl cat php8.4-fpm
nginx -T prints the complete Nginx configuration after resolving included files. The -t option checks the configuration without applying it.
PHP can load different configuration files for the command line, PHP-FPM and Apache. Running this command shows which files the current PHP command-line process uses:
php --ini
PHP searches several possible locations for php.ini, and the selected file can depend on how PHP is being run.
Before changing an important configuration file, create a copy:
sudo cp /etc/nginx/sites-available/example.com \
/etc/nginx/sites-available/example.com.backup
After editing it, test the configuration:
sudo nginx -t
For Apache:
sudo apachectl configtest
For OpenSSH:
sudo sshd -t
Only reload the service after the test succeeds.
sudo systemctl reload nginx
Testing first is particularly important when changing SSH, firewall or networking configuration. A syntax error can make the service unavailable and may lock you out of the server.
/var: data that changesThe name /var comes from variable data. It contains files that are expected to change as the system runs.
For web developers, /var is one of the most important parts of the filesystem.
| Path | Typical purpose |
|---|---|
/var/log |
System and application logs |
/var/lib |
Persistent service state and databases |
/var/cache |
Data that can usually be recreated |
/var/spool |
Queued work such as mail and scheduled jobs |
/var/www |
A common location for website files |
/var/tmp |
Temporary data that may remain after reboot |
/var/backups |
Backups created by packages or local processes |
/var/log: logsMany services write text logs beneath /var/log.
Common examples include:
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/apache2/access.log
/var/log/apache2/error.log
/var/log/httpd/access_log
/var/log/httpd/error_log
/var/log/auth.log
/var/log/secure
/var/log/mysql/
/var/log/php/
/var/log/unattended-upgrades/
Ubuntu describes /var/log as the normal location for system logs, although many modern services also send messages to the systemd journal.
To watch a file as new lines are written:
sudo tail -f /var/log/nginx/error.log
To view the last 100 lines:
sudo tail -n 100 /var/log/nginx/error.log
To search for a particular message:
sudo grep -i "permission denied" /var/log/nginx/error.log
Logs managed by systemd can be viewed with journalctl:
sudo journalctl -u nginx
sudo journalctl -u php8.4-fpm
sudo journalctl -u mysql
Follow new messages in real time:
sudo journalctl -u nginx -f
Show messages from the current boot:
sudo journalctl -u nginx -b
A service may write to a log file, the journal, or both. Check both when troubleshooting.
ServerAuth’s log viewer provides web-based access to common server logs and allows custom log paths to be added. This can be useful when a developer needs to inspect an application or service log without being given unrestricted SSH access.
/var/lib: service-owned persistent dataThe /var/lib directory stores data that belongs to installed services and needs to survive reboots.
Examples may include:
/var/lib/mysql/
/var/lib/postgresql/
/var/lib/redis/
/var/lib/docker/
/var/lib/systemd/
/var/lib/php/
These directories often contain live application state rather than configuration.
For example:
Avoid editing files in /var/lib by hand unless the service documentation specifically tells you to do so.
Copying a live database directory with cp is not a reliable database backup. Use the database’s backup tools, filesystem snapshots designed for the purpose, or a documented backup process.
To discover MySQL’s data directory:
mysql -NBe "SELECT @@datadir;"
For PostgreSQL:
sudo -u postgres psql -tAc "SHOW data_directory;"
PostgreSQL’s configuration files are traditionally stored in the database cluster directory, although Debian and Ubuntu packages commonly place configuration under /etc/postgresql/<version>/main.
/var/cache: replaceable cached dataCached data is stored under /var/cache.
Applications should be able to recreate these files if they are removed, although clearing a cache can temporarily slow the service or interrupt work.
Examples include package download caches and generated application caches.
Do not assume that every directory named cache can be deleted safely. Check what owns it first.
Useful commands include:
sudo du -sh /var/cache/*
sudo lsof +D /var/cache/example
/var/spool: queued workA spool contains work waiting to be processed.
Typical examples include:
Developers rarely need to edit spool files directly. If a queue is stuck, use the service’s management commands and logs rather than deleting files at random.
/var/www: a common website locationMany Ubuntu and Debian web servers use /var/www for website files.
The default Apache document root is commonly:
/var/www/html
A server hosting several sites may use:
/var/www/example.com
/var/www/shop.example.com
/var/www/api.example.com
A framework project might look like:
/var/www/example.com/
├── app
├── bootstrap
├── config
├── public
├── resources
├── routes
├── storage
└── vendor
For Laravel, Symfony and similar frameworks, the web server should point to the public directory rather than the project root:
/var/www/example.com/public
This prevents configuration files, dependencies and application source from being served directly.
/var/www is a widely used convention rather than the only valid location. The FHS identifies /srv as the directory intended for site-specific data served by the system. Ubuntu’s packaged web servers still commonly use /var/www/html as their default document root.
/home: normal user filesEach normal user usually receives a directory beneath /home.
For a user named rick, this would normally be:
/home/rick
The shorthand ~ means the current user’s home directory:
cd ~
A home directory may contain:
/home/rick/
├── .bashrc
├── .profile
├── .ssh
├── projects
└── scripts
Files beginning with a dot are hidden from a normal ls listing. Use:
ls -la
The user’s SSH configuration is stored beneath:
~/.ssh/
Common files include:
~/.ssh/authorized_keys
~/.ssh/config
~/.ssh/known_hosts
On a server, authorized_keys lists the public keys allowed to log in as that user.
Permissions are important:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
The directory and file should belong to the user:
chown -R rick:rick /home/rick/.ssh
Incorrect ownership or permissions can cause OpenSSH to ignore the key.
/home?Yes, although the permissions need to be planned properly.
An isolated project might use:
/home/clientname/example.com
This can be useful when each website runs as a separate system user or with its own PHP-FPM pool.
The web server still needs permission to move through each parent directory and read the public files. Giving broad read access to an entire home directory is rarely a good solution.
A better setup uses:
Ubuntu advises paying attention to home directory permissions on servers with multiple users because those directories can contain private information.
/srv: data served by the systemThe /srv directory is intended for data provided by services running on the machine.
Examples might include:
/srv/www/example.com
/srv/git/repository.git
/srv/files/downloads
The FHS describes /srv as the location for site-specific data served by the system.
A website layout under /srv could be:
/srv/www/example.com/
├── current
├── releases
└── shared
This structure is often used for release-based deployments:
/srv/www/example.com/releases/20260804210000
/srv/www/example.com/releases/20260803203000
/srv/www/example.com/shared
/srv/www/example.com/current
current is a symbolic link pointing to the active release.
ls -l /srv/www/example.com/current
This allows a deployment tool to prepare a new release in a separate directory, then switch the symlink when the deployment is ready.
Whether you choose /srv/www, /var/www or another location matters less than using a consistent structure and configuring permissions correctly.
/opt: self-contained third-party softwareThe /opt directory is intended for add-on application packages.
A third-party product may install itself beneath:
/opt/company-name/product-name
Examples include:
/opt/monitoring-agent
/opt/vendor/application
/opt/custom-dashboard
The Filesystem Hierarchy Standard expects software installed in /opt to keep its static files within its own package or provider directory.
/opt is useful for software that arrives as a self-contained bundle rather than a normal operating system package.
It should not become a general dumping ground for website files, temporary archives or miscellaneous scripts.
Locally maintained scripts are often better placed in:
/usr/local/bin
/usr/local/sbin
/tmp and /var/tmp: temporary files/tmp is used for short-lived temporary data.
Applications may write:
Many systems clear /tmp during reboot or according to an automatic cleanup policy. Files placed there should never be treated as permanent.
/var/tmp is also intended for temporary data, but files there are generally expected to survive a reboot for longer-running work. The exact cleanup policy still depends on the system.
/tmp is normally writable by every user.
Check its permissions:
ls -ld /tmp
A common result is:
drwxrwxrwt
The final t represents the sticky bit. It prevents users from deleting files owned by other users, even though the directory itself is shared.
Applications should create unpredictable filenames and set appropriate permissions. Do not place secrets, private keys or unprotected database exports in /tmp.
/usr: installed software and shared resourcesDespite its name, /usr is not normally used for individual user files.
It contains much of the software installed on the system:
/usr/bin
/usr/sbin
/usr/lib
/usr/share
/usr/binMost normal command-line programs live here:
/usr/bin/php
/usr/bin/git
/usr/bin/curl
/usr/bin/python3
Find the executable used by your shell:
command -v php
Resolve a symbolic link:
readlink -f "$(command -v php)"
/usr/sbinThis contains administrative commands and service binaries.
Examples may include:
/usr/sbin/nginx
/usr/sbin/sshd
/usr/sbin/php-fpm
/usr/libThis contains libraries and package-specific support files.
You will sometimes find systemd service files here:
/usr/lib/systemd/system/
On some Debian-based systems, vendor unit files may instead appear under:
/lib/systemd/system/
Avoid editing packaged service files directly. Package updates may replace them.
Use an override instead:
sudo systemctl edit example.service
Local systemd unit files and overrides belong under /etc/systemd/system, which takes precedence over vendor units in /usr/lib/systemd/system.
/usr/shareArchitecture-independent files are stored here, including:
Useful documentation may be available beneath:
/usr/share/doc/
/usr/local: administrator-installed software/usr/local is reserved for software installed locally by the server administrator.
Common locations include:
/usr/local/bin
/usr/local/sbin
/usr/local/lib
/usr/local/share
A custom script intended for all users might be placed at:
/usr/local/bin/deploy-project
This keeps locally maintained commands separate from files owned by the operating system’s package manager.
Avoid copying custom commands into /usr/bin. A package installation or update could create a conflict or replace them.
/run: temporary runtime stateThe /run directory holds state created since the system booted.
Common examples include:
Examples might include:
/run/nginx.pid
/run/php/php8.4-fpm.sock
/run/mysqld/mysqld.sock
/run/redis/redis-server.pid
/run/user/1000/
/run is normally stored in memory and recreated during boot. Do not store permanent application data there.
A common PHP-FPM Nginx configuration points to a socket such as:
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
If Nginx reports that the socket does not exist, check:
ls -la /run/php/
systemctl status php8.4-fpm
journalctl -u php8.4-fpm
The socket may be missing because PHP-FPM failed to start, the configured PHP version changed, or Nginx points to the wrong path.
/root: the root user’s homeThe root account does not normally use /home/root.
Its home directory is:
/root
This may contain:
/root/.ssh/
/root/.bashrc
/root/.profile
Ordinary users cannot usually access it.
Avoid keeping application code, backups or deployment processes in /root. Doing so encourages routine use of the root account and makes it harder to give developers appropriately limited access.
Use a normal deployment or project user wherever possible, then use sudo for specific administrative tasks.
These are common package locations rather than universal rules.
| Service | Ubuntu and Debian | Rocky, Alma and RHEL family |
|---|---|---|
| Nginx main configuration | /etc/nginx/nginx.conf |
/etc/nginx/nginx.conf |
| Nginx site configuration | /etc/nginx/sites-available/ |
/etc/nginx/conf.d/ |
| Nginx logs | /var/log/nginx/ |
/var/log/nginx/ |
| Apache main configuration | /etc/apache2/apache2.conf |
/etc/httpd/conf/httpd.conf |
| Apache site configuration | /etc/apache2/sites-available/ |
/etc/httpd/conf.d/ |
| Apache logs | /var/log/apache2/ |
/var/log/httpd/ |
| PHP configuration | /etc/php/<version>/ |
/etc/php.ini and /etc/php.d/ |
| PHP-FPM pools | /etc/php/<version>/fpm/pool.d/ |
/etc/php-fpm.d/ |
| MySQL configuration | /etc/mysql/ |
/etc/my.cnf and /etc/my.cnf.d/ |
| PostgreSQL configuration | /etc/postgresql/<version>/main/ |
Often stored with the database cluster |
| Redis configuration | Commonly /etc/redis/ |
Commonly /etc/redis.conf |
| SSH server configuration | /etc/ssh/sshd_config |
/etc/ssh/sshd_config |
| Local systemd units | /etc/systemd/system/ |
/etc/systemd/system/ |
Ubuntu documents Nginx site files under /etc/nginx/sites-available, Apache sites under /etc/apache2/sites-available, and MySQL settings under /etc/mysql. Red Hat packages organise Apache beneath /etc/httpd and use /etc/php-fpm.d for PHP-FPM pool configuration.
Always confirm the location on the actual server before making changes.
Documentation and tutorials are useful, but the server itself can often give you a more reliable answer.
Print the complete active configuration:
sudo nginx -T
Test configuration syntax:
sudo nginx -t
Show compilation and path information:
apachectl -V
Test configuration:
sudo apachectl configtest
Show the active command-line configuration:
php --ini
Show loaded PHP configuration values:
php -i
Remember that PHP-FPM may use a different php.ini from the command-line version.
Ask PostgreSQL for its files:
sudo -u postgres psql -tAc "SHOW config_file;"
sudo -u postgres psql -tAc "SHOW hba_file;"
sudo -u postgres psql -tAc "SHOW data_directory;"
PostgreSQL allows configuration and data to be stored separately, so querying the running server avoids assumptions.
Display the service definition and overrides:
systemctl cat nginx
Show its current status:
systemctl status nginx
The ExecStart line often reveals the configuration file or startup arguments used by the service.
On Ubuntu and Debian:
dpkg -L nginx
On Rocky Linux, AlmaLinux and RHEL:
rpm -ql nginx
These commands list files installed by the package.
Every file and directory has:
View them with:
ls -la
Example:
-rw-r----- 1 deploy www-data 1250 Aug 4 20:30 .env
This line tells us:
| Part | Meaning |
|---|---|
- |
A regular file |
rw- |
The owner can read and write |
r-- |
The group can read |
--- |
Everyone else has no access |
deploy |
File owner |
www-data |
File group |
The three basic permissions are:
| Letter | Permission | Files | Directories |
|---|---|---|---|
r |
Read | Read the contents | List the directory |
w |
Write | Change the contents | Create, rename or remove entries |
x |
Execute | Run the file | Enter or pass through the directory |
The execute permission on directories often causes confusion. A process may have permission to read a file but still be unable to reach it because one of its parent directories lacks execute permission.
Inspect every part of a path with:
namei -l /var/www/example.com/storage/logs/laravel.log
This is particularly useful for diagnosing Nginx and PHP permission errors.
Permissions are often written as three digits.
| Number | Symbolic form | Typical meaning |
|---|---|---|
600 |
rw------- |
Private file readable and writable by its owner |
640 |
rw-r----- |
Owner can write, group can read |
644 |
rw-r--r-- |
Publicly readable file |
700 |
rwx------ |
Private directory or executable |
750 |
rwxr-x--- |
Owner has full access, group can read and enter |
755 |
rwxr-xr-x |
Publicly accessible directory |
770 |
rwxrwx--- |
Owner and group have full access |
Change permissions with:
chmod 640 .env
chmod 750 storage
Change ownership with:
sudo chown deploy:www-data .env
Change ownership recursively:
sudo chown -R deploy:www-data /var/www/example.com
Recursive changes should be used carefully. An application may contain directories and files that deliberately require different permissions.
chmod 777 is usually a bad fixSetting permissions to 777 gives every local user permission to read, write and execute.
It may make an error disappear, but it also allows unrelated users and compromised processes to change those files.
For a web application, work out which process actually needs access.
Questions to ask include:
A common arrangement is:
For example:
sudo chown -R deploy:www-data /var/www/example.com
sudo find /var/www/example.com -type d -exec chmod 750 {} \;
sudo find /var/www/example.com -type f -exec chmod 640 {} \;
A public directory may require wider read access depending on the web server and group setup:
sudo chmod 755 /var/www/example.com/public
Laravel commonly needs write access to:
storage/
bootstrap/cache/
That can be granted to the account running PHP:
sudo chown -R deploy:www-data storage bootstrap/cache
sudo chmod -R 770 storage bootstrap/cache
These are examples rather than universal commands. Confirm the user and group used by your own PHP-FPM pool before applying them.
For Nginx:
ps aux | grep '[n]ginx'
For PHP-FPM:
ps aux | grep '[p]hp-fpm'
You can also inspect the pool configuration:
grep -R "^[[:space:]]*user[[:space:]]*=" /etc/php/*/fpm/pool.d/
For a systemd service:
systemctl show example.service -p User -p Group
A blank User value often means the service runs as root, although the application may later switch to another account.
A symbolic link points from one path to another.
View links with:
ls -la
Example:
current -> releases/20260804203000
Create one with:
ln -s releases/20260804203000 current
Replace an existing deployment link atomically:
ln -sfn releases/20260804210000 current
Symbolic links are frequently used for:
Check the final target with:
readlink -f current
A broken link points to a target that no longer exists:
current -> releases/missing-release
This can produce a web server error even when the link itself still appears in the directory.
Search by filename:
sudo find /etc -name "php.ini"
Search without case sensitivity:
sudo find / -iname "*nginx*" 2>/dev/null
Find files larger than 500 MB:
sudo find /var -xdev -type f -size +500M -ls
Find recently changed files:
sudo find /etc -type f -mtime -1
Find files owned by a particular user:
sudo find /var/www -user deploy
find / can be slow on a large server. Start with the most likely directory whenever possible.
Check filesystem usage:
df -h
Check inode usage:
df -i
A filesystem can have free storage but still be unable to create files if it has exhausted its inodes.
Find the largest top-level directories:
sudo du -xhd1 / | sort -h
Inspect /var:
sudo du -xhd1 /var | sort -h
Find the largest files:
sudo find /var -xdev -type f -printf '%s %p\n' \
| sort -n \
| tail -20
Common causes of unexpected disk usage include:
Find deleted files that are still open:
sudo lsof +L1
A large deleted log can continue using disk space until the service holding it open is restarted or told to reopen its logs.
When a website fails and you do not know where to start, work through the filesystem methodically.
For Nginx:
sudo nginx -T | grep -n "server_name example.com"
For Apache:
sudo apachectl -S
Look for:
root /var/www/example.com/public;
or:
DocumentRoot /var/www/example.com/public
ls -la /var/www/example.com/public
namei -l /var/www/example.com/public/index.php
ps aux | grep '[n]ginx'
ps aux | grep '[p]hp-fpm'
sudo tail -n 100 /var/log/nginx/error.log
sudo journalctl -u php8.4-fpm -n 100
sudo nginx -t
sudo php-fpm8.4 -t
The exact PHP-FPM command varies by distribution and PHP version.
df -h
df -i
systemctl status nginx
systemctl status php8.4-fpm
This process usually gives you a useful error message before you need to start changing permissions or configuration.
A tidy filesystem makes a server easier to maintain.
Use consistent locations for websites, deployments and logs. Avoid storing important files in /tmp, keeping projects under /root, or spreading configuration across undocumented directories.
For each website, record:
ServerAuth centralises many of these day-to-day server management tasks, including websites, PHP configuration, databases, cron jobs, Supervisor processes and logs. This reduces how often developers need to search through the filesystem directly, while the underlying paths remain useful when troubleshooting or handling unusual configurations.
The filesystem stops feeling arbitrary once you recognise the pattern:
/etc/var/home/srv or a documented website directory/opt/tmp or /var/tmp/usr/runYou will still encounter differences between distributions and packages. When that happens, ask the running service which configuration it loaded, inspect its systemd unit, check its logs and confirm the permissions along the complete path.
ServerAuth provides a whole host of management tools, from controlling who can access your server, to managing your website deployments. And with an ever-growing suite of tools you'll always be one step ahead!
Start for free