sysadmin · May 18, 2026 · 12 min read

Stable log references.
inodes, hard links, and dead symlinks.

logadmin1 needs a reference to the current audit log that survives rotation, and a way to catch the broken symlinks that went unnoticed for two days. This is inodes, hard links versus soft links, and find.

// what we’re getting into
  1. The ticket
  2. Concept review. What a filename really is.
  3. The hard link. A reference that survives rotation.
  4. The symlink. A reference that can rot.
  5. Detecting broken symlinks
  6. cp versus ln, made concrete
  7. Exam questions
  8. Final checklist: confirm everything works

The ticket: logadmin1 has an audit log whose name keeps changing because of rotation. They want a stable reference to the current log that does not break when rotation renames the file, and the backup service needs the same thing. On top of that, last week someone deleted a file and the symlink pointing at it broke silently for two days. They want to know how to catch that.

The real problem: A filename is not a file. Once that idea lands, hard links and soft links stop being confusing and the whole ticket falls out of it.

What we are doing: Learning inodes, hard links, and symbolic links on real files, then building a proactive check for dead symlinks.

The ticket

Confirm the users the ticket references exist before you touch anything.

bash
id logadmin1   # UID 8001
id svc_auto    # the backup proxy account, UID 6503

Concept review. What a filename really is.

Read the manual pages first. These four are the whole toolkit for this ticket.

bash
man ln
man find
man stat
man readlink

An inode is the file. It holds the permissions, the owner, and the pointers to the data blocks. A filename is just a label that points at an inode. That single fact explains both link types.

Hard link: another name for the same inode. Same inode number, same data. It survives the original name being deleted or renamed, because it was never pointing at the name, only at the inode. Hard links cannot cross filesystems and cannot point at a directory.

Soft link, or symlink: a tiny file that stores a path string. It has its own inode. If the path it stores is moved or deleted, the symlink is left pointing at nothing. Symlinks can cross filesystems freely and can point at directories.

Set the scene, then link it. ls -li prints the inode number first so you can prove the two names are the same file.

bash
sudo mkdir -p /srv/logs/audit
sudo bash -c 'echo "audit event 1" > /srv/logs/audit/audit.log.20260503'
sudo chown -R logadmin1:logadmins /srv/logs/audit
ls -li /srv/logs/audit/

# create a hard link to give logadmin1 a stable name
sudo ln /srv/logs/audit/audit.log.20260503 /srv/logs/audit/audit.current
ls -li /srv/logs/audit/    # same inode number for both

Now simulate rotation by renaming the original, then read through the stable name.

bash
sudo mv /srv/logs/audit/audit.log.20260503 /srv/logs/audit/audit.log.20260503.gz
cat /srv/logs/audit/audit.current    # still works
ls -li /srv/logs/audit/

The hard link followed the inode, not the name, and the inode did not move. That is the stable reference logadmin1 asked for.

A symlink is the right tool when you need to cross a filesystem or point at a directory. It is also brittle, and this is exactly the failure that bit them.

bash
sudo ln -s /srv/logs/audit/audit.current /srv/logs/audit/backup.target
readlink /srv/logs/audit/backup.target    # shows the stored path
cat /srv/logs/audit/backup.target         # works, for now

# delete the target and watch the link go dead
sudo rm /srv/logs/audit/audit.current
ls -l /srv/logs/audit/backup.target       # shown in red, dangling
cat /srv/logs/audit/backup.target         # No such file or directory

The link still exists. It just points at nothing. Nobody notices until they try to read it, which is how it went unseen for two days.

Detecting broken symlinks

find has a filter built for this. -xtype l matches symlinks whose target does not resolve. Run it on a schedule and dead links surface the same day they break.

bash
# every broken symlink under /srv
sudo find /srv -xtype l 2>/dev/null

# turn it into a report for logadmin1
sudo mkdir -p /srv/audit_reports
sudo find /srv -xtype l 2>/dev/null > /srv/audit_reports/broken_symlinks.txt
cat /srv/audit_reports/broken_symlinks.txt

cp versus ln, made concrete

Worth seeing once so it never trips you up. cp makes a new inode, an independent copy. ln makes another name for the same inode.

bash
echo "original content" | sudo tee /srv/logs/audit/source.log
sudo cp /srv/logs/audit/source.log /srv/logs/audit/copy.log      # new inode
sudo ln /srv/logs/audit/source.log /srv/logs/audit/hardlink.log  # same inode
sudo ls -li /srv/logs/audit/source.log /srv/logs/audit/copy.log /srv/logs/audit/hardlink.log

# edit the source and see who changes
sudo bash -c 'echo "new line" >> /srv/logs/audit/source.log'
sudo cat /srv/logs/audit/copy.log       # unchanged, separate file
sudo cat /srv/logs/audit/hardlink.log   # has the new line, same file

Exam questions

Write the command before you run it.

Q1. Create a hard link for /etc/hostname at /srv/logs/audit/hostname_hard and prove they share an inode.

Q2. Create a symbolic link for /var/log at /srv/logs/audit/varlog_link and confirm its target with readlink.

Q3. Find all broken symlinks under /srv and save the list to /srv/audit_reports/broken_links.txt.

Answers.

bash
# A1
sudo ln /etc/hostname /srv/logs/audit/hostname_hard
ls -li /etc/hostname /srv/logs/audit/hostname_hard    # inode numbers match
# A2
sudo ln -s /var/log /srv/logs/audit/varlog_link
readlink /srv/logs/audit/varlog_link
# A3
sudo find /srv -xtype l 2>/dev/null > /srv/audit_reports/broken_links.txt

Final checklist: confirm everything works

If every check passes, the ticket is done.

bash
# 1. hard link shares the inode of its target
ls -li /srv/logs/audit/audit.current

# 2. broken symlink report exists
cat /srv/audit_reports/broken_symlinks.txt

# 3. readlink shows a symlink's target
readlink /srv/logs/audit/backup.target

# 4. eyeball: all three exam commands written from scratch
# 5. tracker entry checked off

Reply to logadmin1: use a hard link for the stable reference, it tracks the inode so it survives rotation. Use a symlink only when you need to cross a filesystem or point at a directory, and run find /srv -xtype l weekly so a dead link never goes unseen for two days again.

next post
A shared folder that stops the team fighting over files