A symbolic link (symlink or soft link) in Linux is a special file that acts as a pointer to another file or directory. Create one with ln -s target linkname — for example, ln -s /var/log/nginx/access.log ~/logs/nginx_access.log creates a link in your home directory that points to the nginx log file. When you access the symlink, Linux transparently redirects you to the target. Symbolic links can point to files, directories, and even other links, and can cross filesystem boundaries.
The Filesystem’s Shortcuts
Imagine being able to access a file in two different places simultaneously without copying it — the file exists once, but you can reach it from multiple locations. Change it through any path and the change is immediately visible everywhere. That is what symbolic links make possible.
Symbolic links are one of the most useful and most used features in Linux filesystems. Every time you interact with Linux, you are silently using them. The /bin directory on Ubuntu is actually a symbolic link to /usr/bin. The Python interpreter you call as python3 may be a symlink pointing to a specific version. Configuration directories in /etc frequently use symlinks to manage alternative configurations. The entire alternative-versions management system that allows multiple installed versions of a library to coexist uses symlinks extensively.
Beyond their pervasive use within the operating system itself, symlinks are a practical daily tool: creating convenient shortcuts to deeply nested directories, providing stable paths that can be updated to point to new versions, connecting configuration files between locations, and building flexible directory structures that adapt to different environments.
This article explains symbolic links from the ground up — what they are technically, how they differ from hard links, how to create and manage them with the ln command, how to identify them in directory listings, what happens when they break, and the practical patterns where they shine. By the end, you will not only understand what symlinks are but think naturally about when to reach for them.
Understanding Links: How Linux Tracks Files
To understand symbolic links, a foundation in how Linux actually stores files on disk clarifies everything that follows.
Inodes: The True Identity of Files
Every file on a Linux filesystem has an inode — a data structure stored on disk that records everything about the file except its name:
- File size
- Timestamps (created, modified, accessed)
- Owner and group
- Permission bits
- The disk blocks where the file’s data is stored
- A reference count (how many directory entries point to this inode)
The inode has a number — its inode number — but no name. Names live in directory entries, not in inodes. A directory is a special file containing a table that maps filenames to inode numbers.
When you access a file by path — say /home/sarah/document.txt — the kernel:
- Looks up
homein the root directory’s entry table → finds inode for/home - Looks up
sarahin/home‘s entry table → finds inode for/home/sarah - Looks up
document.txtin/home/sarah‘s entry table → finds the file’s inode number - Reads the inode to get the file’s data block locations
- Returns the file’s data
The filename document.txt is just one entry in one directory table pointing to one inode. This design is what makes multiple names for the same file (links) possible.
Hard Links: Multiple Names for the Same Inode
A hard link is simply another directory entry pointing to an existing inode. After running ln original.txt hardlink.txt, you have two filenames in a directory table, both pointing to the same inode:
/home/sarah/original.txt ──┐
├──→ Inode #12345 (the actual file data)
/home/sarah/hardlink.txt ──┘
The inode’s reference count increases to 2. The file data exists once on disk. Both names are completely equivalent — there is no “original” versus “link.” The data persists as long as any hard link to the inode exists.
Hard links are powerful but limited:
- Cannot span filesystems: an inode number is meaningful only within one filesystem; a hard link must be on the same filesystem as its target
- Cannot link directories: hard links to directories are not permitted (they would create loops in the filesystem graph that
fsckand other tools cannot handle) - No visual indication: there is no way to look at a file and know whether it is a hard link or a “regular” file — both look identical in
ls
Symbolic Links: A Different Approach
A symbolic link (symlink, soft link) takes a completely different approach. It is an actual, separate file — with its own inode — whose content is a text string: the path to the target.
/home/sarah/link.txt ──→ Inode #99999 (a symlink file containing "/home/sarah/original.txt")
↓
Inode #12345 (the actual file data)
When the kernel encounters a symlink during path resolution, it reads the path string from the symlink’s inode and continues resolution from there — transparently redirecting to the target. This redirection is called dereferencing the symlink.
Because a symlink stores a path string (not an inode reference), it can:
- Point to anything, on any filesystem
- Point to directories, not just files
- Cross filesystem boundaries (a symlink on
/dev/sda1can point to a file on/dev/sdb1) - Point to non-existent targets (a dangling or broken symlink)
- Be identified visually —
ls -lashows symlinks withltype and→arrow
The trade-off: a symlink adds one level of indirection (the kernel follows the pointer). And unlike hard links, deleting the target breaks the symlink.
Creating Symbolic Links: The ln Command
The ln command creates links. Without flags it creates hard links; with -s it creates symbolic links.
Basic Syntax
ln -s target linkname
- target — the path the symlink will point to (what you want to access)
- linkname — the name and location of the symlink file you are creating
Order matters: target comes first, linkname second. This is the same order as cp source destination — you are creating linkname as a pointer to target.
Creating a Symlink to a File
$ ln -s /var/log/nginx/access.log ~/logs/nginx_access.log
$ ls -la ~/logs/
lrwxrwxrwx 1 sarah sarah 29 Feb 18 11:00 nginx_access.log -> /var/log/nginx/access.log
The l at the start of lrwxrwxrwx indicates a symbolic link. The -> shows what the symlink points to. The permissions shown (rwxrwxrwx) are the symlink’s own permissions — the target’s permissions govern actual access.
Creating a Symlink to a Directory
$ ln -s /home/sarah/projects/current_project ~/work
$ ls -la ~/ | grep work
lrwxrwxrwx 1 sarah sarah 37 Feb 18 11:05 work -> /home/sarah/projects/current_project
$ cd ~/work # This works — bash follows the symlink
$ pwd
/home/sarah/work # Note: pwd shows the logical path through the symlink
Relative vs. Absolute Symlink Paths
Symlinks can use either absolute paths or relative paths. The choice matters for portability.
Absolute symlink (the target path starts with /):
$ ln -s /etc/nginx/nginx.conf ~/nginx.conf
The symlink always points to /etc/nginx/nginx.conf regardless of where the symlink is located.
Relative symlink (the target path is relative to the symlink’s location):
$ cd /etc/nginx/sites-enabled
$ ln -s ../sites-available/mysite.conf mysite.conf
The path ../sites-available/mysite.conf is interpreted relative to /etc/nginx/sites-enabled/. This symlink would break if moved to a different directory but works correctly in its intended location.
When to use relative paths: When the symlink and its target will always be moved together (same relative position in the directory tree). Relative symlinks are more portable for self-contained directory structures.
When to use absolute paths: When pointing to a fixed system location that will not move. Easier to understand and less likely to break unexpectedly.
The -v Flag: Verbose Output
$ ln -sv /etc/nginx/sites-available/mysite.conf /etc/nginx/sites-enabled/mysite.conf
'/etc/nginx/sites-enabled/mysite.conf' -> '/etc/nginx/sites-available/mysite.conf'
The -v flag shows what was created — useful for confirming the link was made correctly.
Creating a Symlink That Overwrites an Existing One
If a symlink at linkname already exists and you want to update where it points:
$ ln -sf new_target existing_linkname
The -f (force) flag removes the existing link before creating the new one. Without -f, ln reports “File exists” and does nothing.
Identifying Symbolic Links
In ls Output
$ ls -la /etc/alternatives/ | head -10
lrwxrwxrwx 1 root root 13 Feb 18 09:00 awk -> /usr/bin/gawk
lrwxrwxrwx 1 root root 15 Feb 18 09:00 editor -> /usr/bin/nano
lrwxrwxrwx 1 root root 19 Feb 18 09:00 python3 -> /usr/bin/python3.12
lrwxrwxrwx 1 root root 22 Feb 18 09:00 python -> /usr/bin/python3.12
Every line beginning with l is a symbolic link. The -> shows the target path. The size shown (13, 15, 19, etc.) is the length of the path string stored in the symlink — not the size of the target file.
Using file to Identify a Symlink
$ file /etc/alternatives/python3
/etc/alternatives/python3: symbolic link to /usr/bin/python3.12
$ file /usr/bin/python3.12
/usr/bin/python3.12: ELF 64-bit LSB pie executable, x86-64
Using find to Locate Symlinks
$ find /etc -type l # Find all symlinks in /etc
$ find /etc -type l -name "*.conf" # Find symlinks with .conf extension
$ find ~ -type l # Find all symlinks in your home directory
Checking What a Symlink Points To
$ readlink /etc/alternatives/python3
/usr/bin/python3.12
$ readlink -f /etc/alternatives/python3 # Follow all symlinks to final target
/usr/bin/python3.12
$ readlink -f ~/work # Resolve symlink to real path
/home/sarah/projects/current_project
readlink -f follows the entire chain of symlinks and returns the absolute real path. Useful for scripts that need the actual file location regardless of how many symlinks are involved.
Broken Symlinks: When the Target Disappears
A symbolic link stores a path — if the target at that path no longer exists, the symlink becomes broken (also called a dangling symlink). The symlink file itself still exists but is useless.
$ ln -s /tmp/temporary_file.txt ~/mylink.txt
$ rm /tmp/temporary_file.txt # Delete the target
$ ls -la ~/mylink.txt
lrwxrwxrwx 1 sarah sarah 22 Feb 18 11:15 mylink.txt -> /tmp/temporary_file.txt
$ cat ~/mylink.txt
cat: /home/sarah/mylink.txt: No such file or directory
In many terminal environments with color support, broken symlinks are displayed in a distinct color (often red or flashing) to make them visually obvious.
Finding Broken Symlinks
$ find /etc -type l ! -e # Find symlinks that do not resolve to an existing file
$ find ~ -xtype l # Another approach: find symlinks with broken targets
-xtype l matches files that are symlinks whose target does not exist or cannot be followed.
Removing Broken Symlinks
$ find /path -xtype l -delete # Delete all broken symlinks
Or more carefully, review them first:
$ find /path -xtype l -print # List broken symlinks
$ find /path -xtype l -exec rm {} \; # Delete after reviewing
Removing Symbolic Links
Remove a symlink with rm or unlink:
$ rm ~/mylink.txt # Removes the symlink, NOT the target
$ unlink ~/mylink.txt # Equivalent, arguably clearer intent
Critical: rm and unlink remove the symlink file itself, not the target it points to. The target file is completely unaffected.
One common mistake: adding a trailing slash when using rm on a symlink to a directory:
$ rm -rf linktodir/ # DANGER: removes contents of the TARGET directory
$ rm linktodir # Safe: removes only the symlink
$ rm -rf linktodir # Also safe: -rf on a symlink itself just removes the symlink
The trailing slash causes the shell to dereference the symlink and operate on the directory it points to. Without the trailing slash, rm operates on the symlink itself.
Symbolic Links vs. Hard Links: When to Use Each
| Feature | Symbolic Link | Hard Link |
|---|---|---|
| Can cross filesystems | Yes | No |
| Can link directories | Yes | No (generally) |
| Shows as link in ls | Yes (l type, -> arrow) |
No (indistinguishable from regular files) |
| Breaks if target deleted | Yes (dangling symlink) | No (data persists while any link exists) |
| Can point to non-existent target | Yes (creates dangling link) | No (target must exist) |
| Inode count | Separate inode from target | Same inode as target |
| Use cases | Shortcuts, version management, cross-filesystem | Redundant names within same filesystem |
Use symbolic links for: shortcuts, pointing to directories, cross-filesystem references, version management (current → v2.1), configuration management, anything where the target might be replaced or moved.
Use hard links for: creating multiple equivalent names for a file within the same filesystem, backup systems that use hard link deduplication (like rsync --link-dest), situations where the link should survive deletion of the “original.”
In practice, symbolic links are used far more frequently. Hard links serve niche use cases, primarily in backup and archival systems.
Practical Uses of Symbolic Links
1. Version Management: Pointing “current” to a Specific Version
Software often gets installed in versioned directories. A symlink named current points to whichever version is active:
/opt/nodejs/
├── v18.20.0/
├── v20.11.0/
└── current -> v20.11.0/
$ ls /opt/nodejs/
current v18.20.0 v20.11.0
$ /opt/nodejs/current/bin/node --version
v20.11.0
# Upgrade by updating the symlink
$ sudo ln -sf /opt/nodejs/v20.11.0 /opt/nodejs/current
Scripts and configurations that reference /opt/nodejs/current/bin/node automatically use the current version without modification.
2. The Nginx/Apache Sites Pattern
Web server configurations use symlinks to manage enabled vs. available configurations:
/etc/nginx/
├── sites-available/ ← All configurations live here
│ ├── mysite.conf
│ ├── api.conf
│ └── staging.conf
└── sites-enabled/ ← Symlinks to active configurations
├── mysite.conf -> ../sites-available/mysite.conf
└── api.conf -> ../sites-available/api.conf
Enable a site:
$ sudo ln -s /etc/nginx/sites-available/staging.conf /etc/nginx/sites-enabled/
Disable a site (without deleting the configuration):
$ sudo rm /etc/nginx/sites-enabled/staging.conf
This pattern separates “all configurations I have written” from “configurations currently active” — making it easy to enable and disable without editing or moving files.
3. Dotfile Management
Developers who maintain their configuration files (dotfiles) in a git repository use symlinks to put those files in their expected locations:
~/dotfiles/
├── .bashrc
├── .vimrc
└── .config/nvim/init.vim
# Create symlinks from home directory to the repo
$ ln -sf ~/dotfiles/.bashrc ~/.bashrc
$ ln -sf ~/dotfiles/.vimrc ~/.vimrc
Now ~/.bashrc is a symlink to ~/dotfiles/.bashrc. Editing either path edits the same file. The git repository at ~/dotfiles tracks all changes, and setting up a new machine just requires cloning the repo and running the symlink-creation script.
4. Accessing Deeply Nested Directories
A symlink creates a convenient shortcut to a frequently accessed location:
$ ln -s ~/projects/client_work/2026/big_project/src/components ~/components
$ cd ~/components # Instead of cd ~/projects/client_work/2026/big_project/src/components
5. /bin → /usr/bin: System-Level Compatibility
On modern Ubuntu and Fedora systems, /bin is a symlink to /usr/bin:
$ ls -la /bin
lrwxrwxrwx 1 root root 7 Jan 15 2023 /bin -> usr/bin
Scripts and programs that reference /bin/bash still work because the symlink transparently redirects to /usr/bin/bash. This consolidation of /bin and /usr/bin into one directory was made possible entirely by symlinks.
6. The update-alternatives System
Ubuntu and Debian use a symlink-based system to manage multiple installed versions of tools:
$ ls -la /usr/bin/python*
lrwxrwxrwx python3 -> python3.12
-rwxr-xr-x python3.12
-rwxr-xr-x python3.11
$ sudo update-alternatives --config python3
There are 2 choices for python3:
Selection Path Priority Status
1 /usr/bin/python3.12 100 auto mode
* 2 /usr/bin/python3.11 50 manual mode
Press Enter to keep current choice, or enter selection number:
update-alternatives manages a chain of symlinks — changing your “default python3” updates a symlink, not the actual binaries.
Working with Symlinks in Commands
Several common Linux commands behave differently with symlinks:
cp: Copying Symlinks
$ cp symlink.txt destination/ # Copies the TARGET file, not the symlink
$ cp -P symlink.txt destination/ # Copies the symlink itself (preserves it)
$ cp -a source/ destination/ # Archive mode: preserves symlinks
mv: Moving Symlinks
$ mv symlink.txt newlocation/ # Moves the symlink (the pointer), not the target
Moving a symlink with an absolute path keeps the pointer working (the target path is unchanged). Moving a symlink with a relative path may break it if the relative path no longer resolves correctly.
ls with Symlinks
$ ls -l symlink.txt # Shows symlink properties and target
$ ls -lL symlink.txt # Dereferences symlink, shows target's properties
$ ls -la directory/ # Shows directory's own properties
$ ls -laL directory/ # Dereferences directory symlink, shows target's contents
du and df with Symlinks
$ du -sh symlink_to_dir/ # Counts the TARGET directory's size (follows symlink)
$ du -sh --no-dereference symlink_to_dir # Counts only the symlink file itself
Quick Reference: Symbolic Link Commands
| Task | Command |
|---|---|
| Create a symlink | ln -s target linkname |
| Create with verbose output | ln -sv target linkname |
| Force overwrite existing symlink | ln -sf new_target linkname |
| Remove a symlink | rm linkname or unlink linkname |
| Show symlink target | readlink linkname |
| Resolve full real path | readlink -f linkname |
| List symlinks in directory | ls -la (look for l type) |
| Find all symlinks in path | find /path -type l |
| Find broken symlinks | find /path -xtype l |
| Delete broken symlinks | find /path -xtype l -delete |
| Copy symlink itself | cp -P symlink destination |
| Check if path is a symlink | [ -L path ] && echo "is symlink" |
Conclusion: Symlinks as Filesystem Architecture
Symbolic links are one of the most elegant features of the Unix/Linux filesystem model. They enable a flexibility that would otherwise require copying files, maintaining multiple versions, or creating rigid directory structures. The ability to point any location in the filesystem to any other location — on any filesystem, across any directory boundary — is a simple mechanism with profound implications for system design.
You encounter symlinks constantly in Linux even without noticing: /bin pointing to /usr/bin, Python version management, nginx site configuration, shared library naming (libssl.so pointing to libssl.so.3.0.2). They are the filesystem’s way of saying “what you see here is actually over there.”
Understanding symlinks — what they are, how to create them, how to identify them, when they break, and the practical patterns they enable — turns you from someone who encounters them as confusing filesystem anomalies into someone who uses them as deliberate architectural tools. They are simple to create with ln -s, visible in ls -la output, inspectable with readlink, and powerful enough to underpin major system management patterns. Add symlinks to your Linux toolkit and you gain a new dimension of filesystem flexibility.




