ZetCode

Linux ln Command

last modified October 23, 2026

The ln command in Linux creates links between files. There are two kinds of links. A hard link is an additional name for the same file data; a symbolic link (symlink, soft link) is a small special file that contains a path to another file or directory.

On Linux file systems, file data and metadata are stored in an inode, and a directory entry maps a name to an inode number. Hard links are several directory entries pointing to one inode. Symbolic links point to a path, which is resolved each time the link is used. The syntax is ln [options] target link_name; the target comes first, as with cp.

Creating a hard link

Without options, ln creates a hard link.

ln data.txt data-hard.txt

The -i option of ls prints the inode number in the first column. Both names share the same inode, and the link count in the third column is 2. Neither name is the original; they are equal.

$ ln data.txt data-hard.txt
$ ls -li data.txt data-hard.txt
2626980 -rw-r--r-- 2 jano jano 17 Oct 23 09:00 data-hard.txt
2626980 -rw-r--r-- 2 jano jano 17 Oct 23 09:00 data.txt

Hard links share data

Since both names refer to the same inode, a change made through one name is visible through the other.

echo "appended line" >> data-hard.txt
rm data.txt

The appended line appears in data.txt. Removing data.txt only deletes one name and decrements the link count; the data stays available through data-hard.txt. The file content is freed when the last link is removed and no process has the file open.

$ echo "appended line" >> data-hard.txt
$ cat data.txt
original content
appended line
$ rm data.txt
$ cat data-hard.txt
original content
appended line
$ ls -li data-hard.txt
2626980 -rw-r--r-- 1 jano jano 31 Oct 23 11:46 data-hard.txt

Creating a symbolic link

The -s option creates a symbolic link.

ln -s data-hard.txt data-link.txt

The listing starts with l and shows the target after the arrow. The size of the link is the length of the stored path. The permissions of a symlink are always shown as rwxrwxrwx and are not used; access is governed by the permissions of the target. Reading the link reads the target.

$ ln -s data-hard.txt data-link.txt
$ ls -l data-link.txt
lrwxrwxrwx 1 jano jano 13 Oct 23 11:46 data-link.txt -> data-hard.txt
$ cat data-link.txt
original content
appended line

Linking to a directory

Symbolic links can point to directories, which is a common way to create shortcuts to deeply nested paths.

ln -s projects/app/config conf

The link conf behaves like the directory itself. Note that ls -l conf shows the link, while ls conf/ with a trailing slash lists the contents of the target directory.

$ ln -s projects/app/config conf
$ ls -l conf
lrwxrwxrwx 1 jano jano 19 Oct 23 11:46 conf -> projects/app/config
$ ls conf/
app.conf
$ cat conf/app.conf
port=8080

Dangling links

A symbolic link stores only a path. If the target is moved or deleted, the link is left dangling.

mv data-hard.txt moved.txt
find . -xtype l

After the target is renamed, the link still exists but points nowhere, so reading it fails. The -xtype l test of find lists broken symbolic links. A hard link would not break, because it refers to the inode, not to the name.

$ mv data-hard.txt moved.txt
$ ls -l data-link.txt
lrwxrwxrwx 1 jano jano 13 Oct 23 11:46 data-link.txt -> data-hard.txt
$ cat data-link.txt
cat: data-link.txt: No such file or directory
$ find . -xtype l
./data-link.txt

Relative symbolic links

A relative path in a symlink is resolved from the directory containing the link, not from the current directory.

ln -s projects/app/config/app.conf bin/broken.conf
ln -sr projects/app/config/app.conf bin/app.conf

The first link is created inside bin, so its target is looked up as bin/projects/..., which does not exist. The -r option computes the correct relative path, ../projects/..., for us. Relative links keep working when the whole tree is moved or copied elsewhere; absolute links do not depend on the location of the link.

$ mkdir -p bin
$ ln -s projects/app/config/app.conf bin/broken.conf
$ cat bin/broken.conf
cat: bin/broken.conf: No such file or directory
$ ln -sr projects/app/config/app.conf bin/app.conf
$ ls -l bin
total 0
lrwxrwxrwx 1 jano jano 31 Oct 23 11:46 app.conf -> ../projects/app/config/app.conf
lrwxrwxrwx 1 jano jano 28 Oct 23 11:46 broken.conf -> projects/app/config/app.conf
$ cat bin/app.conf
port=8080

Reading link targets

The readlink command prints the target stored in a link.

readlink bin/app.conf
readlink -f bin/app.conf
realpath conf

Plain readlink prints the path as stored. With -f, it follows all links and prints the canonical absolute path; realpath does the same.

$ readlink bin/app.conf
../projects/app/config/app.conf
$ readlink -f bin/app.conf
/home/jano/work/projects/app/config/app.conf
$ realpath conf
/home/jano/work/projects/app/config

Replacing a directory link

Switching a link such as current from one release directory to another is a common deployment technique. It needs the -f and -n options.

ln -s releases/v1 current
ln -s releases/v2 current
ln -sfn releases/v2 current

The second command does not replace the link. Since current resolves to a directory, ln creates a new link named v2 inside releases/v1. The -f option removes an existing destination, and -n treats a symlink to a directory as a normal file, so the link itself is replaced. The replacement removes the old link before creating the new one; for a switch without even a brief gap, create a temporary link and rename it with mv -T current.tmp current.

$ mkdir -p releases/v1 releases/v2
$ ln -s releases/v1 current
$ ln -s releases/v2 current
$ ls releases/v1
v2
$ rm releases/v1/v2
$ ln -sfn releases/v2 current
$ ls -l current
lrwxrwxrwx 1 jano jano 11 Oct 23 11:46 current -> releases/v2

Linking multiple files

With several targets, the last argument must be a directory, in which links with the same names are created.

ln -sv /etc/hostname /etc/os-release links/

The -v option prints each created link.

$ mkdir links
$ ln -sv /etc/hostname /etc/os-release links/
'links/hostname' -> '/etc/hostname'
'links/os-release' -> '/etc/os-release'

Hard link limitations

Hard links have two restrictions: they cannot point to directories, and they cannot cross file system boundaries.

ln projects hardlink-dir
ln moved.txt /dev/shm/moved.txt

Inode numbers are unique only within one file system, so a hard link to another file system is impossible; /dev/shm is a separate in-memory file system, as df shows. Symbolic links have neither limitation, which is why they are used much more often.

$ ln projects hardlink-dir
ln: projects: hard link not allowed for directory
$ df --output=target . /dev/shm | tail -n +2
/
/dev/shm
$ ln moved.txt /dev/shm/moved.txt
ln: failed to create hard link '/dev/shm/moved.txt' => 'moved.txt': Invalid cross-device link

Best practices for ln

Source

GNU ln manual

In this article, we have used the ln command to create hard and symbolic links, worked with relative and dangling links, and replaced links to directories.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Linux tutorials.