Skip to content
elephantoo

Hard links & symbolic links

Lesson 10 of 31 10 min read

Inodes, ln and ln -s, when to use each, and how broken links happen.


Sometimes you want one file to appear in two places: a config file kept in a Git repo but used from ~/.bashrc, a current directory that points to the latest release, or a short command name for a long program path. Linux has two tools for this: symbolic links (symlinks) and hard links. To understand the difference you first need to know about inodes.

Inodes: what a filename really is#

On a Linux filesystem, a file's data and metadata (size, owner, permissions, timestamps, where the blocks are) live in an inode, identified by a number. A filename is just a directory entry that maps a name to an inode number. ls -i shows the inode:

Terminal
echo "hello" > original.txt
ls -li original.txt
Output
1838190 -rw-r--r-- 1 ada ada 6 Oct  1 10:55 original.txt

The first column is the inode number (yours will differ). The 1 after the permissions is the link count: how many names point to this inode.

ln TARGET NEWNAME creates a hard link, a second directory entry for the same inode:

Terminal
ln original.txt hard.txt
ls -li original.txt hard.txt
Output
1838190 -rw-r--r-- 2 ada ada 6 Oct  1 10:55 hard.txt
1838190 -rw-r--r-- 2 ada ada 6 Oct  1 10:55 original.txt

Both names share the same inode number and the link count is now 2. Neither one is "the original": they are equal names for one file. Change it through either name and both see the change:

Terminal
echo "more" >> hard.txt
cat original.txt
rm original.txt
cat hard.txt
ls -li hard.txt
Output
hello
more
hello
more
1838190 -rw-r--r-- 1 ada ada 11 Oct  1 10:55 hard.txt

Removing a name only decrements the link count. The data is freed when the count reaches zero (and no process still has the file open). That's why the command for deleting is called rm ("remove a name") and the system call is unlink.

Limits of hard links:

  • They can't cross filesystems (inode numbers are only unique within one filesystem).
  • You can't hard-link directories (it would create loops in the tree).

ln -s TARGET LINKNAME creates a symlink, a tiny special file that contains a path:

Terminal
echo "settings" > config-v1.txt
ln -s config-v1.txt config.txt
ls -l config.txt
cat config.txt
Output
lrwxrwxrwx 1 ada ada 13 Oct  1 10:55 config.txt -> config-v1.txt
settings

The l file type and the -> arrow mark the symlink. Reading config.txt follows the path to config-v1.txt. Unlike hard links, symlinks:

  • can point to directories and across filesystems (even to other disks or network mounts);
  • have their own inode;
  • break if the target is moved or deleted.
Terminal
mv config-v1.txt config-old.txt
cat config.txt
ls -l config.txt
Output
cat: config.txt: No such file or directory
lrwxrwxrwx 1 ada ada 13 Oct  1 10:55 config.txt -> config-v1.txt

That's a broken (dangling) link. Many terminals show these in red. Put the target back (or repoint the link) and it works again.

Relative vs absolute targets

A relative target is resolved relative to the directory containing the link, not relative to where you ran ln:

Terminal
mkdir -p project/docs
echo "# Readme" > project/docs/README.md
ln -s docs/README.md project/README.md     # target is relative to project/
cat project/README.md
readlink project/README.md
readlink -f project/README.md
Output
# Readme
docs/README.md
/home/ada/project/docs/README.md

readlink prints the stored target. readlink -f resolves it to a full absolute path (realpath does the same). Relative links keep working if you move the whole project folder; absolute links (ln -s /home/ada/project/docs/README.md ...) keep working if you move the link itself.

Terminal
mkdir -p releases/app-1.0 releases/app-1.1
ln -s releases/app-1.0 current
ln -sfn releases/app-1.1 current      # repoint it
ls -l current
Output
lrwxrwxrwx 1 ada ada 16 Oct  1 10:55 current -> releases/app-1.1
  • -f replaces an existing link.
  • -n treats an existing link to a directory as a file, so ln doesn't create a new link inside the old target directory. Always use -sfn when repointing directory links.

Deployment tools use exactly this pattern: upload a new release, flip the current symlink in one step, and roll back by pointing it at the old release.

Terminal
ls -l /bin /usr/bin/vi 2>/dev/null | head -3
Output
lrwxrwxrwx 1 root root  7 Jul 17 22:35 /bin -> usr/bin
lrwxrwxrwx 1 root root 20 May 23  2025 /usr/bin/vi -> /etc/alternatives/vi

On modern Ubuntu, Debian and Fedora, /bin is a symlink to /usr/bin (the "usr merge"), and commands such as vi, python3, editor and java are often symlinks managed by the alternatives system (update-alternatives on Debian/Ubuntu, alternatives on Fedora). Other common places:

  • /etc/nginx/sites-enabled/ holds symlinks to configs in sites-available/ (Debian/Ubuntu Nginx).
  • /etc/systemd/system/*.wants/ holds symlinks created by systemctl enable.
  • Dotfiles: keep ~/.bashrc and friends in a Git repo and symlink them into your home directory (ln -s ~/dotfiles/bashrc ~/.bashrc).
Hard linkSymbolic link
Created withln target nameln -s target name
Points tothe inode (data)a path (name)
Directories❌✅
Across filesystems❌✅
If target deleteddata surviveslink breaks
Visible in ls -llink count > 1l type and ->

In everyday use you'll create symlinks 95% of the time. Hard links show up in backup tools (rsync --link-dest and Time Machine-style snapshots use them to store unchanged files only once) and package managers.

Terminal
echo "symlinks:";   find . -type l              # all symlinks under the current directory
echo "broken:";     find . -xtype l             # broken symlinks only (GNU find)
echo "same inode:"; find . -samefile hard.txt   # every hard link to that inode
Output
symlinks:
./project/README.md
./current
./config.txt
broken:
./config.txt
same inode:
./hard.txt

Common mistakes#

  • Swapping the arguments. It's ln -s TARGET LINKNAME, the same order as cp SOURCE DEST. If you're unsure, run ls -l afterwards.
  • Relative targets from the wrong directory. ln -s docs/README.md project/README.md points to project/docs/README.md, not ./docs/README.md.
  • rm -r link/ with a trailing slash. On a symlink to a directory, the slash makes rm follow the link: GNU rm deletes the target's contents and then reports cannot remove 'link/': Not a directory. To remove just the link, use rm link or unlink link without a slash.
  • Editing through a symlink and expecting a copy. The link has no data of its own, so any edit changes the target.
  • Using hard links for directories or across disks. It's not allowed; use a symlink.

What's next#

You've finished the Beginner section! Next you'll level up with the command-line power tools, starting with finding files by name, size, age and more.

Check your understanding

Quick quiz

0/3 answered
  1. 1.You delete the original file that a symbolic link points to. What happens to the link?

  2. 2.Which statement about hard links is true?

  3. 3.What does ln -s /opt/app-2.4 /opt/app create?

Finished reading?

Mark this lesson complete to track your progress.