Linux ln Command Deep Dive: Understanding Hard Links and Symbolic Links
Linux ln Command Deep Dive: Understanding Hard Links and Symbolic Links#
2026-05-09 12:57
Ever wondered why deleting a file sometimes doesn’t truly remove it? That’s the magic of Linux links. The ln command is one of the core tools in the Linux filesystem arsenal. Understanding it unlocks the secrets of inodes, filesystem architecture, and even how Docker image layers work.
Inodes: The Foundation of Links#
Before diving into ln, we need to understand inodes. Every file in Linux has a unique inode number that stores metadata (permissions, owner, timestamps, data block locations) but not the filename.
# View a file's inode number
ls -i myfile.txt
# Output: 131074 myfile.txt
stat myfile.txt
# Output includes: Inode: 131074
Filenames are just entries in directories pointing to inodes. A single inode can be pointed to by multiple filenames—that’s the essence of hard links.
Hard Links: Multiple Entries Sharing One Inode#
Hard links create multiple filenames pointing to the same inode—they’re completely equivalent at the filesystem level:
# Create a hard link
ln original.txt hardlink.txt
# Verify: both share the same inode
ls -li original.txt hardlink.txt
# 131074 -rw-r--r-- 2 user group 100 May 9 12:00 hardlink.txt
# 131074 -rw-r--r-- 2 user group 100 May 9 12:00 original.txt
Notice the 2 in the second column—that’s the link count, showing how many directory entries point to this inode.
Hard Link Characteristics#
- Complete Equivalence: No distinction between “original” and “link”—deleting either doesn’t affect the other
- Synchronized Updates: Modifying one file is visible through all links
- Space Efficiency: No data copying, just adding a directory entry
- Limitations: Cannot cross filesystems, cannot point to directories (prevents cycles)
# Hard link remains valid after deleting the original
rm original.txt
cat hardlink.txt # Content fully intact
# Link count decreases by 1
ls -l hardlink.txt
# -rw-r--r-- 1 user group 100 May 9 12:00 hardlink.txt
Symbolic Links: Special Files Pointing to Paths#
Symbolic links (soft links) are independent files that store the target path as a string:
# Create a symbolic link
ln -s /path/to/target.txt symlink.txt
# View link information
ls -l symlink.txt
# lrwxrwxrwx 1 user group 15 May 9 12:00 symlink.txt -> /path/to/target.txt
Notice the l at the start of permissions—this indicates a link file. Symbolic links have their own inode, and their size equals the length of the target path string.
Symbolic Link Characteristics#
- Can Cross Filesystems: It’s just storing a path string
- Can Point to Directories: Commonly used for directory aliases
- Become “Broken Links” When Target Doesn’t Exist: The link exists but can’t be accessed
- Can Link to Network Paths: NFS, SMB, etc.
# Create a directory symbolic link
ln -s /var/log/apache2 /var/www/logs
# Practical use: version switching
ln -s /usr/local/node-20/bin/node /usr/local/bin/node
# When upgrading, just change the symlink target
Under the Hood: Directory Entries and Inode Tables#
From the kernel’s perspective, ln modifies directory entries:
// Hard link: Create new entry in directory pointing to existing inode
// Simplified kernel logic
int sys_link(const char *oldpath, const char *newpath) {
// 1. Find the original file's inode
inode_t *inode = namei(oldpath);
if (!inode) return -ENOENT;
// 2. Create new directory entry in target directory
dir_entry_t entry;
entry.inode = inode->ino;
strcpy(entry.name, basename(newpath));
// 3. Increment link count
inode->i_links++;
return 0;
}
Symbolic links create a new inode and data block:
// Symbolic link: Create new inode, data block stores target path
int sys_symlink(const char *target, const char *linkpath) {
// 1. Allocate new inode
inode_t *inode = ialloc();
inode->i_mode = S_IFLNK | 0777;
// 2. Write target path to data block
write_data(inode, target, strlen(target));
// 3. Create directory entry
add_dir_entry(linkpath, inode);
return 0;
}
Practical Scenarios#
1. Version Switching#
# Multiple versions coexisting
ls -l /usr/local/
# drwxr-xr-x node-18
# drwxr-xr-x node-20
# lrwxrwxrwx node -> node-20
# One-command switch
rm /usr/local/node
ln -s /usr/local/node-18 /usr/local/node
2. Disk Space Expansion#
# Migrate logs to new disk
mv /var/log/app /data/logs/app
ln -s /data/logs/app /var/log/app
# Application is unaware, path unchanged
3. Incremental Backups#
# Hard links for incremental backups
cp -al backup-2026-05-08/ backup-2026-05-09/
# -a preserves attributes, -l creates hard links instead of copying
# rsync updates only changed files
rsync -a --delete /data/ backup-2026-05-09/
# Unchanged files share inodes, zero extra space
4. Docker Image Layer Mechanism#
Docker’s layered storage is built on hard links:
# Docker layers stored in /var/lib/docker/overlay2/
# Multiple images share identical base layer files (hard links)
ls -li /var/lib/docker/overlay2/*/diff/usr/bin/python3
# python3 in multiple image layers is the same inode
Common Pitfalls#
Pitfall 1: Relative Paths in Symbolic Links#
# Wrong: relative path based on current directory, not link location
ln -s ../config.json /app/config/link.json
# Correct: use absolute paths or paths relative to the link
ln -s /app/config.json /app/link/link.json
Pitfall 2: Deleting a Symbolic Link’s Target Directory#
ln -s /tmp/myapp /var/myapp
rm -rf /tmp/myapp
# /var/myapp becomes a broken link
ls -l /var/myapp
# lrwxrwxrwx 1 user group 9 May 9 12:00 /var/myapp -> /tmp/myapp
# Link exists but target is gone
Pitfall 3: Hard Link Count Misinterpretation#
# Directory link count includes . and ..
mkdir mydir
ls -ld mydir
# drwxr-xr-x 2 user group 4096 May 9 12:00 mydir
# ^ Link count is 2: mydir + mydir/.
mkdir mydir/subdir
ls -ld mydir
# drwxr-xr-x 3 user group 4096 May 9 12:00 mydir
# ^ Link count is 3: mydir + mydir/. + mydir/subdir/..
ln Command Parameters Reference#
| Flag | Purpose | Example |
|---|---|---|
-s |
Create symbolic link | ln -s target link |
-f |
Force overwrite existing link | ln -sf target link |
-n |
Don’t dereference target (link to link itself) | ln -sn link1 link2 |
-v |
Show operation details | ln -sv target link |
-b |
Backup overwritten files | ln -sb target link |
Web Implementation: Simulating Links in the Browser#
While browsers can’t directly create filesystem links, we can model the concept in JavaScript:
// Simulated filesystem
interface FileSystem {
inodes: Map<number, Inode>;
directories: Map<string, DirEntry[]>;
}
interface Inode {
id: number;
type: 'file' | 'directory' | 'symlink';
data?: string;
linkCount: number;
target?: string; // Symbolic link target path
}
// Create hard link
function createHardLink(fs: FileSystem, source: string, target: string) {
const sourceEntry = findDirEntry(fs, source);
if (!sourceEntry) throw new Error('Source not found');
// Point to the same inode
const targetDir = dirname(target);
const targetName = basename(target);
fs.directories.get(targetDir)?.push({
name: targetName,
inodeId: sourceEntry.inodeId
});
// Increment link count
fs.inodes.get(sourceEntry.inodeId)!.linkCount++;
}
// Create symbolic link
function createSymlink(fs: FileSystem, target: string, link: string) {
// Create new inode
const inodeId = fs.inodes.size + 1;
fs.inodes.set(inodeId, {
id: inodeId,
type: 'symlink',
target: target,
linkCount: 1
});
// Add directory entry
const linkDir = dirname(link);
const linkName = basename(link);
fs.directories.get(linkDir)?.push({
name: linkName,
inodeId: inodeId
});
}
Performance Considerations#
| Operation | Time Complexity | Notes |
|---|---|---|
| Create hard link | O(1) | Only adds directory entry, increments counter |
| Create symbolic link | O(1) | Creates new inode, writes path |
| Delete hard link | O(1) | Decrements counter, may free inode |
| Access symbolic link | O(n) | n is link depth, follows chain |
Summary#
The core value of the ln command:
- Space Efficiency: Hard links share data at zero space cost
- Flexibility: Symbolic links cross filesystems and link to directories
- Version Management: Switch versions via symlinks without changing environment variables
- Backup Optimization: Hard links enable incremental backups
Mastering ln isn’t just about learning a tool—it’s your gateway to understanding Linux filesystem architecture. Concepts like inodes, directory entries, and link counts appear everywhere from databases to distributed storage to container technologies.
Related Tools:
- Linux diff Command - Compare file differences
- Linux find Command - Advanced file search
- Linux chmod Calculator - Visual permission bits calculator