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

  1. Explain how the Linux filesystem stores files — Describe inodes, data blocks, and how filenames map to file data
  2. Create and use hard links — Use ln to create hard links and explain their behaviour when the original file is deleted
  3. Create and use symbolic links — Use ln -s to create symbolic links and explain their behaviour when the target is moved or deleted
  4. Identify and distinguish link types — Use ls -l, ls -i, and stat to 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
Key insight

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.

Directories always start with a link count of 2

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
Hard link survival rule

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
RHCSA Exam Syntax

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
Dangling symlinks

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
Reading ls -l output for symlinks

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
Best practice for most cases

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
Dangling symlinks in ls

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
Never use rm -r on a symlink to a directory

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.

  1. What is an inode, and what information does it store?
  2. You create a hard link to a file and then delete the original filename. What happens to the data, and why?
  3. Write the command to create a symbolic link named /tmp/sshd-link that points to /etc/ssh/sshd_config.
  4. How can you tell from ls -l output whether a file is a symbolic link? How can you tell whether two names are hard links to the same file?
  5. You move /etc/ssh/sshd_config to a new location. What happens to a hard link that pointed to it? What happens to a symlink that pointed to it?
  6. What is the danger of running rm -r on a symlink that points to a directory?

Knowledge Check — Answers

  1. 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.
  2. 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.
  3. ln -s /etc/ssh/sshd_config /tmp/sshd-link
  4. A symlink shows l as the first character of its permissions field and displays -> target after the name in ls -l output.
    Two hard links to the same file are identified by running ls -li and comparing their inode numbers — hard links to the same file share the same number.
  5. 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.
  6. When rm -r follows a symlink to a directory, it deletes the actual contents of the target directory — not just the symlink. Use rm linkname without -r to remove only the symlink itself.

Key Takeaways

  1. 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.
  2. 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 ln and ln -s respectively.
  3. Identify links with ls -li and stat. Symlinks show l in the permissions field and -> in the name. Hard links look like regular files — compare inode numbers with ls -li to confirm two names share the same inode.
  4. Two critical safety rules. Never use rm -r on a symlink to a directory — it deletes the real directory contents. Always use ln -sf (not ln -s) to replace an existing symlink.

Graded Lab

  • Create a file /tmp/original.txt with one line of content. Use stat to view its inode number and initial link count.
  • Create a hard link at /tmp/hardlink.txt pointing to /tmp/original.txt. Run ls -li on both names and confirm they share the same inode number and that the link count is now 2.
  • Edit /tmp/hardlink.txt and add a second line. Read /tmp/original.txt and confirm the change appears in both names.
  • Remove /tmp/original.txt. Confirm /tmp/hardlink.txt still works and the link count has returned to 1.
  • Create a symbolic link at /tmp/symlink.txt pointing to /tmp/hardlink.txt. Run ls -li and confirm the symlink has a different inode number.
  • Remove /tmp/hardlink.txt. Attempt to read /tmp/symlink.txt and observe the dangling link error. Use find /tmp -xtype l to locate the dangling symlink.
RHCSA Objective

"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.