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.