A residential IP address is not contractually static, and mine had quietly accumulated five independent hardcoded copies across the fleet. That is not a tidiness problem. It is an outage waiting for an ISP to renumber, because all five break at the same moment and produce symptoms that look completely unrelated to each other and to the cause.
Here is the real list, and the point is how little these have in common:
| Where the address was hardcoded | Symptom when it goes stale |
|---|---|
| Firewall rule, SSH | You cannot log into a public host |
| Firewall rule, monitoring agent port | Monitoring goes CRIT, the host looks down |
| Reverse proxy backend for one API path | A website's contact form starts returning 502 |
| Analytics dashboard client-IP allowlist | The dashboard 404s from your own LAN |
| Cloud provider API key ACL | API calls fail with an auth error |
| A DNS-only A record | Inbound mail or a service stops resolving correctly |
Nobody debugging "the contact form is down" naturally connects it to "the analytics dashboard 404s". You end up diagnosing six unrelated incidents that share one cause, on the day your ISP happened to renumber you.
Detect the real current address every run, compare it to what was last propagated, and reconcile the difference. Keep a state file if you like, but it records what you did, not what is true. That distinction is what lets you report a change rather than silently apply one.
This automation writes firewall rules on public hosts. A wrong value locks you out of your own infrastructure, so it has to be safer than the problem it solves:
# three independent resolvers, at least two must agree
for r in https://api.ipify.org https://ifconfig.me https://icanhazip.com; do
curl -fsS --max-time 5 "$r"; echo
done | sort | uniq -c | sort -rn | head -1
If two of three do not agree on the same address, log it and exit without changing anything. A stale rule is strictly better than a wrong one. Then sanity-check the winner before using it: syntactically valid IPv4, not private, not loopback, not link-local. A resolver that returns an error page instead of an address should never make it into a firewall rule.
This is the part worth stealing even if you automate nothing else.
Keep one access path that a home-IP change cannot affect, and never let the automation touch it. A jump host, a bastion, a relay on a fixed address. It gets its own permanent firewall entry, written by hand, excluded from the script.
Now consider the worst case. The script gets the address wrong, or the ISP renumbers, or the quorum is fooled. Every home-IP rule on the host is now incorrect. You can still get in, because the path you came in on was never in scope. That single exclusion converts "this automation can lock me out of a machine I cannot physically reach" into "this automation can be wrong and I fix it".
Test that claim rather than believing it. Inject a deliberately wrong address from a documentation range, let the reconcile run, and confirm you can still reach the host by the untouched path.
Two ordering rules, both cheap:
Two of the six locations here are deliberately reported by email and never touched, for two different reasons, and both are worth recognising in your own setup:
An automation that quietly skips two of six locations is worse than one that emails you about them.
The single most useful thing this grew was a read-only flag:
home-ip-sync.sh --audit # report drift everywhere, change nothing
home-ip-sync.sh # reconcile, which is what the timer runs
One command that prints the truth across every location is the thing you actually wanted the whole time. It is also how you check the automation is working without waiting for an ISP to renumber you.