RED HAT ENTERPRISE LINUX
Hard Links and
Soft Links
Create hard and soft links
CIS126RH | RHEL System Administration 1
Mesa Community College
Links let multiple names refer to the same data on disk. Hard links and symbolic links serve different purposes and behave differently when the original file is moved or deleted. Understanding the distinction — and knowing which type to use — is a practical skill for managing configuration files, shared libraries, and deployment workflows on RHEL. Both link types are tested on the RHCSA exam.
Learning Objectives
- Explain how the Linux filesystem stores files — Describe inodes, data blocks, and how filenames map to file data
-
Create and use hard links —
Use
lnto create hard links and explain their behaviour when the original file is deleted -
Create and use symbolic links —
Use
ln -sto create symbolic links and explain their behaviour when the target is moved or deleted -
Identify and distinguish link types —
Use
ls -l,ls -i, andstatto inspect links and determine whether a name is a hard link or symbolic link
How the Filesystem Stores Files
Every file on a Linux filesystem has two separate parts: the data and the name. Understanding this separation explains everything about how links work.
The Inode
- An inode is a data structure that stores everything about a file except its name — permissions, owner, size, timestamps, and pointers to data blocks
- Every file has exactly one inode, identified by an inode number
- The inode number is unique within a filesystem
The Directory Entry
- A directory is a table that maps filenames to inode numbers
- The filename is just a label — it is stored in the directory, not the inode
- Multiple filenames can point to the same inode — this is what a hard link is
When you open a file by name, the filesystem looks up the name in a directory to find the inode number, then reads the inode to find the data blocks. The name is just one path to the inode — there can be many.
Inodes and Link Counts
The inode tracks how many directory entries point to it. This is the hard link count.
# View inode number and link count
$ ls -li /etc/hosts
1234567 -rw-r--r-- 1 root root 158 May 25 09:00 /etc/hosts
# ^inode ^link count
# Detailed inode information with stat
$ stat /etc/hosts
File: /etc/hosts
Size: 158 Blocks: 8 IO Block: 4096 regular file
Device: fd00h Inode: 1234567 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/root) Gid: ( 0/root)
When a new hard link is created, the link count increases. When a name is removed
with rm, the link count decreases. The data is only deleted when
the link count reaches zero.
A new directory has a link count of 2 — one for its own entry in the parent directory
and one for the . entry inside itself. Each subdirectory adds 1 more
because of its .. entry pointing back up.
Hard Links
A hard link is an additional directory entry pointing to an existing inode. Both the original name and the hard link refer to exactly the same inode — and therefore the same data and the same metadata.
- Both names are equal — there is no "original" and no "link" at the inode level
- Editing the file through either name changes the same data
- Deleting one name does not delete the data — the other name still works
- Hard links must be on the same filesystem as the target
- Hard links cannot point to directories (to prevent loops)
- Hard links cannot cross filesystem boundaries
The file's data survives as long as at least one hard link (directory entry) points to its inode. Only when the last name is removed does the filesystem free the data blocks.
Creating Hard Links with ln
The ln command without any flags creates a hard link.
# Create a hard link — syntax: ln TARGET LINK_NAME
$ ln /etc/hosts /tmp/hosts-hardlink
# Confirm both names share the same inode number
$ ls -li /etc/hosts /tmp/hosts-hardlink
1234567 -rw-r--r-- 2 root root 158 May 25 09:00 /etc/hosts
1234567 -rw-r--r-- 2 root root 158 May 25 09:00 /tmp/hosts-hardlink
# Same inode, link count is now 2
# Both names show identical content
$ cat /tmp/hosts-hardlink
# Removing one name leaves the other intact
$ rm /tmp/hosts-hardlink
$ cat /etc/hosts # still works — link count back to 1
Hard link: ln target linkname — no flags.
Symbolic link: ln -s target linkname — requires -s.
Symbolic Links
A symbolic link (also called a symlink or soft link) is a special file whose contents are the path to another file or directory. It is a pointer by name rather than a pointer by inode.
- A symlink has its own inode — it is a separate file with its own inode number
- The symlink's data content is the target path string
- When you access a symlink, the kernel follows the path to the target
- Symlinks can point to directories
- Symlinks can cross filesystem boundaries
- If the target is deleted or moved, the symlink breaks — it becomes a dangling link
If the file a symlink points to is deleted or renamed, the symlink still exists
but no longer works. Accessing a dangling symlink produces: No such file or directory.
The symlink itself must be removed or updated.
Creating Symbolic Links with ln -s
Add the -s flag to ln to create a symbolic link.
# Create a symlink — syntax: ln -s TARGET LINK_NAME
$ ln -s /etc/hosts /tmp/hosts-symlink
# Symlinks are shown with l type and -> arrow in ls -l
$ ls -li /etc/hosts /tmp/hosts-symlink
1234567 -rw-r--r-- 1 root root 158 May 25 09:00 /etc/hosts
9876543 lrwxrwxrwx 1 root root 10 May 25 09:05 /tmp/hosts-symlink -> /etc/hosts
# Different inode numbers — symlink is its own file
# l at the start of permissions = symlink type
# size 10 = length of the target path string "/etc/hosts"
# Symlink to a directory
$ ln -s /etc/ssh /tmp/ssh-config
$ ls /tmp/ssh-config
sshd_config ssh_config moduli
The l at the start of the permissions field identifies a symlink.
The -> shows where the symlink points. The size shown is the
length of the target path string, not the size of the target file.
Absolute vs Relative Symlink Targets
The target path stored in a symlink can be absolute or relative. The choice affects whether the symlink still works after the link is moved.
Absolute Target — Always the Same Path
# Absolute target — works from anywhere, but breaks if target moves
$ ln -s /etc/ssh/sshd_config /tmp/sshd_config
$ ls -l /tmp/sshd_config
lrwxrwxrwx 1 root root 20 ... /tmp/sshd_config -> /etc/ssh/sshd_config
Relative Target — Resolved from the Link's Location
# Relative target — resolved relative to the symlink's directory
$ ln -s ../ssh/sshd_config /etc/backup/sshd_config
# From /etc/backup/, ../ssh/sshd_config resolves to /etc/ssh/sshd_config
# If the symlink is moved, it will break because the relative path changes
Use absolute paths for symlink targets unless you are creating a symlink that must travel with the files it points to (such as inside a self-contained package or repository). Absolute paths are easier to understand and less likely to break.
Dangling Symlinks
A symlink becomes dangling when its target no longer exists at the stored path. The symlink file itself still exists but is broken.
# Create a symlink
$ ln -s /tmp/original.txt /tmp/mylink
$ echo "hello" > /tmp/original.txt
$ cat /tmp/mylink
hello
# Delete the target
$ rm /tmp/original.txt
# The symlink still exists but is broken
$ ls -l /tmp/mylink
lrwxrwxrwx 1 student student 20 ... /tmp/mylink -> /tmp/original.txt
$ cat /tmp/mylink
cat: /tmp/mylink: No such file or directory
# Find dangling symlinks under a directory
$ find /tmp -xtype l
Many terminals display dangling symlinks in a different colour (often red or blinking)
when using ls --color. The arrow still shows the target path
even though it no longer exists.
Hard Links vs Symbolic Links
| Feature | Hard link | Symbolic link |
|---|---|---|
| Own inode? | No — shares the target's inode | Yes — has its own inode |
| Cross filesystem? | No — same filesystem only | Yes — can point anywhere |
| Can link to a directory? | No (root only, special cases) | Yes |
| If target is deleted? | Data survives — hard link still works | Link becomes dangling — broken |
| If target is moved? | Hard link still works — same inode | Link breaks if absolute path used |
Shown in ls -l as? |
Regular file (-) — indistinguishable without -i |
Symlink (l) with -> target |
| Command to create | ln target linkname |
ln -s target linkname |
| Typical use | Redundant names for critical files; version-independent access | Shortcuts to directories; version management; cross-filesystem links |
Inspecting Links
Three tools let you examine links and determine their type.
ls -l — Identify Symlinks
$ ls -l /etc/localtime
lrwxrwxrwx 1 root root 38 ... /etc/localtime -> ../usr/share/zoneinfo/America/Phoenix
ls -li — Compare Inode Numbers
$ ls -li /usr/bin/python3 /usr/bin/python3.11
8888 lrwxrwxrwx 1 root root 9 ... /usr/bin/python3 -> python3.11
9999 -rwxr-xr-x 1 root root ... /usr/bin/python3.11
stat — Full Inode Details
$ stat /etc/localtime
File: /etc/localtime -> ../usr/share/zoneinfo/America/Phoenix
Size: 38 Blocks: 0 IO Block: 4096 symbolic link
Inode: 33554519 Links: 1
readlink — Show the Target of a Symlink
$ readlink /etc/localtime
../usr/share/zoneinfo/America/Phoenix
$ readlink -f /etc/localtime
/usr/share/zoneinfo/America/Phoenix # fully resolved absolute path
Real-World Uses of Links
Symlinks for Version Management
# /usr/bin/python3 points to the current default Python version
$ ls -l /usr/bin/python3
lrwxrwxrwx ... /usr/bin/python3 -> python3.11
# Updating the default version is a one-command symlink change
$ sudo ln -sf /usr/bin/python3.12 /usr/bin/python3
Symlinks for Timezone Configuration
# /etc/localtime is always a symlink to the active timezone file
$ ls -l /etc/localtime
lrwxrwxrwx ... /etc/localtime -> ../usr/share/zoneinfo/America/Phoenix
Hard Links for Backup Efficiency
# rsync uses hard links to avoid duplicating unchanged files
$ rsync -a --link-dest=/backup/yesterday /etc/ /backup/today/
# Unchanged files appear in both /backup/yesterday and /backup/today
# but occupy disk space only once — they share inodes
Symlinks for Log Management
# /var/log/httpd/access_log may be a symlink to the real log location
$ ls -l /var/log/httpd/
Updating and Removing Links
Removing a Link
# Remove a symlink — rm removes the link, not the target
$ rm /tmp/mylink
# unlink is an alternative — explicitly removes one directory entry
$ unlink /tmp/mylink
# Remove a hard link — decrements the link count
$ rm /tmp/hosts-hardlink
# The original /etc/hosts is unaffected if link count was > 1
Replacing a Symlink
# -f forces replacement of an existing symlink
$ ln -sf /new/target /tmp/mylink
# Without -f, ln errors if the link name already exists
$ ln -s /new/target /tmp/mylink
ln: failed to create symbolic link '/tmp/mylink': File exists
Running rm -r /tmp/ssh-config where ssh-config is a symlink
to /etc/ssh will delete the contents of /etc/ssh itself.
Use rm /tmp/ssh-config (without -r) to remove just the
symlink.
Links in the RHEL System
RHEL uses symlinks extensively throughout the filesystem. Knowing how to read them helps you understand how the system is organized.
# Timezone — always a symlink
$ ls -l /etc/localtime
lrwxrwxrwx ... /etc/localtime -> ../usr/share/zoneinfo/America/Phoenix
# systemd default target — a symlink to the active target unit
$ ls -l /etc/systemd/system/default.target
lrwxrwxrwx ... default.target -> /usr/lib/systemd/system/multi-user.target
# /bin is a symlink to /usr/bin on modern RHEL
$ ls -l /bin
lrwxrwxrwx ... /bin -> usr/bin
# Enabled services are symlinks in /etc/systemd/system/
$ ls -l /etc/systemd/system/multi-user.target.wants/
lrwxrwxrwx ... sshd.service -> /usr/lib/systemd/system/sshd.service
Common Mistakes
| Mistake | What goes wrong | Correct approach |
|---|---|---|
Forgetting -s for a symlink |
ln creates a hard link instead — fails if crossing filesystems or targeting a directory |
Always use ln -s for symbolic links |
Using rm -r on a symlink to a directory |
Deletes the contents of the real directory the symlink points to | Use rm linkname without -r to remove just the symlink |
Replacing a symlink without -f |
ln -s fails because the link name already exists |
Use ln -sf to force replacement of an existing symlink |
| Moving the target of a symlink | Symlink becomes dangling — accessing it gives "No such file or directory" | Update the symlink after moving the target, or use a hard link if staying on the same filesystem |
| Confusing hard links with copies | Assuming the hard link is independent — edits to one appear in the other | Remember: hard links share one inode. Editing either name changes the same data. |
| Expecting a hard link to work across filesystems | ln fails with "Invalid cross-device link" |
Use a symbolic link instead — symlinks can cross filesystem boundaries |
Knowledge Check
Answer these before moving to the next slide.
- What is an inode, and what information does it store?
- You create a hard link to a file and then delete the original filename. What happens to the data, and why?
- Write the command to create a symbolic link named
/tmp/sshd-linkthat points to/etc/ssh/sshd_config. - How can you tell from
ls -loutput whether a file is a symbolic link? How can you tell whether two names are hard links to the same file? - You move
/etc/ssh/sshd_configto a new location. What happens to a hard link that pointed to it? What happens to a symlink that pointed to it? - What is the danger of running
rm -ron a symlink that points to a directory?
Knowledge Check — Answers
- An inode is a data structure that stores everything about a file except its name — permissions, owner, group, size, timestamps, and pointers to the data blocks on disk. Every file has exactly one inode identified by an inode number.
- The data survives. Deleting the original filename removes one directory entry and decrements the link count from 2 to 1. The inode and its data blocks remain on disk because the hard link still points to them. The data is only freed when the link count reaches zero.
ln -s /etc/ssh/sshd_config /tmp/sshd-link- A symlink shows
las the first character of its permissions field and displays-> targetafter the name inls -loutput.
Two hard links to the same file are identified by runningls -liand comparing their inode numbers — hard links to the same file share the same number. - The hard link still works. Moving a file does not change its inode.
The hard link points directly to the inode, so it is unaffected by the name change.
The symlink becomes dangling. It stored the old path as a string. Now that the file has moved, the stored path no longer resolves to anything. - When
rm -rfollows a symlink to a directory, it deletes the actual contents of the target directory — not just the symlink. Userm linknamewithout-rto remove only the symlink itself.
Key Takeaways
- A filename is just a pointer to an inode. The inode holds all file metadata and data pointers. Multiple names can point to the same inode — this is a hard link. The data survives until all links are removed.
-
Hard links share an inode — symlinks store a path.
Hard links: same filesystem only, no directories, data survives deletion of original.
Symlinks: cross filesystems, can point to directories, break if target is moved or deleted.
Create with
lnandln -srespectively. -
Identify links with
ls -liandstat. Symlinks showlin the permissions field and->in the name. Hard links look like regular files — compare inode numbers withls -lito confirm two names share the same inode. -
Two critical safety rules.
Never use
rm -ron a symlink to a directory — it deletes the real directory contents. Always useln -sf(notln -s) to replace an existing symlink.
Graded Lab
- Create a file
/tmp/original.txtwith one line of content. Usestatto view its inode number and initial link count. - Create a hard link at
/tmp/hardlink.txtpointing to/tmp/original.txt. Runls -lion both names and confirm they share the same inode number and that the link count is now 2. - Edit
/tmp/hardlink.txtand add a second line. Read/tmp/original.txtand confirm the change appears in both names. - Remove
/tmp/original.txt. Confirm/tmp/hardlink.txtstill works and the link count has returned to 1. - Create a symbolic link at
/tmp/symlink.txtpointing to/tmp/hardlink.txt. Runls -liand confirm the symlink has a different inode number. - Remove
/tmp/hardlink.txt. Attempt to read/tmp/symlink.txtand observe the dangling link error. Usefind /tmp -xtype lto locate the dangling symlink.
"Create hard and soft links."
Know ln target linkname for hard links and
ln -s target linkname for symbolic links.
Be able to identify link type and inode from ls -li output.