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.
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 targetThe 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:
$ 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/v2The 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:
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 firstThe 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:
$ 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 is | A small file that stores a path | Another name for the same inode |
| Target deleted | Link breaks (dangling) | Data stays; the other name still works |
| Directories | Yes | No: hard link not allowed for directory |
| Across filesystems | Yes | No: Invalid cross-device link |
| Shown in ls -l | l at the start, -> target | Looks like a normal file; link count above 1 |
| Typical use | current release, sites-enabled, binaries in /usr/local/bin | Backup 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:
$ 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 tree | Yes | No |
| Survives moving only the link | No: in the test, a working link moved one folder down gave No such file or directory | Yes: the absolute link moved the same way still worked |
| Works inside a container or chroot that mounts the tree elsewhere | Yes | Only if the path is the same there |
| Easy to read | Less | More |
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:
$ 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:
$ 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' # rightFor 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.
Where does a link point? readlink and realpath
$ readlink app/current
releases/v3
$ readlink -f app/current
/srv/app/releases/v3
$ realpath app/current
/srv/app/releases/v3Plain 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
$ ls -l broken
lrwxrwxrwx 1 root root 11 Oct 21 19:45 broken -> missing.txt
$ cat broken
cat: broken: No such file or directoryThe 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:
find /etc /usr/local -xtype l # list broken links
find ./etc -xtype l -print -delete # remove them, after reading the listIn 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:
$ 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 slinkchanged the target’s mode to-rw-------and left the link as it was; see the chmod guide. - Owner:
chownalso acts on the target;chown -hchanges the link itself. - Copying: plain
cp slink copycopied the target’s content into a regular file;cp -P(andcp -a) copied the link as a link.rsync -aandtarkeep 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:
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$ ls -l /etc/nginx/sites-enabled
lrwxrwxrwx 1 root root 38 Oct 21 19:46 example.com -> /etc/nginx/sites-available/example.comUse 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.
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.txtCreating 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:
New-Item -ItemType SymbolicLink -Path C:\www\current -Target C:\www\releases\v2On 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.