Rotate Sonarr / Radarr / Lidarr API keys
When to run this
  • A key leaked into chat, into a doc, or into the homelab-config git repo.
  • A consumer (briefing helper, Prowlarr, etc.) started rejecting auth.
  • Routine rotation.
Fix

The *arr API keys live inside each container’s /config/config.xml (<ApiKey>...</ApiKey>). Rotating = generate a new key, write it back, restart the container, update any consumer that hardcoded the old value.

Generate new keys and rotate one at a time (so you can verify between):

# Generate a 32-char hex key
NEW_KEY=$(openssl rand -hex 16)
echo "$NEW_KEY"

# SSH to pve, edit Sonarr config.xml inside CT100
ssh pve "pct exec 100 -- bash -c 'sed -i.bak \"s|<ApiKey>[^<]*</ApiKey>|<ApiKey>$NEW_KEY</ApiKey>|\" /var/lib/docker/volumes/sonarr_config/_data/config.xml || docker exec sonarr sed -i.bak \"s|<ApiKey>[^<]*</ApiKey>|<ApiKey>$NEW_KEY</ApiKey>|\" /config/config.xml'"

# Restart Sonarr
ssh pve "pct exec 100 -- docker restart sonarr"

# Wait for it to come back, then verify the new key works
sleep 10
ssh pve "pct exec 100 -- curl -s 'http://localhost:8989/api/v3/system/status?apikey=$NEW_KEY'" | head -c 200

Repeat for Radarr (port 7878) and Lidarr (port 8686).

Update consumers

The daily-briefing helper (~/scripts/arr-briefing-data.py) extracts keys at runtime from config.xml via SSH, so it doesn’t need updating after rotation โ€” that’s the whole point of not hardcoding.

What does need updating:

Consumer Where to update
Prowlarr http://192.168.8.100:9696 โ†’ Settings โ†’ Apps โ†’ Sonarr/Radarr/Lidarr โ†’ API Key field
Recyclarr /opt/recyclarr/recyclarr.yml โ€” set new API keys, restart container
Any hardcoded use grep -rln 'd792444549|b117993eb50|3dc17d20ca664' ~/Sync/ED ~/scripts โ€” find leftovers from the May 2026 leak
Bee Hub docs None should hardcode โ€” but search anyway: grep -rln '<ApiKey>' ~/Sync/ED/homelab/bee_hub/content
Verify

Run the briefing helper and confirm it pulls real data:

python3 ~/scripts/arr-briefing-data.py --hours 168 | head -30

Should return JSON with non-empty sonarr.imports, etc. If you get {"error": "api key: ..."}, the key didn’t actually rotate or the container hasn’t restarted.

Record
Update ~/Sync/ED/SECRETS.md with the new keys (or note that they’re extracted at runtime, depending on the entry). If this was a leak-driven rotation, remove the rotation entry from TASKS.md once consumers are updated.