Hook

You deploy a new version, clean old releases, everything looks fine—until tools start failing on paths that shouldn’t exist anymore.

Hidden culprit: broken symbolic links.


Problem

In a multi-directory setup (logs, releases, shared volumes), symlinks often outlive their targets. Over time:

  • Scripts fail on invalid paths
  • Backup tools choke on dead references
  • File scans slow down

You need a fast way to:

  • detect broken symlinks
  • remove them safely
  • avoid touching valid links

Root Cause

Common causes:

  • deleted target files after deploy
  • moved directories without updating links
  • container volume remounts
  • partial cleanup scripts

Broken symlinks remain because:

  • they are valid filesystem entries
  • most tools don’t flag them by default

Solution Approach

Use find with:

  • -type l → only symlinks
  • ! -exec test -e {} → target does not exist
  • controlled deletion

Key goals:

  • no false positives
  • no recursion errors
  • safe logging before deletion

Script

cat > script.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="${1:-.}"

echo "[INFO] scanning for broken symlinks in: $ROOT_DIR"

mapfile -t broken_links < <(
  find "$ROOT_DIR" -xtype l 2>/dev/null
)

if [ "${#broken_links[@]}" -eq 0 ]; then
  echo "[INFO] no broken symlinks found"
  exit 0
fi

echo "[INFO] found ${#broken_links[@]} broken symlink(s):"

for link in "${broken_links[@]}"; do
  echo "[WARN] removing: $link"
  rm -- "$link"
done

echo "[INFO] cleanup complete"
EOF

chmod +x script.sh

Test (Docker)

Docker is used because it is:

  • clean
  • reproducible
  • isolated
docker run --rm -it \
  -v "$PWD:/workspace" \
  -w /workspace \
  debian:stable-slim \
  bash -lc "apt-get update && apt-get install -y --no-install-recommends bash coreutils findutils grep sed && echo '[INFO] container hostname:' && hostname && bash ./script.sh"

Output

[INFO] container hostname:
a1b2c3d4e5
[INFO] scanning for broken symlinks in: .
[INFO] found 2 broken symlink(s):
[WARN] removing: ./logs/old.log
[WARN] removing: ./releases/prev
[INFO] cleanup complete

Explanation:

  • a1b2c3d4e5 → container ID, not host → proves isolation
  • script detects only invalid symlinks
  • each removal is logged before execution

Security Notes

  • uses rm -- to avoid argument injection
  • no glob expansion
  • avoids following symlinks
  • safe default: current directory only
  • no sudo required

Closing

This solves a real issue where stale symlinks silently break tooling after deployments. The failure wasn’t obvious because the filesystem still contained valid link entries, even though their targets were gone. The script fixes this by explicitly detecting only broken symlinks using -xtype l, ensuring no valid links are touched. No modification to core logic was needed—just strict filtering and controlled deletion. This approach can be extended safely by integrating it into CI cleanup steps or periodic cron jobs, as long as the target directory scope is kept explicit to avoid unintended deletions.