Symbolic Links and Hard Links - Understanding File References in Linux
The Short Answer
A symbolic link stores a path string (like a shortcut); a hard link is an additional directory entry pointing to the same inode (the same data under a different name). When in doubt, use a symbolic link (ln -s).
When to use which
- Cross-filesystem links, or links to directories → symbolic link
- Incremental backups where deleted originals must not destroy data → hard link
- Not sure → symbolic link
ln Command Syntax
The ln command creates both symbolic and hard links.
# Symbolic link (-s is required) ln -s [target] [link_name] # Hard link (no -s) ln [target] [link_name]
Omitting -s silently creates a hard link. If the target is a directory it will fail with an error, but for regular files the mistake goes unnoticed.
What Is a Symbolic Link?
A symbolic link is a special file that stores a path string. The OS resolves that path at each access — similar to a Windows shortcut, but handled transparently by the kernel.
$ ln -s /etc/nginx/nginx.conf ./nginx.conf $ ls -la lrwxrwxrwx 1 user user 22 Jan 1 00:00 nginx.conf -> /etc/nginx/nginx.conf
The l at the start of the permissions string indicates a symlink. The -> shows the stored path.
Symbolic Link Characteristics
| Property | Details |
|---|---|
| Cross-filesystem | Yes (different mount points OK) |
| Link to directory | Yes |
| If target is deleted | Link becomes dangling (broken) |
| Resolution | Path is re-resolved at every access |
Inspect and Resolve Links
# Show the stored path $ readlink nginx.conf /etc/nginx/nginx.conf # Resolve to absolute path $ readlink -f nginx.conf /etc/nginx/nginx.conf
What Is a Hard Link?
A hard link is an additional directory entry pointing to the same inode. Both names refer to the exact same data — one inode, two names.
$ echo "hello" > original.txt $ ln original.txt hardlink.txt $ ls -lai 1234567 -rw-r--r-- 2 user user 6 Jan 1 00:00 hardlink.txt 1234567 -rw-r--r-- 2 user user 6 Jan 1 00:00 original.txt
The leading number (1234567) is the inode number — identical numbers mean identical data. The link count (2) shows how many directory entries reference this inode.
Hard Link Characteristics
| Property | Details |
|---|---|
| Cross-filesystem | No (same partition only) |
| Link to directory | No (restricted for regular users) |
| If original is deleted | Data persists until link count reaches 0 |
| Resolution | Direct inode access (no path traversal) |
Comparison: Symlink vs Hard Link
The core difference is what each one references.
| Aspect | Symbolic Link | Hard Link |
|---|---|---|
| References | Path string | inode |
| Cross-filesystem | Yes | No |
| Directory target | Yes | No |
| After target deleted | Becomes dangling | Data survives |
ls permissions |
Starts with l |
Same as regular file |
| inode | Own inode (separate) | Same inode as target |
Practical Use Cases
When to Use Symbolic Links
Dotfiles management
$ ln -s ~/dotfiles/.bashrc ~/.bashrc $ ln -s ~/dotfiles/.vimrc ~/.vimrc
Version-managed "current" directory
# Point current to version 1.2.0 $ ln -s /opt/app-1.2.0 /opt/app/current # Upgrade: re-point the link atomically $ ln -sfn /opt/app-1.3.0 /opt/app/current
-f overwrites an existing link; -n prevents following a directory target, so the link itself is replaced rather than a new link being created inside the directory.
When to Use Hard Links
Incremental backups with rsync
$ rsync -a --link-dest=/backup/prev/ /data/ /backup/today/
Files unchanged since the previous backup are hard-linked rather than copied, so each day's snapshot is space-efficient while remaining independent.
Common Pitfalls
Relative path symlinks break when accessed from a different directory
# Created from /home/user $ ln -s ../etc/nginx/nginx.conf nginx.conf # Breaks when accessed from /tmp $ cd /tmp $ cat nginx.conf cat: nginx.conf: No such file or directory
The symlink records the exact path string you passed to ln — not a path relative to where the link file lives. Use absolute paths for symlinks that must work from multiple locations.
Re-pointing a symlink that targets a directory
# Wrong: if current is a directory, ln -sf creates a link INSIDE it $ ln -sf /opt/app-2.0.0 /opt/app/current # Right: -n replaces the link itself $ ln -sfn /opt/app-2.0.0 /opt/app/current
Finding dangling symlinks
$ find /path -maxdepth 1 -xtype l
-xtype l matches symlinks whose target does not exist (-type l alone includes all symlinks, both valid and broken).