Skip to main content

Symbolic Link

A symbolic link (symlink) is a special file that contains a path pointing to another file or directory. Accessing the symlink transparently accesses the target. Unlike hard links, symlinks can cross filesystems and point to directories.

A symbolic link is a shortcut — like a Windows shortcut or a macOS alias. It is a file that says "the thing you are looking for is actually over there." When you open the shortcut, the system transparently redirects you to the real thing.

The classic real-world use: a deployment system keeps the current version of an application at /opt/app/current. Instead of updating every config file when a new version is deployed, current is a symlink that gets updated to point to the new version directory.

How It Works

A symlink has its own inode. The data stored in its data block is just the target path as text — not the actual file content.

◈ DIAGRAM
Symlink inode 9876
+---------------------------+
| Type: symlink (l) |
| Contents: /opt/app/v2.0/ |
+---------------------------+
|
| kernel follows path
v
/opt/app/v2.0/
+---------------------------+
| index.html |
| config.yaml |
| bin/ |
+---------------------------+

Hard link vs symbolic link comparison:

◈ DIAGRAM
Hard Link Symbolic Link
+-----------------------------++-----------------------------+
| Same inode as target || Own inode |
| Link count increments || Link count unchanged |
| Cannot cross filesystems || Can cross filesystems |
| Cannot point to directory || Can point to directory |
| Works if target deleted || Breaks if target deleted |
| ls shows as regular file || ls shows as l, shows target |
+-----------------------------++-----------------------------+

Practical Commands

Bash
## Create a symbolic link
ln -s /opt/app/v2.0 /opt/app/current
## Now /opt/app/current -> /opt/app/v2.0
## Create symlink to a file
ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite
## View symlinks
ls -la /opt/app/current
## lrwxrwxrwx 1 root root 14 Jan 15 /opt/app/current -> /opt/app/v2.0
## Read target path
readlink /opt/app/current
## /opt/app/v2.0
## Resolve full absolute path (follows all symlinks)
readlink -f /opt/app/current
## /opt/app/v2.0
## Update symlink to new version (atomic)
ln -sf /opt/app/v3.0 /opt/app/current
## -f forces overwrite. This is atomic -- no window where link is broken
## Find all symlinks in a directory
find /etc -type l
## Find broken symlinks (target does not exist)
find /etc -xtype l
## Remove a symlink (NOT the target)
rm /opt/app/current
## or
unlink /opt/app/current
## Check if a path is a symlink in a script
if [ -L /opt/app/current ]; then
echo "It is a symlink"
fi

Production deployment pattern:

Bash
## Deploy new version without downtime
SEMVER="v3.0.1"
## Step 1: Extract new version
tar xzf app-${SEMVER}.tar.gz -C /opt/app/
## Step 2: Run database migrations, build steps, etc.
/opt/app/${SEMVER}/scripts/migrate.sh
## Step 3: Atomically update the symlink
ln -sf /opt/app/${SEMVER} /opt/app/current
## systemd service points to /opt/app/current/bin/server
## Reloading the service now uses the new version
systemctl reload myapp
## Rollback: just point current back to previous version
ln -sf /opt/app/v2.9.0 /opt/app/current
systemctl reload myapp

Troubleshooting

Symptom Command What to Look For
Symlink not working readlink -f symlinkname Target path does not exist
Circular symlink readlink -f symlinkname Output: Too many levels of symbolic links
Broken symlinks in /etc find /etc -xtype l Lists all broken symlinks
ls shows wrong size for symlink stat symlinkname Size shown is length of path string, not target
Tip

ln -sf (symbolic, force) is the safest way to update a symlink. It atomically replaces the existing symlink in a single syscall. There is no window where the symlink does not exist, which means services reading through the symlink are never interrupted.

Remember

Deleting a symlink with rm removes only the symlink, not the target. This is almost always what you want. To remove the target, you must rm the actual file or directory, not the symlink pointing to it.

Frequently Asked Questions

What actually breaks if I delete the file a symlink points to?

The symlink itself still exists as a file containing that now-invalid path, but following it returns 'No such file or directory' — this is called a dangling or broken symlink. This is different from a hard link, where the underlying data isn't actually freed until every hard link to it is removed, because hard links point to the same inode rather than to a path string.

When should you use a symlink instead of a hard link, and what's the common gotcha?

Use a symlink when you need to link across filesystems, link to a directory, or want the link to obviously break if the target moves (useful for versioned release directories like `current -> releases/v1.2.3`, a common blue-green deploy pattern). The gotcha: relative symlinks resolve relative to the symlink's own location, not your current working directory, which trips people up when a symlink is moved or accessed from a different path than expected.