Sign in
← Linux: file system in depth
12 min · medium · Lesson 2 of 7

Hard and symbolic links

Hard and symbolic links, and inodes.

Why this matters

Links show up everywhere in Linux. /etc/nginx/sites-enabled on Debian and Ubuntu is built from symlinks, the current directory in a deploy points to the latest release, and snapshot scripts save space with hard links. If you don't tell the two kinds of links apart, it's easy to delete a "copy" along with the data, or end up with a link that leads nowhere.

inode and file name

An inode is the record that holds all of a file's metadata: size, permissions, owner, timestamps, and pointers to the data blocks. The file name itself isn't in the inode. The name lives in the directory: a directory stores pairs of "name → inode number".

You can see the inode number with the -i flag on ls:

ls -li notes.txt
1310745 -rw-r--r-- 1 deploy deploy 42 Jul 11 09:14 notes.txt

The first number (1310745) is the inode number. Next comes the link count column (1): how many names point to this inode. The lesson linux-admin-08-fs-inodes goes into more detail on how inodes work.

A hard link (hardlink) is another name for the same inode. The ln command with no flags creates such a name: two different paths lead to the same data.

ln notes.txt backup.txt
ls -li notes.txt backup.txt
1310745 -rw-r--r-- 2 deploy deploy 42 Jul 11 09:14 backup.txt
1310745 -rw-r--r-- 2 deploy deploy 42 Jul 11 09:14 notes.txt

The inode number is the same for both (1310745), and the link count went up to 2. Add something to the file through one name, and the change shows up through the other, because the data is shared.

The data exists as long as the link count is greater than zero. Removing one name lowers the count but doesn't touch the content:

rm notes.txt
ls -li backup.txt
1310745 -rw-r--r-- 1 deploy deploy 42 Jul 11 09:14 backup.txt

The count is back to 1, and the data is intact. It only gets freed once the last name disappears (and no process still has the file open).

A symbolic link (symlink) is a separate, small file that has the path to its target written inside it. You create one with the -s flag:

ln -s /var/log/app/current app.log
ls -li app.log
1310902 lrwxrwxrwx 1 deploy deploy 20 Jul 11 09:20 app.log -> /var/log/app/current

A symlink has its own inode (1310902, unlike the target), the type l at the start of the line, and an arrow to the path. The size 20 is the length of the path /var/log/app/current in bytes, not the size of the target. When something accesses app.log, the system resolves this path and lands on the target. readlink app.log shows where the link points, and readlink -f app.log gives the full path to the final target.

A symlink can do what a hard link can't: point to a directory and cross filesystem boundaries. A hard link only works inside one filesystem, because inode numbers are unique within their own filesystem, not globally. ln says so directly:

ln /home/deploy/notes.txt /mnt/backup/notes.txt
ln: failed to create hard link '/mnt/backup/notes.txt' => '/home/deploy/notes.txt': Invalid cross-device link
ln /home/deploy logs
ln: /home/deploy: hard link not allowed for directory

The price of that flexibility: a symlink breaks if the target is removed or moved. The path inside stays the same, but it no longer leads anywhere:

rm /var/log/app/current
cat app.log
cat: app.log: No such file or directory

The link itself is still intact, only the target is gone. ls usually highlights such a dangling link in red.

The path inside a symlink can be absolute (/var/log/app/current) or relative (builds/2026-10). The system resolves a relative path from the directory where the link itself lives, not from the directory you were in when you created it. That's why ln -s builds/2026-10 /opt/app/live works no matter where you run it from, and the link /opt/app/live points to /opt/app/builds/2026-10. If you move the whole /opt/app directory, the relative links inside it keep working, while the absolute ones break.

How they differ

PropertyHard linkSymbolic link
Commandln A Bln -s A B
What it points toan inodea path
Own inodeno, shared with the targetyes, separate
To a directorynot allowedallowed
Across filesystemsnot allowedallowed
Target removeddata survives while count > 0link breaks

Common mistakes

"A hard link and a symlink are the same thing." A hard link shares an inode with the target, a symlink stores a path and has its own inode. Easy to check with ls -li: a hard link's inode number matches the original, a symlink's doesn't.

"I deleted the file, and the hard link broke." A hard link has no concept of "original" and "copy", these are equal names for the same inode. As long as the link count is above zero, the data is intact:

ln data.bin data-hardlink.bin
rm data.bin
cat data-hardlink.bin

Here rm removes one name, and cat through the second name reads the data just like before. Only a symlink breaks from a deletion, because it holds a path, not the data itself.

Argument order. ln takes the target first, then the name of the new link, just like cp: ln -s builds/2026-10 live. Mix up the arguments, and the link shows up somewhere you don't expect: if the second argument is an existing directory, ln creates the link inside it.

What you'll need for the tasks

The next lesson covers the virtual filesystems /proc and /sys.

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson linux-fs-deep-02-links