| name | emergency-rescue |
| description | Recover from developer disasters. Use when someone force-pushed to main, leaked credentials in git, ran out of disk space, killed the wrong process, corrupted a database, broke a deploy, locked themselves out of SSH, lost commits after a bad rebase, or hit any other "oh no" moment that needs immediate, calm, step-by-step recovery. |
| metadata | {"clawdbot":{"emoji":"🚨","requires":{"anyBins":["git","bash"]},"os":["linux","darwin","win32"]}} |
Emergency Rescue Kit
Step-by-step recovery procedures for the worst moments in a developer's day. Every section follows the same pattern: diagnose → fix → verify. Commands are non-destructive by default. Destructive steps are flagged.
When something has gone wrong, find your situation below and follow the steps in order.
When to Use
- Someone force-pushed to main and overwrote history
- Credentials were committed to a public repository
- A rebase or reset destroyed commits you need
- Disk is full and nothing works
- A process is consuming all memory or won't die
- A database migration failed halfway through
- A deploy needs to be rolled back immediately
- SSH access is locked out
- SSL certificates expired in production
- You don't know what went wrong, but it's broken
Git Disasters
Force-pushed to main (or any shared branch)
Someone ran git push --force and overwrote remote history.
git reflog show origin/main
git push origin <good-commit-hash>:main --force-with-lease
gh api repos/{owner}/{repo}/events --jq '.[] | select(.type=="PushEvent") | .payload.before'
git log --oneline -10 origin/main
Lost commits after rebase or reset --hard
You ran git rebase or git reset --hard and commits disappeared.
git reflog
git reset --hard <commit-hash-before-disaster>
git cherry-pick <lost-commit-hash>
git fsck --lost-found
ls .git/lost-found/commit/
git show <hash>
git log --oneline -10
Committed to the wrong branch
You made commits on main that should be on a feature branch.
git log --oneline -5
git branch
git branch feature-branch
git reset --hard HEAD~<N>
git checkout feature-branch
git checkout -b feature-branch
git checkout main
git reset --hard origin/main
git log --oneline main -5
git log --oneline feature-branch -5
Merge gone wrong (conflicts everywhere, wrong result)
A merge produced a bad result and you want to start over.
git merge --abort
git reset --hard HEAD~1
git revert -m 1 <merge-commit-hash>
git push
git log --oneline --graph -10
git diff HEAD~1
Corrupted git repository
Git commands fail with "bad object", "corrupt", or "broken link" errors.
git fsck --full
cp -r . ../repo-backup
cd ..
git clone <remote-url> repo-fresh
cp -r repo-backup/path/to/uncommitted/files repo-fresh/
git fsck --full 2>&1 | grep "corrupt\|missing" | awk '{print $NF}'
rm .git/objects/<first-2-chars>/<remaining-hash>
git fetch origin
git fsck --full
git log --oneline -5
Credential Leaks
Secret committed to git (API key, password, token)
A credential is in the git history. Every second counts — automated scrapers monitor public GitHub repos for leaked keys.
aws iam delete-access-key --access-key-id AKIAXXXXXXXXXXXXXXXX --user-name <user>
git rm --cached <file-with-secret>
git add <file>
echo ".env" >> .gitignore
echo "credentials.json" >> .gitignore
git add .gitignore
git filter-repo --path <file-with-secret> --invert-paths
java -jar bfg.jar --delete-files <filename> .
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push origin --force --all
git push origin --force --tags
git log --all -p -S '<the-secret-string>' --diff-filter=A
.env file pushed to public repo
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit -m "Remove .env from tracking"
git filter-repo --path .env --invert-paths
git show HEAD~1:.env 2>/dev/null || git log --all -p -- .env | head -50
cat > .git/hooks/pre-commit << 'HOOK'
if git diff --cached --name-only | grep -qE '\.env$|\.env\.local$|credentials'; then
echo "ERROR: Attempting to commit potential secrets file"
echo "Files: $(git diff --cached --name-only | grep -E '\.env|credentials')"
exit 1
fi
HOOK
chmod +x .git/hooks/pre-commit
Secret visible in CI/CD logs
gh run delete <run-id>
Disk Full Emergencies
System or container disk is full
Nothing works — builds fail, logs can't write, services crash.
df -h
du -sh /* 2>/dev/null | sort -rh | head -20
du -sh /var/log/* | sort -rh | head -10
docker system df
docker system prune -a -f
docker volume prune -f
docker builder prune -a -f
npm cache clean --force
rm -rf ~/.npm/_cacache
pip cache purge
sudo apt-get clean
sudo apt-get autoremove -y
brew cleanup --prune=all
sudo truncate -s 0 /var/log/syslog
sudo truncate -s 0 /var/log/journal/*/*.journal
find /var/log -name "*.log" -size +100M -exec truncate -s 0 {} \;
find . -name "node_modules" -type d -prune - -rf {} + 2>/dev/null
find . -name - d - -rf {} + 2>/dev/null
find . -name - d - -rf {} + 2>/dev/null
find /tmp - f -mtime +7 -delete 2>/dev/null
find / -xdev - f -size +100M - -lh {} \; 2>/dev/null | -k5 -rh | -20
-h
Docker-specific disk full
docker system df -v
docker image prune -f
docker container prune -f
docker builder prune -a -f
docker volume ls -qf dangling=true
docker volume prune -f
docker system prune -a --volumes -f
docker system df
df -h
Process Emergencies
Port already in use
lsof -i :8080
ss -tlnp | grep 8080
lsof -i :8080
netstat -ano | findstr :8080
kill $(lsof -t -i :8080)
kill -9 $(lsof -t -i :8080)
taskkill /PID <pid> /F
docker ps | grep 8080
docker stop <container-id>
lsof -i :8080
Process won't die
ps aux | grep <process-name>
kill <pid>
sleep 5
kill -9 <pid>
ps aux | grep <pid>
kill -SIGCHLD $(ps -o ppid= -p <pid>)
pkill -f <pattern>
pkill -9 -f <pattern>
Out of memory (OOM killed)
dmesg | grep -i "oom\|killed process" | tail -20
journalctl -k | grep -i "oom\|killed" | tail -20
ps aux --sort=-%mem | head -20
free -h
kill $(ps aux --sort=-%mem | awk 'NR==2{print $2}')
sync && echo 3 | sudo tee /proc/sys/vm/drop_caches
sudo swapoff -a && sudo swapon -a
docker run --memory=512m --memory-swap=1g myapp
node --max-old-space-size=512 app.js
free -h
ps aux --sort=-%mem | head -5
Database Emergencies
Failed migration (partially applied)
rails db:migrate:status
python manage.py showmigrations
npx knex migrate:status
npx prisma migrate status
SELECT * FROM schema_migrations ORDER BY version DESC LIMIT 10;
rails db:rollback STEP=1
python manage.py migrate <app_name> <previous_migration_number>
npx knex migrate:rollback
Accidentally dropped a table or database
pg_restore -d mydb /backups/latest.dump -t dropped_table
mysqlbinlog /var/log/mysql/mysql-bin.000001 \
--start-datetime="2026-02-03 10:00:00" \
--stop-datetime="2026-02-03 10:30:00" > recovery.sql
cp /backups/db.sqlite3 ./db.sqlite3
BEGIN;
DROP TABLE users; -- oops
ROLLBACK; -- saved
Database locked / deadlocked
-- Find blocking queries
SELECT pid, usename, state, query, wait_event_type, query_start
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY query_start;
-- Find locks
SELECT blocked_locks.pid AS blocked_pid,
blocking_locks.pid AS blocking_pid,
blocked_activity.query AS blocked_query,
blocking_activity.query AS blocking_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;
-- Kill blocking query
SELECT pg_terminate_backend(<blocking_pid>);
SHOW PROCESSLIST;
SHOW ENGINE INNODB STATUS\G -- Look for "LATEST DETECTED DEADLOCK"
KILL <process_id>;
SELECT 1;
Connection pool exhausted
SELECT count(*), state FROM pg_stat_activity GROUP BY state;
SELECT max_conn, used, max_conn - used AS available
FROM (SELECT count(*) AS used FROM pg_stat_activity) t,
(SELECT setting::int AS max_conn FROM pg_settings WHERE name='max_connections') m;
-- Terminate idle connections older than 5 minutes
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
AND query_start < now() - interval '5 minutes';
SELECT count(*) FROM pg_stat_activity;
Deploy Emergencies
Quick rollback
git log --oneline -5 origin/main
git revert HEAD
git push origin main
docker pull myapp:previous-tag
docker stop myapp-current
docker run -d --name myapp myapp:previous-tag
kubectl rollout undo deployment/myapp
kubectl rollout status deployment/myapp
heroku releases
heroku rollback v<previous-version>
aws ecs update-service --cluster mycluster --service myservice \
--task-definition myapp:<previous-revision>
curl -s -o /dev/null -w "%{http_code}" https://myapp.example.com/health
Container won't start
docker logs <container-id> --tail 100
docker inspect <container-id> | grep -A5 "State"
docker build --platform linux/amd64 -t myapp .
RUN chmod +x /app/entrypoint.sh
docker ps -a | grep <port>
docker stop <conflicting-container>
docker run -it --entrypoint sh myapp
ls -la /app/
docker inspect <container-id> --format='{{json .State.Health}}'
docker run --no-healthcheck myapp
docker inspect <container-id> --format='{{.State.OOMKilled}}'
docker ps
docker logs <container-id> --tail 5
SSL certificate expired
echo | openssl s_client -connect mysite.com:443 -servername mysite.com 2>/dev/null | \
openssl x509 -noout -dates
sudo certbot renew --force-renewal
sudo systemctl reload nginx
sudo cp new-cert.pem /etc/ssl/certs/mysite.pem
sudo cp new-key.pem /etc/ssl/private/mysite.key
sudo nginx -t && sudo systemctl reload nginx
echo '0 9 * * 1 echo | openssl s_client -connect mysite.com:443 2>/dev/null | openssl x509 -checkend 604800 -noout || echo "CERT EXPIRES WITHIN 7 DAYS" | mail -s "SSL ALERT" admin@example.com' | crontab -
curl -sI https://mysite.com | head -5
Access Emergencies
SSH locked out
ssh -vvv user@host
ssh -i ~/.ssh/specific_key user@host
chmod 600 ~/.ssh/id_rsa
chmod 700 ~/.ssh
sudo systemctl start sshd
sudo systemctl status sshd
sudo ufw allow 22/tcp
sudo firewall-cmd --add-service=ssh --permanent && sudo firewall-cmd --reload
ssh -p 2222 user@host
ssh -p 22222 user@host
ping hostname
ssh user@<direct-ip>
sudo fail2ban-client set sshd unbanip <your-ip>
ssh user@host
Lost sudo access
mount -o remount,rw /
usermod -aG sudo <username>
usermod -aG wheel <username>
visudo
reboot
su - other-admin
sudo usermod -aG sudo <locked-user>
Network Emergencies
Nothing connects (total network failure)
ip addr show
ping 127.0.0.1
ip route | grep default
ping <gateway-ip>
ping 8.8.8.8
ping 1.1.1.1
nslookup google.com
dig google.com
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
sudo ip link set eth0 up
sudo dhclient eth0
sudo systemctl restart NetworkManager
sudo systemctl restart networking
sudo systemctl restart systemd-networkd
docker run --rm alpine ping 8.8.8.8
sudo systemctl restart docker
DNS not propagating after change
dig @8.8.8.8 mysite.com
dig @1.1.1.1 mysite.com
dig @ns1.yourdns.com mysite.com
dig mysite.com | grep -i ttl
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder
sudo systemd-resolve --flush-caches
ipconfig /flushdns
echo "93.184.216.34 mysite.com" | sudo tee -a /etc/hosts
dig +short mysite.com
File Emergencies
Accidentally deleted files (not in git)
lsof | grep deleted
cp /proc/<pid>/fd/<fd-number> /path/to/restored-file
sudo extundelete /dev/sda1 --restore-file path/to/file
Wrong permissions applied recursively
find /path -type d -exec chmod 755 {} \;
find /path -type f -exec chmod 644 {} \;
find /path -name "*.sh" -exec chmod 755 {} \;
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 600 ~/.ssh/authorized_keys
chmod 644 ~/.ssh/config
ls -la /path/to/fixed/directory
The Universal Diagnostic
When you don't know what's wrong, run this sequence:
#!/bin/bash
echo "=== DISK ==="
df -h | grep -E '^/|Filesystem'
echo -e "\n=== MEMORY ==="
free -h
echo -e "\n=== CPU / LOAD ==="
uptime
echo -e "\n=== TOP PROCESSES (by CPU) ==="
ps aux --sort=-%cpu | head -6
echo -e "\n=== TOP PROCESSES (by MEM) ==="
ps aux --sort=-%mem | head -6
echo -e "\n=== NETWORK ==="
ping -c 1 -W 2 8.8.8.8 > /dev/null 2>&1 && echo "Internet: OK" || echo "Internet: UNREACHABLE"
ping -c 1 -W 2 $(ip route | awk '/default/{print $3}') > /dev/null 2>&1 && echo "Gateway: OK" || echo "Gateway: UNREACHABLE"
echo -e "\n=== RECENT ERRORS ==="
journalctl -p err --since "1 hour ago" --no-pager | tail -20 2>/dev/null || \
dmesg | tail -20
echo -e "\n=== DOCKER (if running) ==="
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo "Docker not running"
docker system df 2>/dev/null || true
echo -e "\n=== LISTENING PORTS ==="
ss -tlnp 2>/dev/null | head -15 || netstat -tlnp 2>/dev/null | -15
-e
systemctl --failed 2>/dev/null ||
Run it, read the output, then jump to the relevant section above.
Tips
- Revoke credentials before cleaning git history. The moment a secret is pushed publicly, automated scrapers have it within minutes. Cleaning the history is important but secondary to revocation.
git reflog is your undo button. It records every HEAD movement for 30+ days. Lost commits, bad rebases, accidental resets — the reflog has the recovery hash. Learn to read it before you need it.
- Truncate log files, don't delete them.
truncate -s 0 file.log frees disk space instantly while keeping the file handle open. Deleting a log file that a process has open won't free space until the process restarts.
--force-with-lease instead of --force. Always. It fails if someone else has pushed, preventing you from overwriting their work on top of your recovery.
- Every recovery operation should end with verification. Run the diagnostic command, check the output, confirm the fix worked. Don't assume — confirm.
- Docker is the #1 disk space thief on developer machines.
docker system prune -a is almost always safe on development machines and can recover tens of gigabytes.
- Database emergencies: wrap destructive operations in transactions.
BEGIN; DROP TABLE users; ROLLBACK; costs nothing and saves everything. Make it muscle memory.
- When SSH is locked out, every cloud provider has a console escape hatch. AWS Session Manager, GCP browser SSH, Azure Serial Console. Know where yours is before you need it.
- The order matters: diagnose → fix → verify. Skipping diagnosis leads to wrong fixes. Skipping verification leads to false confidence. Follow the sequence every time.
- Keep this skill installed. You won't need it most days. The day you do need it, you'll need it immediately.