The Linux filesystem explained
In this series (15 parts)
- What is Linux and how it differs from other OSes
- Installing Linux and setting up your environment
- The Linux filesystem explained
- Users, groups, and permissions
- Essential command line tools
- Shell scripting fundamentals
- Processes and job control
- Standard I/O, pipes, and redirection
- The Linux networking stack
- Package management and software installation
- Disk management and filesystems
- Logs and system monitoring
- SSH and remote access
- Cron jobs and task scheduling
- Linux security basics for sysadmins
The Linux filesystem is a single tree. There is no C: drive, no D: drive. Everything starts at / (root) and branches down from there. Disks, USB drives, network shares, even running processes are all mounted somewhere in this tree. Once you understand the layout, you know where to find anything on any Linux system.
Prerequisites
You should have a working Linux installation to follow along with the examples.
The Filesystem Hierarchy Standard (FHS)
The FHS defines where files go. Most Linux distributions follow it, which means the directory structure is predictable across Ubuntu, Debian, Fedora, Arch, and others.
graph TD ROOT["/"] --> BIN["/bin - Essential binaries"] ROOT --> ETC["/etc - Configuration files"] ROOT --> HOME["/home - User directories"] ROOT --> VAR["/var - Variable data"] ROOT --> TMP["/tmp - Temporary files"] ROOT --> USR["/usr - User programs"] ROOT --> PROC["/proc - Process info"] ROOT --> DEV["/dev - Device files"] ROOT --> SYS["/sys - Kernel info"] ROOT --> OPT["/opt - Optional software"] USR --> USRBIN["/usr/bin"] USR --> USRLIB["/usr/lib"] USR --> USRSHARE["/usr/share"] style ROOT fill:#f9a825,stroke:#f57f17,color:#000
Here is what each directory holds:
| Directory | Purpose | Example contents |
|---|---|---|
/bin | Essential command binaries | ls, cp, cat, bash |
/sbin | System binaries (admin tools) | fdisk, iptables, mount |
/etc | Configuration files | fstab, passwd, hosts, ssh/ |
/home | User home directories | /home/pratik, /home/deploy |
/root | Root user’s home directory | Root’s personal files |
/var | Variable data (logs, mail, spool) | /var/log/, /var/www/ |
/tmp | Temporary files (cleared on reboot) | Session files, build artifacts |
/usr | User programs and data | Most installed software |
/usr/bin | User command binaries | git, python3, vim |
/usr/lib | Libraries for /usr/bin programs | Shared libraries (.so files) |
/usr/share | Architecture-independent data | Man pages, documentation |
/opt | Optional/third-party software | Manually installed apps |
/proc | Virtual filesystem for processes | /proc/cpuinfo, /proc/1/status |
/dev | Device files | /dev/sda, /dev/null, /dev/tty |
/sys | Kernel and hardware info | /sys/class/net/, /sys/block/ |
/mnt | Temporary mount points | Manually mounted drives |
/media | Removable media auto-mount | USB drives, CD-ROMs |
/boot | Boot files | Kernel image, GRUB config |
A few important things to note:
/etc is where you configure everything. Network settings, user accounts, service configurations, SSH keys, firewall rules. When something is not working, the answer is usually in a file under /etc.
/var/log is where you debug everything. System logs, application logs, authentication logs. When something breaks, check the logs.
/proc and /sys are virtual. They do not exist on disk. The kernel generates their contents on the fly when you read them. /proc/cpuinfo does not take up disk space.
/tmp is disposable. Most systems clear it on reboot. Never store anything important there.
Everything is a file
This is the most important concept in Linux. Devices, pipes, sockets, and processes are all represented as files. This means you can use the same tools (cat, echo, read, write) on everything.
| Type | Example | What it is |
|---|---|---|
| Regular file | /etc/hostname | Text or binary data |
| Directory | /home/pratik/ | A list of file entries |
| Block device | /dev/sda | A storage device |
| Character device | /dev/tty | A serial stream (terminal) |
| Symbolic link | /usr/bin/python -> python3 | A pointer to another file |
| Named pipe (FIFO) | /tmp/mypipe | Inter-process communication |
| Socket | /var/run/docker.sock | Network or local communication |
You can see the file type with ls -l. The first character tells you:
ls -l /dev/sda /etc/hostname /usr/bin/python3
Output:
brw-rw---- 1 root disk 8, 0 Jun 15 10:00 /dev/sda
-rw-r--r-- 1 root root 7 Jun 15 10:00 /etc/hostname
-rwxr-xr-x 1 root root 5432 Jun 15 10:00 /usr/bin/python3
b= block device-= regular filel= symbolic linkd= directoryc= character device
Inodes: what files really are
Every file on a Linux filesystem has an inode (index node). The inode stores metadata about the file:
- File type and permissions
- Owner and group
- Size
- Timestamps (created, modified, accessed)
- Pointers to the actual data blocks on disk
The inode does NOT store the filename. Filenames are stored in directory entries that point to inodes. This is an important distinction because it makes hard links possible.
# View inode number for a file
ls -i /etc/hostname
Output:
1234567 /etc/hostname
# View detailed inode info
stat /etc/hostname
Output:
File: /etc/hostname
Size: 7 Blocks: 8 IO Block: 4096 regular file
Device: 802h/2050d Inode: 1234567 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2024-06-15 10:00:00.000000000 +0000
Modify: 2024-06-15 10:00:00.000000000 +0000
Change: 2024-06-15 10:00:00.000000000 +0000
Birth: 2024-06-15 10:00:00.000000000 +0000
Hard links vs soft links
Hard links
A hard link is a directory entry that points to the same inode as another file. Both names are equally valid. The file data is not duplicated.
# Create a file
echo "hello" > original.txt
# Create a hard link
ln original.txt hardlink.txt
# Both files have the same inode
ls -li original.txt hardlink.txt
Output:
9876543 -rw-r--r-- 2 pratik pratik 6 Jun 15 10:00 hardlink.txt
9876543 -rw-r--r-- 2 pratik pratik 6 Jun 15 10:00 original.txt
Notice: same inode number (9876543), link count is 2. If you delete original.txt, hardlink.txt still works because the inode and data blocks remain until the link count drops to zero.
# Delete the original
rm original.txt
# Hard link still works
cat hardlink.txt
Output:
hello
Limitations of hard links:
- Cannot link across filesystems (different partitions)
- Cannot link to directories (to prevent loops)
Soft links (symbolic links)
A symbolic link is a special file that contains a path to another file. It is like a shortcut. If the target is deleted, the symlink becomes “dangling” (broken).
# Create a file
echo "world" > target.txt
# Create a symbolic link
ln -s target.txt symlink.txt
# Inspect both
ls -li target.txt symlink.txt
Output:
1111111 -rw-r--r-- 1 pratik pratik 6 Jun 15 10:00 target.txt
2222222 lrwxrwxrwx 1 pratik pratik 10 Jun 15 10:00 symlink.txt -> target.txt
Notice: different inode numbers, the symlink shows l type and the -> arrow.
# Delete the target
rm target.txt
# Symlink is now broken
cat symlink.txt
Output:
cat: symlink.txt: No such file or directory
Soft links can cross filesystems and can link to directories. They are more flexible but break if the target moves.
Example 1: Navigate the filesystem and find things
Let’s explore a real Linux system. First, look at the top-level structure:
ls -la /
Output:
total 68
drwxr-xr-x 20 root root 4096 Jun 15 10:00 .
drwxr-xr-x 20 root root 4096 Jun 15 10:00 ..
drwxr-xr-x 2 root root 4096 Jun 15 10:00 bin
drwxr-xr-x 4 root root 4096 Jun 15 10:00 boot
drwxr-xr-x 18 root root 3860 Jun 15 10:00 dev
drwxr-xr-x 133 root root 12288 Jun 15 10:00 etc
drwxr-xr-x 3 root root 4096 Jun 15 10:00 home
drwxr-xr-x 22 root root 4096 Jun 15 10:00 lib
drwxr-xr-x 2 root root 4096 Jun 15 10:00 mnt
dr-xr-xr-x 250 root root 0 Jun 15 10:00 proc
drwx------ 5 root root 4096 Jun 15 10:00 root
drwxr-xr-x 2 root root 4096 Jun 15 10:00 tmp
drwxr-xr-x 14 root root 4096 Jun 15 10:00 usr
drwxr-xr-x 14 root root 4096 Jun 15 10:00 var
Find all configuration files that mention your hostname:
grep -r "$(hostname)" /etc/ 2>/dev/null | head -5
Output:
/etc/hostname:devbox
/etc/hosts:127.0.1.1 devbox
Find the 10 largest files under /var/log:
sudo find /var/log -type f -exec du -h {} + 2>/dev/null | sort -rh | head -10
Output:
234M /var/log/journal/abc123/system.journal
45M /var/log/syslog
12M /var/log/kern.log
8.5M /var/log/auth.log
3.2M /var/log/dpkg.log
1.1M /var/log/apt/history.log
456K /var/log/boot.log
234K /var/log/faillog
128K /var/log/alternatives.log
64K /var/log/btmp
Find all files modified in the last 24 hours under /etc:
find /etc -type f -mtime -1 2>/dev/null
Output:
/etc/resolv.conf
/etc/mtab
Example 2: Create hard and soft links and inspect them
Let’s build a practical example. Suppose you have a config file that multiple services need to read:
# Create a directory for our experiment
mkdir -p ~/link-demo && cd ~/link-demo
# Create the original config
cat > app.conf << 'EOF'
database_host=localhost
database_port=5432
log_level=info
EOF
# Create a hard link (service-a reads the same file)
ln app.conf service-a.conf
# Create a soft link (service-b gets a pointer)
ln -s app.conf service-b.conf
# Inspect everything
ls -li
Output:
total 8
5555555 -rw-r--r-- 2 pratik pratik 60 Jun 15 10:00 app.conf
5555555 -rw-r--r-- 2 pratik pratik 60 Jun 15 10:00 service-a.conf
6666666 lrwxrwxrwx 1 pratik pratik 8 Jun 15 10:00 service-b.conf -> app.conf
Now modify the original and check both links:
echo "debug_mode=true" >> app.conf
# Hard link sees the change (same inode)
cat service-a.conf
Output:
database_host=localhost
database_port=5432
log_level=info
debug_mode=true
# Soft link also sees the change (follows the path)
cat service-b.conf
Output:
database_host=localhost
database_port=5432
log_level=info
debug_mode=true
Now delete the original and see the difference:
rm app.conf
# Hard link still works
cat service-a.conf
Output:
database_host=localhost
database_port=5432
log_level=info
debug_mode=true
# Soft link is broken
cat service-b.conf
Output:
cat: service-b.conf: No such file or directory
# The broken symlink shows up in red with ls
ls -l service-b.conf
Output:
lrwxrwxrwx 1 pratik pratik 8 Jun 15 10:00 service-b.conf -> app.conf
The symlink file itself still exists, but it points to a file that no longer exists. You can detect broken symlinks with:
find . -xtype l
Output:
./service-b.conf
Clean up:
cd ~ && rm -rf ~/link-demo
What comes next
Now that you understand where files live and how they are structured, the next article covers Users, groups, and permissions. You will learn who can read, write, and execute files, and how to control access.
For a deeper look at disk-level filesystem management (partitioning, formatting, mounting), see Disk management and filesystems.