Guides

Symbolic links in Linux: create a symlink with ln -s, and fix or remove one

A Linux symlink points to another path. Create a symbolic link with ln -s, repoint it with -sfn, find broken links and remove one without losing data.

Published ~6 min read
On this page

Short answer: a symbolic link (symlink) is a small file that points to another path. Create one with ln -s TARGET LINK_NAME: the existing file or directory comes first, the new link second, the same order as cp. ls -l shows where a link points, and rm LINK_NAME removes the link without touching the target.

any Linux
ln -s /srv/app/releases/v2 /srv/app/current   # link to a directory
ln -s ../shared/config.yml config.yml           # relative link to a file
ln -sfn releases/v3 current                     # repoint an existing directory link
ls -l current                                   # current -> releases/v3
rm current                                      # remove the link, not the target

The outputs on this page come from Debian 12 with GNU coreutils 9.1 and findutils 4.9.0, run in a scratch directory on 21 October 2026; absolute paths in the outputs are shortened to /srv/app and /etc/nginx. The Windows part follows Microsoft’s mklink reference and was not run. Ubuntu, Rocky Linux and AlmaLinux ship the same GNU ln.

What is a symbolic link?

A symbolic link is a file whose only content is a path. When a program opens the link, the kernel follows that path and opens the target instead, so editors, web servers and scripts work with the link as if it were the real file or directory. In ls -l it starts with l and shows its target after an arrow:

Example output
$ ln -sv releases/v2 app/current
'app/current' -> 'releases/v2'
$ ls -l app/current
lrwxrwxrwx 1 root root 11 Oct 21 19:45 app/current -> releases/v2

The size, 11, is the length of the text releases/v2: that string is all the link stores. It is close to a Windows shortcut, with one difference that matters: a shortcut is a file that only the Windows shell interprets, while a symlink is resolved by the kernel for every program.

How do I create a symlink in Linux?

ln -s takes the target first and the name of the new link second. If you give only a target, or name an existing directory as the second argument, the link is created there under the target’s own name:

any Linux
ln -s /etc/hostname                    # creates ./hostname -> /etc/hostname
ln -s /opt/tool/bin/tool /usr/local/bin/ # creates /usr/local/bin/tool
ln -s -t /usr/local/bin /opt/tool/bin/*  # one link per file, target directory first

The target does not have to exist. ln creates the link anyway and does not warn, which is useful for a link you will fill later and a classic way to create a broken one by typo. Swapping the two arguments is the other common slip: ln -s newlink real with an existing real fails with failed to create symbolic link 'real': File exists, because ln tried to create the link under the name of your existing file.

Hard link vs symbolic link

Without -s, ln creates a hard link: a second name for the same file, not a pointer to a path. Both names share one inode, and the data stays until the last name is gone:

Example output
$ ln -v orig.txt hard.txt
'hard.txt' => 'orig.txt'
$ ls -li orig.txt hard.txt
3542701 -rw-r--r-- 2 root root 5 Oct 21 19:45 hard.txt
3542701 -rw-r--r-- 2 root root 5 Oct 21 19:45 orig.txt
$ rm orig.txt; cat hard.txt
data
Symbolic link (ln -s)Hard link (ln)
What it isA small file that stores a pathAnother name for the same inode
Target deletedLink breaks (dangling)Data stays; the other name still works
DirectoriesYesNo: hard link not allowed for directory
Across filesystemsYesNo: Invalid cross-device link
Shown in ls -ll at the start, -> targetLooks like a normal file; link count above 1
Typical usecurrent release, sites-enabled, binaries in /usr/local/binBackup snapshots (rsync --link-dest), deduplication

For almost everything on a server you want -s. Hard links show up mostly in backup tools, such as the snapshot pattern in the rsync guide.

Relative or absolute target?

A relative target is resolved from the directory the link sits in, not from the directory you were in when you typed the command. This is the mistake behind most broken links:

Example output
$ ln -s notes.txt conf/notes        # looks for conf/notes.txt
$ cat conf/notes
cat: conf/notes: No such file or directory
$ ln -sr notes.txt conf/notes       # -r works out the relative path for you
'conf/notes' -> '../notes.txt'
Relative (../shared/x)Absolute (/srv/app/shared/x)
Survives moving the whole treeYesNo
Survives moving only the linkNo: in the test, a working link moved one folder down gave No such file or directoryYes: the absolute link moved the same way still worked
Works inside a container or chroot that mounts the tree elsewhereYesOnly if the path is the same there
Easy to readLessMore

Rule of thumb: relative for links inside one project or release tree, absolute for links from system locations such as /usr/local/bin or /etc/nginx/sites-enabled.

Replacing a link: -f and -n

ln refuses to overwrite an existing name. -f removes the old one first, which is enough for a link to a file:

Example output
$ ln -s target.txt name
ln: failed to create symbolic link 'name': File exists
$ ln -sfv target.txt name
'name' -> 'target.txt'

For a link to a directory, -f alone does the wrong thing: ln follows the existing link into the directory and creates the new link inside it. -n tells ln to treat the existing link as a plain file, so always repoint directory links with -sfn:

Example output
$ ln -sfv releases/v3 app/current
'app/current/v3' -> 'releases/v3'          # wrong: a new link inside releases/v2
$ ln -sfnv releases/v3 app/current
'app/current' -> 'releases/v3'             # right

For a release switch where no request may ever find the link missing, create the new link under a temporary name and rename it over the old one with mv -T current.new current; rename() replaces a name atomically.

Example output
$ readlink app/current
releases/v3
$ readlink -f app/current
/srv/app/releases/v3
$ realpath app/current
/srv/app/releases/v3

Plain readlink prints the stored text, one step only. readlink -f and realpath follow every link in the chain and print the absolute path at the end. Broken links are where the variants differ: for a link to a missing file, readlink -f and realpath still printed the would-be path with exit status 0, while readlink -e printed nothing and returned 1, which makes it the right test in a script. pwd -P does the same for the directory you are in; the pwd guide shows the difference that makes.

Find and fix broken symlinks

Example output
$ ls -l broken
lrwxrwxrwx 1 root root 11 Oct 21 19:45 broken -> missing.txt
$ cat broken
cat: broken: No such file or directory

The error names the link, which exists, so it can be confusing at first. find -type l lists all links; -xtype l lists only those whose target is missing:

any Linux
find /etc /usr/local -xtype l                 # list broken links
find ./etc -xtype l -print -delete            # remove them, after reading the list

In the test, -xtype l listed the two dangling links and skipped the working one. A link that points to itself, directly or in a circle, fails differently: cat a answered Too many levels of symbolic links. More on these tests in the find guide.

Remove a symlink: rm or unlink, no trailing slash

rm current or unlink current removes the link and leaves the target alone. The trouble starts with a trailing slash, which shell completion adds to a link to a directory:

Example output
$ rm current/
rm: cannot remove 'current/': Is a directory
$ unlink current/
unlink: cannot unlink 'current/': Not a directory
$ rmdir current
rmdir: failed to remove 'current': Not a directory
$ rm -v current
removed 'current'

Permissions, ownership and copying

  • Mode: a symlink always shows lrwxrwxrwx, and Linux ignores it. What counts is the target’s mode. chmod 600 slink changed the target’s mode to -rw------- and left the link as it was; see the chmod guide.
  • Owner: chown also acts on the target; chown -h changes the link itself.
  • Copying: plain cp slink copy copied the target’s content into a regular file; cp -P (and cp -a) copied the link as a link. rsync -a and tar keep links as links too.

Symlinks on a server: nginx sites-enabled and releases

On Debian and Ubuntu, nginx.conf includes every file in /etc/nginx/sites-enabled, not sites-available. A site is switched on by linking its file from sites-available, and off by removing the link:

Debian · Ubuntu
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

# switch the site off again: remove the link, never the file in sites-available
sudo rm /etc/nginx/sites-enabled/example.com
sudo nginx -t && sudo systemctl reload nginx
Example output
$ ls -l /etc/nginx/sites-enabled
lrwxrwxrwx 1 root root 38 Oct 21 19:46 example.com -> /etc/nginx/sites-available/example.com

Use the absolute path here. ln -s sites-available/example.com sites-enabled/, typed in /etc/nginx, creates a dangling link: its relative target is resolved from sites-enabled, so it points at sites-enabled/sites-available/example.com. The full site setup is in the nginx reverse proxy guide. The same pattern runs zero-downtime deploys: each release goes into its own directory, and current points to the live one, so a rollback is one ln -sfn back to the previous release.

Symbolic links on Windows: mklink

Windows has symlinks too, created with mklink in Command Prompt. Per Microsoft’s command reference the order is the reverse of Linux: the new link first, the target second.

cmd (as Administrator)
rem file symlink
mklink C:\links\app.conf C:\apps\app\app.conf
rem directory symlink
mklink /D C:\www\current C:\www\releases\v2
rem directory junction
mklink /J C:\data D:\data
rem hard link
mklink /H C:\links\hard.txt C:\files\orig.txt

Creating symbolic links needs administrator rights by default, so open the prompt with “Run as administrator”. mklink is built into cmd, not a program, so PowerShell uses its own command instead:

PowerShell (as Administrator)
New-Item -ItemType SymbolicLink -Path C:\www\current -Target C:\www\releases\v2

On a Windows VPS, junctions (/J) are a common way to move a data folder to another drive or partition while programs keep using the old path: they link folders on local volumes only, and creating one needs no administrator rights.

Frequently asked

What is a symbolic link in Linux?

A symbolic link (symlink, soft link) is a small file that contains the path of another file or directory. When a program opens the link, the kernel opens the target instead. If the target is deleted or moved, the link breaks and points nowhere.

What is the order of arguments in ln -s?

Target first, link name second: ln -s TARGET LINK_NAME, the same order as cp. ln -s /srv/app/releases/v2 /srv/app/current creates current, pointing to releases/v2. Windows mklink uses the reverse order: link first, target second.

What is the difference between a hard link and a soft link?

A soft (symbolic) link stores a path: it can point to directories and to other filesystems, and it breaks when the target is removed. A hard link is a second name for the same file data: the data stays as long as one name exists, but hard links cannot point to directories or cross filesystems.

How do I remove a symlink without deleting the target?

Run rm linkname or unlink linkname, without a trailing slash. With the slash, rm treats the name as the directory it points to: rm -rf linkname/ deletes the contents of the target directory and leaves the link in place.

Related articles

All guides