The Filesystem Hierarchy Standard
Filesystem Hierarchy Standard (FHS)
Linux organizes files in a single rooted tree, unlike Windows drive letters. Every file and directory starts from / (root).
Key Directories
/ # Root of the entire filesystem
├── bin/ # Essential user binaries (ls, cp, mv)
├── sbin/ # System binaries (fdisk, iptables)
├── etc/ # System configuration files
│ ├── ssh/ # SSH server config
│ ├── nginx/ # Web server config
│ └── fstab # Filesystem mount table
├── var/ # Variable data: logs, databases, mail
│ ├── log/ # System and application logs
│ └── lib/ # Package manager data
├── tmp/ # Temporary files (cleared on reboot)
├── home/ # User home directories
│ └── ubuntu/
├── root/ # Root user's home directory
├── usr/ # User-space programs and libraries
│ ├── bin/ # Non-essential user binaries
│ ├── lib/ # Libraries
│ └── share/ # Architecture-independent data
├── opt/ # Optional/third-party software
├── proc/ # Virtual filesystem for process/kernel info
├── sys/ # Virtual filesystem for hardware info
└── dev/ # Device files (sda, tty, null)
Practical Examples
# View the directory structure tree
ls -la /
# Check disk usage of major directories
du -sh /var /tmp /home /usr
# See what's in /proc (kernel and process info)
ls /proc/
cat /proc/cpuinfo
cat /proc/meminfo
# View mount points
df -h
mount | head -20
Why This Matters
When debugging a production server, knowing that logs live in /var/log, configs in /etc, and temp files in /tmp lets you navigate quickly. Misplacing files (e.g., logs in /home) causes confusion for the entire team.
File Types and the ls Command
File Types and the ls Command
Every file in Linux has a type. The ls -la output reveals it in the first character of the permissions string.
File Type Indicators
| Character | Type | Example |
|---|---|---|
- |
Regular file | Text, binary, image |
d |
Directory | /etc/, /home/ |
l |
Symbolic link | /usr/bin/python3 -> python3.11 |
c |
Character device | /dev/tty, /dev/null |
b |
Block device | /dev/sda |
p |
Named pipe (FIFO) | Created with mkfifo |
s |
Socket | /var/run/mysqld/mysqld.sock |
Reading ls -la Output
$ ls -la /etc/nginx/
drwxr-xr-x 5 root root 4096 Mar 10 14:23 .
-rw-r--r-- 1 root root 1077 Feb 18 09:45 nginx.conf
drwxr-xr-x 2 root root 4096 Mar 10 14:23 sites-enabled/
Breakdown: type(1) + permissions(9) + links + owner + group + size + date + name
The file Command
file inspects actual content, not just extensions:
$ file mystery_file
mystery_file: ELF 64-bit LSB executable, x86-64
$ file /usr/bin/python3
/usr/bin/python3: symbolic link to python3.11
$ file /dev/sda
/dev/sda: block special (8,0)
Glob Patterns (Wildcards)
# Match all .conf files in /etc
ls /etc/*.conf
# Match files starting with 'log'
ls /var/log/log*
# Match any single character
ls /etc/init.d/rs?
# Match any number of characters
ls /etc/ssh/sshd_config*
# Brace expansion
mkdir -p /tmp/{dev,staging,prod}
ls /tmp/{dev,staging,prod}
Practical Examples
# Find all symbolic links in /usr/bin
find /usr/bin -type l -ls 2>/dev/null | head -20
# List only directories
ls -d /etc/*/ | head -10
# Sort by modification time (newest first)
ls -lt /var/log/ | head -10
# Show file sizes in human-readable format
ls -lhS /var/log/
File Operations and Navigation
File Operations and Navigation
Master the essential commands for creating, copying, moving, and linking files.
Creating Files and Directories
# Create empty files
touch /tmp/newfile.txt
# Create directories (parent as needed)
mkdir -p /tmp/project/{src,bin,config}
# Create file with content
echo 'Hello World' > /tmp/greeting.txt
# Create multiple files
touch file{1..10}.txt
Copying and Moving
# Copy file (preserves permissions)
cp source.txt dest.txt
# Copy with verbose output
cp -v /etc/nginx/nginx.conf /tmp/nginx.conf.bak
# Copy directory recursively
cp -r /etc/nginx/ /tmp/nginx-backup/
# Preserve permissions, ownership, timestamps
cp -a /var/www/html/ /backup/html/
# Move/rename file
mv oldname.txt newname.txt
mv /tmp/file.txt /var/tmp/file.txt
# Move multiple files to a directory
mv *.log /var/log/archive/
Removing Files
# Remove file
rm file.txt
# Remove directory and contents
rm -rf /tmp/old-project/
# Remove empty directory
rmdir /tmp/empty-dir/
# Safe interactive removal
rm -i *.tmp
Symbolic Links vs Hard Links
# Symbolic (soft) link - points to path
ln -s /etc/nginx/nginx.conf /tmp/nginx-link
ls -la /tmp/nginx-link # Shows -> /etc/nginx/nginx.conf
# Hard link - points to inode (same data)
ln /etc/hostname /tmp/hostname-hard
ls -li /etc/hostname /tmp/hostname-hard # Same inode number
# Key difference: soft link breaks if original moves; hard link doesn't
# Soft links can cross filesystems; hard links cannot
Navigating with cd
# Go to home directory
cd ~ # or just cd
# Go to previous directory
cd -
# Go up one level
cd ..
# Go to root
cd /
# Go to another user's home
cd ~otheruser
Practical Exercise
# Create this structure and navigate it:
mkdir -p ~/project/{src/lib,tests,config/{dev,prod}}
touch ~/project/src/app.js ~/project/tests/test.js ~/project/config/dev/db.json
# Navigate to tests directory
cd ~/project/tests
# List files in config/prod from here
ls ../config/prod/
# Copy app.js to config as template
cp ../src/app.js ../config/dev/app.template.js
# Create a symlink to the config
cd ~/project
ln -s config/dev/dev-config.json ./dev-config.json
ls -la dev-config.json
Finding Files with find and locate
Finding Files with find and locate
The find Command
find recursively searches directories in real-time. It's powerful but can be slow on large filesystems.
# Find files by name
find / -name 'nginx.conf' 2>/dev/null
# Case-insensitive search
find /etc -iname '*.CONF'
# Find by type
find /var -type f -name '*.log' # Files only
find /home -type d -name '.git' # Directories only
find /dev -type b # Block devices
# Find by size
find / -size +100M # Larger than 100MB
find /tmp -size -1k # Smaller than 1KB
# Find by modification time
find /var/log -mtime -1 # Modified in last 24 hours
find /home -mmin -30 # Modified in last 30 minutes
# Find by permissions
find / -perm 777 -type f # World-writable files (security!)
find / -perm -4000 -type f # SUID binaries
# Find and execute commands on results
find /tmp -name '*.log' -exec rm {} \;
find . -name '*.py' -exec grep -l 'import requests' {} \;
# Find and delete
find /var/tmp -type f -mtime +7 -delete
The locate Command
locate uses a pre-built database (/var/lib/mlocate/) for instant results. Update it with updatedb.
# Search instantly
locate nginx.conf
# Limit results
locate -l 10 '*.py'
# Update the database
sudo updatedb
# Case-insensitive
locate -i 'README'
Practical Examples
# Find all processes holding open files in /var/log
lsof +D /var/log 2>/dev/null | head -20
# Find files larger than 50MB and list them sorted
find / -type f -size +50M -exec ls -lh {} \; 2>/dev/null | sort -k5 -h
# Find all empty files and directories
find /tmp -empty
# Find files modified today and archive them
tar czf /backup/today.tar.gz $(find /var/log -type f -mtime -1)
# Security audit: find world-writable files outside /tmp
find / -path /proc -prune -o -path /sys -prune -o -perm -0002 -type f -print 2>/dev/null
Which and Type
# Find command location
which python3
# /usr/bin/python3
# Determine command type
type ls
# ls is aliased to 'ls --color=auto'
# Where is a binary on the PATH?
whereis nginx
# nginx: /usr/sbin/nginx /etc/nginx /usr/share/nginx /usr/share/man/man8/nginx.8.gz
Mounting Filesystems and /etc/fstab
Mounting Filesystems and /etc/fstab
Linux treats all filesystems as a single directory tree. You "attach" (mount) storage devices to mount points.
Basic Mount Commands
# View current mounts
mount | grep -E '^/dev'
df -hT # Shows filesystem type and usage
# Mount a device
sudo mount /dev/sdb1 /mnt/data
# Mount with options
sudo mount -t ext4 -o rw,noatime /dev/sdb1 /mnt/data
# Unmount
sudo umount /mnt/data
sudo umount -l /mnt/data # Lazy unmount (detaches now, cleans up later)
# Remount as read-only
sudo mount -o remount,ro /
The /etc/fstab File
/etc/fstab defines which filesystems mount automatically at boot:
# <device> <mount> <type> <options> <dump> <fsck>
UUID=1234abcd-5678-efgh / ext4 defaults,noatime 0 1
UUID=abcd-1234 /boot ext4 defaults 0 2
/dev/mapper/vg0-swap none swap sw 0 0
tmpfs /tmp tmpfs defaults,noexec,nosuid 0 0
/dev/sdb1 /mnt/data ext4 defaults,nofail 0 2
//nas/share /mnt/nas cifs credentials=/etc/nascred,uid=1000 0 0
UUID vs Device Names
Device names (/dev/sdb1) can change between boots. UUIDs are stable:
# Find UUID of a partition
sudo blkid /dev/sdb1
# /dev/sdb1: UUID="1234abcd-5678-efgh" TYPE="ext4"
# Use UUID in fstab (copy-paste from blkid output)
Adding a New Disk
# Identify the disk
lsblk
sudo fdisk -l
# Partition the disk
sudo fdisk /dev/sdb
# n (new partition) -> p (primary) -> 1 -> default -> +50G -> w (write)
# Format the partition
sudo mkfs.ext4 /dev/sdb1
# Create mount point and mount
sudo mkdir -p /mnt/data
sudo mount /dev/sdb1 /mnt/data
# Add to fstab for persistence
echo 'UUID=$(sudo blkid -s UUID -o value /dev/sdb1) /mnt/data ext4 defaults 0 2' | sudo tee -a /etc/fstab
# Verify
sudo mount -a
mount | grep /mnt/data
Practical Tips
# Find which process is using a mounted filesystem (before unmount)
sudo lsof +f -- /mnt/data
sudo fuser -mv /mnt/data
# Check fstab without rebooting
sudo findmnt --verify
# View block devices with more detail
lsblk -f
Best Practices and Production Tips
Best Practices and Production Tips
Directory Organization Conventions
# Follow the FHS for your applications
/opt/myapp/ # Application binaries
/etc/myapp/ # Configuration files
/var/log/myapp/ # Log files
/var/lib/myapp/ # Persistent data
/tmp/myapp/ # Temporary runtime data
# Use consistent naming
mkdir -p /var/log/myapp/{access,error,audit}
mkdir -p /etc/myapp/{ssl,conf.d,env.d}
Security Checklist
# 1. Find and fix world-writable files
find / -path /proc -prune -o -path /sys -prune -o -perm -0002 -type f -print
# 2. Find files with no owner
find / -path /proc -prune -o -path /sys -prune -o -nouser -print
# 3. Check SUID/SGID binaries (limit these)
find /usr/bin /usr/sbin -perm /6000 -type f -ls
# 4. Protect sensitive config files
chmod 600 /etc/ssh/sshd_config
chmod 600 /etc/myapp/secrets.env
chown root:root /etc/myapp/secrets.env
# 5. Set proper permissions on web directories
chown -R www-data:www-data /var/www/html
chmod -R 750 /var/www/html
Disk Usage Monitoring Script
#!/bin/bash
THRESHOLD=80
df -h | awk 'NR>1 {print $5, $6}' | while read usage mount; do
pct=${usage%?}
if [ "$pct" -ge "$THRESHOLD" ]; then
echo "WARNING: $mount is ${usage} full"
fi
done
# Run via cron:
# 0 */6 * * * /usr/local/bin/disk_check.sh | mail -s 'Disk Alert' admin@example.com
Quick Reference Cheat Sheet
# Navigation
pwd # Print working directory
cd - # Previous directory
pushd /tmp && popd # Directory stack
# File info
stat file.txt # Detailed file metadata
file file.txt # Determine file type
wc -l file.txt # Count lines
diff file1 file2 # Compare files
# Disk usage
du -sh /var/* # Size of each dir
df -h # Filesystem usage
ncdu / # Interactive disk usage tool
# Searching
grep -r 'pattern' /etc/ # Recursive grep
rg 'pattern' /etc/ # ripgrep (faster)
fd 'pattern' /etc/ # fd (user-friendly find)
Common Mistakes to Avoid
- Never run
rm -rf /— this destroys the entire filesystem - Always quote glob expansions in scripts:
rm -- "$file"instead ofrm $file - Use
/tmpfor temporary files, not random locations - Set
noexecon/tmpto prevent executing uploaded binaries - Prefer UUIDs in fstab over device names which can change
- Use
findwith-print0andxargs -0to handle filenames with spaces