Links on Linux let multiple names point to the same file data. The confusion between hard links and symbolic links is common, but the distinction matters in practice: a dead symbolic link silently breaks your nginx config, while a hard link continues working even after the original file is deleted. Understanding how they work at the inode level removes the mystery entirely.

How inodes work

Every file on a Linux filesystem has an inode — a data structure that stores the file’s metadata (size, permissions, owner, timestamps, and the location of the file’s data blocks on disk). The filename is just an entry in a directory that points to an inode number. A single inode can have multiple directory entries pointing to it — those are hard links.

Filesystem
├── Inodes (metadata + data location)
│   └── Inode 4521: size=1823, perms=644, data=blocks[1,2,3]
│
└── Directories (name → inode mappings)
    ├── /etc/nginx/nginx.conf   → inode 4521   (original name)
    └── /etc/nginx/nginx.bak   → inode 4521   (hard link — same inode)
    └── /tmp/nginx-config      → points to path "/etc/nginx/nginx.conf"
                                 (symlink — different inode, stores a path)

A hard link is a second (or third, or fourth) directory entry pointing to the same inode. Both names are equal — there is no “original” and “link”. Deleting one name does not affect the data; the data is only deleted when the last hard link is removed and no process has the file open.

# Create a file and check its inode number
echo "production config" > config.conf
ls -li config.conf

Output (inode number is the first column)

394782 -rw-rw-r-- 1 irfan irfan 19 Jun 10 12:00 config.conf
↑                  ↑
inode              link count (1 = only one name)
# Create a hard link
ln config.conf config-hardlink.conf

# Now both names point to the same inode
ls -li config.conf config-hardlink.conf

Output

394782 -rw-rw-r-- 2 irfan irfan 19 Jun 10 12:00 config-hardlink.conf
394782 -rw-rw-r-- 2 irfan irfan 19 Jun 10 12:00 config.conf
↑                  ↑
same inode         link count now 2
# Delete the original — the hard link still has the data
rm config.conf
cat config-hardlink.conf    # Still works perfectly
ls -li config-hardlink.conf # Link count drops to 1

Hard link limitations:

  • Cannot link to directories (prevents filesystem loops)
  • Cannot span filesystems or disk partitions
  • Cannot hard-link to a file on a different mount point

A symbolic link (symlink) is a special file that stores a text path pointing to another file or directory. It is similar to a Windows shortcut. The symlink itself is a tiny file; it just contains a path string.

# Create a symbolic link
ln -s /etc/nginx/nginx.conf /tmp/nginx-conf-link

# Inspect the symlink
ls -la /tmp/nginx-conf-link

Output

lrwxrwxrwx 1 irfan irfan 22 Jun 10 12:00 /tmp/nginx-conf-link -> /etc/nginx/nginx.conf
↑                                                                   ↑
l = symlink                                                    points to this path
# A broken symlink: target does not exist
ln -s /nonexistent/file broken-link
ls -la broken-link          # Shows the link (in red on most terminals)
cat broken-link             # Error: no such file or directory

# Check if a symlink target exists
[ -e /tmp/nginx-conf-link ] && echo "target exists" || echo "broken link"
# Hard link (no -s flag)
ln source.txt hardlink.txt

# Symbolic link (-s flag)
ln -s /full/path/to/target linkname

# Symbolic link to a directory
ln -s /var/www/html /home/irfan/webroot

# Create a symlink, overwriting if it already exists
ln -sf /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

# Check where a symlink points
readlink /etc/nginx/sites-enabled/mysite
readlink -f /etc/nginx/sites-enabled/mysite    # Resolve full absolute path

Hard vs soft comparison

FeatureHard linkSymbolic link
Different inode?No — same inode as targetYes — new inode containing the path
Can link across filesystems?NoYes
Can link to directories?NoYes
Works after original is deleted?Yes — data persistsNo — becomes a broken link
Shown in ls outputLooks like a regular fileShows l type and -> target
Permission changes affect link?Yes (same inode)Yes (resolved to target)

Real-world uses

Symlinks in nginx (most common use in web server administration):

# The standard nginx pattern: create config in sites-available,
# then enable it by symlinking into sites-enabled
sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

# To disable the site without deleting the config:
sudo rm /etc/nginx/sites-enabled/mysite

# Verify the link structure
ls -la /etc/nginx/sites-enabled/

Symlinks for Python versions:

# Check what /usr/bin/python3 points to
ls -la /usr/bin/python3
# lrwxrwxrwx ... python3 -> python3.12

# Check what python3.12 points to
ls -la /usr/bin/python3.12

Hard links for backup efficiency (rsync --link-dest, Time Machine-style backups):

# rsync --link-dest creates hard links for unchanged files
# so consecutive backups share the same disk blocks
rsync -a --link-dest=/backup/yesterday/ /home/ /backup/today/

Conclusion

Use symbolic links when you need a pointer to a file or directory, especially across filesystems or to manage multiple versions of software — nginx sites-enabled is the everyday example. Use hard links when you want guaranteed data persistence even if one name is deleted — backup systems use them to save space without losing old file versions. The key distinction: a symlink breaks when the target disappears; a hard link never breaks because it is the file.

Need help with Ubuntu administration?

Work directly with Muhammad Irfan Aslam for Ubuntu Server, Linux, cloud, Docker, DevOps, CI/CD, or infrastructure troubleshooting support.

Work with Muhammad Irfan Aslam