Self Healing Systems With Guardrails
Overview
Your database hits its connection limit. An alert fires. You page an on-call DBA. They increase the connection pool size. The database recovers. Everyone goes back to sleep.
But you could have solved this in 30 seconds with an automated fix. The database is now at 70% capacity again, and you'll get the same alert next week. This lesson teaches you to build self-healing systems that detect problems and fix them automatically, but only when it's safe.
Purpose
Some IT problems have known, safe, reversible fixes. Database connection pool exhaustion? Increase pool size. Service instance unhealthy? Remove it from load balancer and restart. Cache cluster member down? Add it back. Disk space low? Clean old logs.
For these problems, waiting for human response adds no value. It just delays recovery.
A self-healing system detects the problem, diagnoses the cause, executes the fix, and verifies the result, all in seconds.
But you need guardrails. Some problems look easy but aren't (scaling down might cause another outage). Some fixes could break things if applied wrong (restarting a shared service might hurt 1000 users). You need limits.
This lesson teaches you to identify which problems are safe to auto-fix, design guardrails that prevent catastrophic mistakes, and build a self-healing system that gives humans control without slowing down recovery.
Why This Matters
A SaaS company had 80 incidents per month caused by "service instance unhealthy." Each incident:
- Alert fires at 2 AM
- On-call engineer wakes up
- Engineer logs in, finds unhealthy instance
- Engineer removes instance from load balancer, restarts it
- Instance comes back healthy
- Service recovers
- Time: 5 minutes (plus sleep disruption)
With auto-remediation:
- Alert fires at 2 AM
- System automatically removes unhealthy instance, restarts it
- Instance comes back healthy
- Service recovers
- Time: 30 seconds
- No human involvement
Result: 80 incidents/month × 5 minutes = 400 minutes = 6.7 hours of on-call work eliminated. Plus 80 times the engineer's sleep wasn't disrupted.
The on-call team's quality of life improved dramatically. Incident response time improved. MTTR dropped from 20 minutes (detect + page + wait for human) to 1 minute (detect + auto-remediate + verify).
Core Concepts
Key insight: Self-healing has five steps
Step 1: Detect
A monitoring system detects an anomaly or failure.
Example: "This service instance is returning 500 errors for 30 seconds straight."
Step 2: Diagnose
Determine the likely cause.
Example: "Instance crashed. Memory usage at 0%, process not running."
Step 3: Decide
Is this safe to auto-remediate? Apply guardrails.
Example: "Process crash + auto-restart safe for stateless service. Proceed."
Step 4: Execute
Run the fix.
Example: "Restart the service instance."
Step 5: Verify
Confirm the fix worked.
Example: "Instance healthy? Yes. Error rate back to normal? Yes. Incident resolved."
Key insight: Guardrails prevent catastrophic mistakes
A guardrail is a check that prevents auto-remediation if conditions aren't met.
Guardrail 1: Blast Radius
How many users affected if the fix fails?
- Restarting a stateless service instance: 0 users affected (load balancer routes around it)
- Restarting a shared database: 1000+ users affected
Safe to auto-fix: Stateless services (blast radius near zero)
Not safe: Shared infrastructure (blast radius high)
Guardrail 2: Reversibility
Can we undo the fix if it makes things worse?
- Removing instance from load balancer: Yes, add it back
- Deleting a database volume: No, it's gone forever
Safe to auto-fix: Reversible operations
Not safe: Irreversible operations
Guardrail 3: Rollback Trigger
If the fix doesn't solve the problem, when do we undo it?
- Fix: Increase database connection pool
- Rollback trigger: If connection pool still exhausted 2 minutes later, revert change
Safe to auto-fix: Has clear rollback trigger
Not safe: No way to detect failure
Guardrail 4: Escalation Condition
If conditions are unusual, escalate to human instead of auto-remediate.
- Normal: Service crashes, restart it (happens once every 3 months)
- Unusual: Service crashes 5 times in 1 hour (something else is wrong)
If crashes >3 in 1 hour: Page human, don't auto-restart.
Guardrail 5: Dependency Check
Does fixing this problem require coordination with another system?
- Restart service-a alone: Safe
- Restart database while transactions in progress: Needs coordination
Check: Are there in-flight transactions? Are dependent services stable? Proceed only if safe.
Key insight: Safe auto-fixes vs. human-approved fixes
Some fixes are safe enough to run without human approval. Others need a human to review and click "OK."
Safe for auto-remediation (no human approval needed):
1. Service instance health check fails → Remove from load balancer + restart
Blast radius: Zero (other instances handle traffic)
Reversible: Yes (add back to load balancer)
Rollback: If metrics don't improve in 30 sec, add back to LB
- Cache node unresponsive → Remove from cluster
Blast radius: None (other cache nodes serve traffic)
Reversible: Yes (add back)
Rollback: If cache hit rate drops >5%, add back - Disk space >95% → Archive old logs
Blast radius: None (just cleanup)
Reversible: Archived logs still accessible
Rollback: N/A (nothing to rollback)
Needs human approval (AI suggests, human approves):
1. Scale up database → Add capacity
Blast radius: High cost if scaled too much
Reversibility: Scale down later, but not instant
Need human to approve: "Is this budget-approved? Do we really need more capacity?"
- Upgrade service version → Deploy new code
Blast radius: Could introduce new bugs
Reversibility: Rollback available but takes time
Need human to approve: "Has this version been tested? Are we ready to deploy?" - Change firewall rules → Modify network access
Blast radius: Security implications
Reversibility: Yes, but takes time
Need human to approve: "Does this comply with security policy?"
Key insight: Feedback loops improve auto-remediation over time
After each auto-remediation:
- Did the fix work?
- Were guardrails appropriate? (Too conservative? Too permissive?)
- Should we have escalated instead?
Auto-remediation: Service-a instance unhealthy → Restart
Did it work? Yes, service recovered in 30 seconds.
Guardrails triggered? No (instance not in cascade failure state)
Result: Success
Learning: This is a low-risk, high-value auto-remediation. Keep it.
Auto-remediation: Database connection pool exhausted → Increase pool size
Did it work? Partially. Pool exhaustion resolved, but latency got worse.
Guardrails triggered? No (upgrade was within limits)
Result: Rollback after 2 minutes (latency worse than original problem)
Learning: Increasing pool size alone isn't the fix. Need to investigate why pool is exhausting.
Auto-remediation: Change to "Alert for investigation" instead of "Auto-increase pool"
Add runbook: "Connection pool exhaustion? Check query times, check deployment logs."
Key insight: Different services need different auto-remediation rules
What's safe for one service might not be safe for another.
Stateless web tier (api-gateway):
- Auto-restart: Safe (traffic routed to other instances)
- Auto-scale down: Safe (load balancer handles it)
- Risky: Don't auto-remediate data consistency issues
Stateful services (payment processor):
- Auto-restart: Risky (in-flight transactions might be lost)
- Auto-scale: Very risky (breaks transaction guarantees)
- Safe: Escalate to human for any remediation
Database:
- Auto-restart: Risky (downtime affects everything)
- Auto-increase pool: Okay (reversible, low risk)
- Auto-run maintenance: Very risky (locks tables)
- Safe: Escalate to human or DBA
Cache cluster:
- Remove unhealthy node: Safe (other nodes handle traffic)
- Auto-repair: Safe (rebalance cache)
- Clear cache: Very risky (performance cliff)
- Safe: Auto-remove, escalate on capacity issues
Practical Use Cases
Use Case 1: Service Instance Auto-Healing
You have 50 instances of api-gateway behind a load balancer. Each instance sometimes crashes. Currently:
- Instance crashes
- Health check fails
- Alert fires
- Engineer gets paged at 2 AM
- Engineer removes instance, restarts it
- Recovery: 5 minutes
Auto-healing workflow:
DETECT
Health check fails 3 times in 10 seconds
Instance marked unhealthy
DIAGNOSE
Check metrics:
- Memory: Normal (512 MB used, 2GB available)
- CPU: Normal (30%)
- Disk: Normal (40% used)
- Logs: Last log entry 5 seconds ago, then silence
Diagnosis: Process crashed, likely OOM killer or segfault
DECIDE (Guardrails)
1. Blast radius: Traffic routed to 49 other instances? Yes.
Customers affected? No (immediate failover).
2. Reversible: Can we add it back if fix fails? Yes.
3. Rollback: If error rate doesn't improve in 30 sec, add back to LB.
4. Escalation: Is this a cascade failure (multiple instances)?
Check: How many instances unhealthy?
Count: 1 of 50. Normal. Proceed.
5. Dependencies: Any in-flight requests to this instance?
Check: LB removed it 30 sec ago. In-flight requests served by timeout/fallback.
Proceed.
Result: All guardrails passed. Auto-remediate.
EXECUTE
Step 1: Remove instance from load balancer (already done by health check)
Step 2: Restart service process (systemctl restart api-gateway)
Step 3: Wait 10 seconds for service to come up
Step 4: Run health check: Healthy?
Response: HTTP 200, latency 50ms
Result: Process restarted successfully
VERIFY
Monitor for 30 seconds:
- Error rate on instance: 0% (healthy)
- Latency: Normal (50-60ms, matching other instances)
- CPU/Memory: Normal (warming up)
Decision: Add instance back to load balancer
Monitor for 5 minutes:
- Error rate: Still 0%
- Latency: Matches other instances
- CPU: Stabilized at 35%
Result: Auto-remediation successful
Incident: Resolved
Human involvement: None
Recovery time: 1 minute (detect 10 sec, restart 10 sec, verify 30 sec, add back 10 sec)
FEEDBACK
Log: "Instance crash → Restart successful"
Historical: "This is 8th instance restart in last month"
"All 8 have recovered without issues"
Learning: Low-risk auto-remediation is working well. Keep it.
If the crash rate increases (e.g., 5 crashes in 1 hour):
Escalation trigger: "Repeated crashes suggest deeper issue (memory leak, bug)"
Action: Page engineer, escalate to investigation
Disable auto-restart: "Something is systematically wrong, need human investigation"
Result: 80 instance restarts per month, zero human involvement, MTTR 1 minute per incident instead of 5+ minutes.
Use Case 2: Cache Cluster Member Recovery
You have a Redis cache cluster with 5 nodes. One node occasionally becomes unresponsive.
Auto-healing:
DETECT
Node-3 timeout on read requests
Health check: Unable to connect
Mark node as unhealthy
DIAGNOSE
Network status: Reachable? Yes, TCP port open.
Memory status: Can't query (node unresponsive)
Assumption: Node might have memory exhaustion, OOM killer killed Redis process
DECIDE (Guardrails)
1. Blast radius: Cache is replicated across 5 nodes. 4/5 still active.
Performance impact: ~20% hit rate reduction, acceptable.
2. Reversible: Can we re-add node if it fails? Yes.
3. Rollback: If cache hit rate drops >30%, add node back.
4. Escalation: Is this a cluster-wide problem?
Check: Other nodes healthy? Yes.
Proceed.
5. Dependencies: Will removing node break replication?
Check: 3-way replication on all keys? Yes.
Removing 1 of 5 is safe.
Result: All guardrails passed.
EXECUTE
Step 1: Remove node-3 from cluster
Step 2: Restart node-3 (systemctl restart redis)
Step 3: Wait 30 seconds for startup
Step 4: Re-add to cluster (redis-cli cluster addslots ...)
Step 5: Wait for rebalance (copy replicas, sync data)
VERIFY
- Cache hit rate: Dropped from 95% to 75% (18% impact, acceptable)
- Latency: Normal (5ms avg)
- Memory on node-3: Back to 60% (healthy)
Monitor rebalance:
- Rebalance complete in 60 seconds
- Cache hit rate: Back to 95%
- All nodes healthy
Result: Node recovered, cluster healthy
FEEDBACK
Log: "Cache node failure → Restart & rejoin successful"
Historical: "This is the 3rd time node-3 has failed"
"Always recovers with restart + rejoin"
Learning: Node-3 might have a memory leak or hardware issue
Action item: Investigate node-3, schedule hardware replacement or OS upgrade
Monitor: Alert if node-3 fails more than 2x per month
Escalate: If 2x per month, page DBA for investigation
Result: Automatic cache failover/recovery, zero human involvement, zero cache misses due to single node failure.
Use Case 3: Disk Space Auto-Remediation
Your systems generate lots of logs. Disk fills up. Alerts fire. Currently manual cleanup.
Auto-healing:
DETECT
Disk usage on /var/log: 92%
Alert threshold: >90%
DIAGNOSE
Logs being written: Yes (100 MB/hour application logs)
Retention policy: 30 days
Oldest logs: 28 days old
Available cleanup: 2 days of logs = 4.8 GB
Disk space needed: 8 GB
Diagnosis: Can cleanup 4.8 GB, need 8 GB. Might still be tight.
DECIDE (Guardrails)
1. Blast radius: If cleanup goes wrong, logs are lost? Yes, but they're backups exist.
Could affect debugging? Slightly (less log history).
2. Reversible: Deleted logs are gone. Not reversible. Only safe if backups exist.
Check: Are logs being backed up to S3? Yes, daily.
Proceed (with caution).
3. Rollback: No rollback for deleted data. Ensure we keep enough old logs for investigation.
Plan: Keep 7 days of logs locally, rest in S3.
4. Escalation: Is this a one-time spike or growing problem?
Check: Disk usage trend (growing 5% per day)
Status: Growing trend, not one-time.
Action: Auto-cleanup buys time, but need to address root cause.
5. Dependencies: Will cleanup affect running applications?
Check: Are apps still writing logs? Yes, might conflict.
Use: Linux "logrotate" to safely rotate/compress, not delete.
Result: Safe to auto-cleanup using logrotate (compress old logs, archive)
EXECUTE
Step 1: Run logrotate (compresses logs older than 3 days)
Step 2: Archive compressed logs to S3
Step 3: Delete archived logs locally
Step 4: Monitor disk space
VERIFY
- Disk usage: 92% → 45% (47% recovered)
- Log write functionality: Normal (still writing new logs)
- Backup: Verified logs in S3
Result: Auto-cleanup successful
FEEDBACK
Log: "Disk space cleanup → Success"
Trend: This is 3rd cleanup in last 2 weeks
Learning: Disk space growing faster than expected
Action item: Investigate log volume growth, reduce unnecessary logging
Monitor: If cleanup needed >2x per week, alert and escalate
Result: Automatic disk space management, prevents "disk full" outages, gives team time to address root cause.
Anti-Patterns
Anti-Pattern 1: "We'll auto-remediate everything"
You set up auto-remediation for all problems. Disk full → Auto-cleanup. Memory leak → Auto-restart. Database slow → Auto-scale. Everything is automatic.
But one day, auto-remediation chains cause a cascade failure. Auto-restart causes other services to fail. Auto-scale starts removing capacity at the wrong time. Without human oversight, you create new problems.
Why it fails: Some problems require judgment. Not all fixes are universally safe. You need guardrails and limits.
How to avoid it: Start conservative. Auto-remediate only obvious, low-risk, reversible fixes. Everything else should alert + escalate to human. Expand auto-remediation slowly as you gain confidence.
Anti-Pattern 2: "Guardrails are hard, skip them"
You want to auto-remediate, but setting up guardrails seems complex. So you skip it. You auto-remediate without checks.
In production, the guardrails would have prevented mistakes. But without them, you scale down too much, delete the wrong thing, or restart a critical service at the wrong time.
Why it fails: Guardrails exist for safety. Without them, automation becomes dangerous.
How to avoid it: Guardrails are simple checks:
- "Is this service stateless?" (Before auto-restart)
- "Are other instances healthy?" (Before removing this instance)
- "Does this operation have a rollback?" (Before executing)
Add 2-3 guardrails per auto-remediation. It's not much extra work.
Anti-Pattern 3: "We don't track auto-remediation success"
You deploy auto-remediation and it runs silently. You don't log successes or failures. You don't review what works vs. what doesn't.
Six months later, you realize one of your auto-remediations has been failing 30% of the time. But you never knew because you didn't track it.
Why it fails: Without tracking, you can't measure effectiveness or detect problems.
How to avoid it: Log every auto-remediation attempt.
- What was the problem?
- What fix did we try?
- Did it succeed?
- Did we have to escalate?
Review monthly. Fix any auto-remediations that succeed <90% of the time.
Anti-Pattern 4: "Auto-remediation removed human oversight entirely"
You auto-remediate service restarts. You don't monitor what happens after the restart. The service comes back up but with different behavior. Nobody notices.
Why it fails: Auto-remediation without verification creates false confidence.
How to avoid it: Always verify after auto-remediation.
- Restart service → Check: Is it healthy?
- Increase capacity → Check: Did latency improve?
- Clean logs → Check: Is the system still functional?
Verification is part of the auto-remediation workflow. It's not optional.
Anti-Pattern 5: "Escalation paths aren't clear"
Your auto-remediation tries to fix a problem and fails. But you don't have a clear escalation path. Does it retry? Does it alert? Does it page someone?
Without clear escalation, failures go unnoticed until they cascade into bigger problems.
Why it fails: Failed auto-remediation is worse than no auto-remediation. You wasted the chance to fix it quickly, and humans don't know they need to take over.
How to avoid it: Every auto-remediation should have:
- Success criteria (how do we know it worked?)
- Timeout (how long to wait before giving up?)
- Escalation action (if it fails, what do we do? Alert? Page? Both?)
Human Judgment Checkpoints
Guardrail coverage: For each auto-remediation, can you articulate 3+ guardrails that prevent it from being dangerous?
Blast radius assessment: If this auto-fix fails, how many users are affected? Is it acceptable?
Reversibility: Can this fix be undone if it makes things worse? Is there a rollback plan?
Escalation clarity: If auto-remediation fails, what happens next? Is it clear?
Feedback tracking: Are you logging auto-remediation attempts and outcomes? Can you measure success rate?
Key Takeaways
Identify what's safe to auto-fix. Stateless service restarts, removing unhealthy nodes, cleaning old logs = safe. Changing database schemas, modifying security rules, deleting data = not safe.
Design guardrails to prevent catastrophic mistakes. Check blast radius, reversibility, rollback triggers, escalation conditions. Guardrails take 10 minutes to implement and prevent disaster.
Verify every auto-remediation. Don't just execute the fix; verify it actually worked. If metrics don't improve, rollback.
Escalate when uncertain. If guardrails aren't satisfied or the problem is unusual, escalate to human instead of proceeding.
Log and track auto-remediation outcomes. You can't improve what you don't measure. Log successes and failures. Review monthly. Fix auto-remediations that fail >10% of the time.
Start conservative, expand slowly. Auto-remediate obvious, low-risk problems first. Prove they work. Then expand to more complex cases as you gain confidence.
Separate auto-remediation from human-approved actions. Some fixes run without approval (restart service). Others need human review and approval (scale up database). Make this distinction clear.
Skill.re