Search…
Linux from Scratch · Part 3

The Linux filesystem explained

In this series (15 parts)
  1. What is Linux and how it differs from other OSes
  2. Installing Linux and setting up your environment
  3. The Linux filesystem explained
  4. Users, groups, and permissions
  5. Essential command line tools
  6. Shell scripting fundamentals
  7. Processes and job control
  8. Standard I/O, pipes, and redirection
  9. The Linux networking stack
  10. Package management and software installation
  11. Disk management and filesystems
  12. Logs and system monitoring
  13. SSH and remote access
  14. Cron jobs and task scheduling
  15. 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:

DirectoryPurposeExample contents
/binEssential command binariesls, cp, cat, bash
/sbinSystem binaries (admin tools)fdisk, iptables, mount
/etcConfiguration filesfstab, passwd, hosts, ssh/
/homeUser home directories/home/pratik, /home/deploy
/rootRoot user’s home directoryRoot’s personal files
/varVariable data (logs, mail, spool)/var/log/, /var/www/
/tmpTemporary files (cleared on reboot)Session files, build artifacts
/usrUser programs and dataMost installed software
/usr/binUser command binariesgit, python3, vim
/usr/libLibraries for /usr/bin programsShared libraries (.so files)
/usr/shareArchitecture-independent dataMan pages, documentation
/optOptional/third-party softwareManually installed apps
/procVirtual filesystem for processes/proc/cpuinfo, /proc/1/status
/devDevice files/dev/sda, /dev/null, /dev/tty
/sysKernel and hardware info/sys/class/net/, /sys/block/
/mntTemporary mount pointsManually mounted drives
/mediaRemovable media auto-mountUSB drives, CD-ROMs
/bootBoot filesKernel 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.

TypeExampleWhat it is
Regular file/etc/hostnameText or binary data
Directory/home/pratik/A list of file entries
Block device/dev/sdaA storage device
Character device/dev/ttyA serial stream (terminal)
Symbolic link/usr/bin/python -> python3A pointer to another file
Named pipe (FIFO)/tmp/mypipeInter-process communication
Socket/var/run/docker.sockNetwork 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 file
  • l = symbolic link
  • d = directory
  • c = 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

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)

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

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.

Start typing to search across all content
navigate Enter open Esc close