linux · difficulty ◆
ln — create hard and symbolic links
A link is a name, not a copy. Learn the two flavors — and why using the wrong one breaks on a reboot.
You know cp. You know mv. But the file on disk you have "two copies" of is the same one file wearing two names — and that trick is the whole art of the Linux filesystem.
AI-generated draft · reviewed and editedThis text was generated with AI as a first draft, then reviewed and edited by the author, who holds editorial responsibility for it.$ lnWhat it does
ln creates links — extra names that point at an existing file or directory without copying its data. The default is a hard link: a second directory entry sharing the exact same inode, the on-disk record that holds everything about a file except its name — owner, permissions, timestamps, size, and the pointers to the blocks that carry the data. You can spot hard links because `ls -l` shows the link count (the number after the permissions) and `ls -i` shows the same inode number. The second flavor, `ln -s`, makes a symbolic (soft) link: a tiny pointer file that stores the target's path as text. Symlinks can cross filesystems, point at directories, and even point at things that do not exist yet — hard links are limited to files on the same filesystem.
Why it matters
Links are how you version a config without duplicating it (`ln -s conf/app.ini latest.ini`), how installed binaries appear in your PATH from a central directory (`ln -s /opt/node/bin/node ~/bin/node`), and how Docker and home-manager-style setups make multiple trees share one truth. The moment you grasp that a symlink is just a path-on-a-stick that the kernel resolves on access, a whole class of 'why did mv break it?' bugs stops happening. Hard links are the quieter superpower: the classic backup pattern of linking unchanged files costs no disk and lets you delete a name without losing the data.
Example
$ ln -s conf/app.ini latest.ini && ls -l latest.inilrwxrwxrwx 1 kmail kmail 12 Sep 18 09:01 latest.ini -> conf/app.iniSymbolic link to a file. The `->` shows the stored target path. Reading latest.ini resolves through it — the kernel follows the pointer on every access, so editing app.ini is instantly visible through the link.
$ cat latest.ini[server]
host = 127.0.0.1
port = 5432Reading through the symlink is transparent — cat has no idea it is not the real file. This is the 'name as a version' trick: one real config, any number of named views into it.
$ ln -sf conf/app.ini must_be_link && ls -l must_be_linklrwxrwxrwx 1 kmail kmail 12 Sep 18 09:01 must_be_link -> conf/app.ini-f overwrites an existing file or link in place: it removes the destination first, then creates the new link. Without -f, `ln -s` refuses if the target name already exists. The trap worth knowing is a destination that is itself a symlink to a directory — `ln -f` treats that as a directory and puts the new link inside it, unless you add -n, which says 'no, I meant the link itself'.
$ ln -s conf/vanished.ini broken && cat brokencat: broken: No such file or directoryA dangling symlink — the pointer exists but points at nothing. cat fails with 'No such file or directory', which confuses people because the link clearly exists. The file it points to is gone; the link is now a landmine.
$ find -L /tmp/lntut -type l/tmp/lntut/brokenfind -L follows symlinks while matching, so -type l surfaces every dangling link in the tree. Run this after a move or rm binge to sweep up the dead pointers before they break a backup or a service.
$ ln orig.txt hard.txt && ls -li orig.txt hard.txt3994751 -rw-rw-r-- 2 kmail kmail 12 Sep 18 09:01 hard.txt
3994751 -rw-rw-r-- 2 kmail kmail 12 Sep 18 09:01 orig.txtA hard link: same inode 3994751, link count 2, no -> arrow. These are two names for the SAME data. Editing either name changes both, and deleting one name leaves the data intact under the other.
Common flags
- -s, --symbolic
- make a symbolic (soft) link rather than a hard link — the only form that works on directories
- -f, --force
- remove any existing destination file or link before creating the new one
- -n, --no-dereference
- if the destination is a symlink to a directory, treat it as a normal file instead of descending into it
- -v, --verbose
- print the name of each link created
- -t, --target-directory
- create links in the given directory: `ln -s /a /b -t /dir`
- no flag (default)
- create a hard link that shares the source's inode — same filesystem required, and not allowed for directories
- -i, --interactive
- ask before overwriting an existing destination (good safety net atop -f)
- -r, --relative
- With -s, generate the link target relative to where the link will live: `ln -sr /a/file /tmp` writes '/tmp/file -> ../a/file' instead of the absolute path. Added in coreutils 8.16, and the reason a symlink keeps working when you move the whole tree together.
History
Origin
ln is one of the oldest commands — an ln command appeared in Version 1 AT&T Unix in 1971, so it was there at the birth of Unix at Bell Labs. Hard links come straight from the original filesystem design: an inode with multiple directory entries. Symlinks, the far more useful flavor for everyday work, are much younger — they came out of Berkeley, in 4.1a and then in 4.2BSD in 1983, which is where ln -s comes from. That split still shows: the '-s' flag is the newcomer bolted onto a fifty-year-old command.
Why symlinks won
Hard links cannot cross filesystems and cannot link directories, which makes them useless for the two jobs people actually want: aliasing a binary anywhere on the box and pointing at a tree. Symlinks are just path strings, so the kernel resolves them on every access — that is why a symlink to a directory 'works' where a hard link cannot. It also means a symlink is always relative to its own location when the path does not start with '/', which is exactly how `ln -s conf/app.ini latest.ini` stays valid even if both get moved together.
Fun facts
Pros & cons
pros
- + A symlink is a pointer, not a copy — no disk, no drift, no manual sync
- + Hard links share one inode, so deleting one name never destroys the data
- + Instant: creating a link is microseconds even for a multi-gigabyte file
cons
- − Symlinks break (dangle) when the target moves — and the error says 'No such file or directory' with no hint the link is at fault
- − Hard links cannot cross filesystems or point at directories, limiting where you can use them
- − Overwriting a symlink with -f silently retargets or replaces, and the trailing-slash ambiguity catches people off guard
Takeaways
- 1Use `ln -s` for the common case — files and directories, across any filesystem
- 2Re-point a versioned config with `ln -sf new.conf latest.conf`, never a plain cp
- 3A hard link (`ln` without -s) is a second name for the same inode — deleting one name keeps the data alive under the other
- 4After any mv or migration, `find -L . -type l` to find dangling symlinks before they bite a backup or service
- 5Reading through a symlink is transparent to every tool; writing to it edits the target, not a copy
