
Managing Incidents
- 47 installs
- 426 repo stars
- Updated December 11, 2025
- ancoleman/ai-design-components
managing-incidents is a skill that guides SRE-style incident response, from severity classification and on-call roles to blameless post-mortems.
About
A skill that guides incident response using SRE practices, from detection through blameless post-mortem. A developer or on-call engineer uses it to classify severity, assign incident roles, run escalation and communication protocols, and design on-call rotations. It matters because structured incident handling reduces downtime and turns outages into repeatable learning.
- Classifies incidents SEV0 to SEV3 with response times and defines IC, Comms Lead, SME, and Scribe roles
- Structures the incident lifecycle from detection to blameless post-mortem within 48 hours
- Covers on-call rotation patterns, escalation policy, and internal/external communication cadence
Managing Incidents by the numbers
- 47 all-time installs (skills.sh)
- Ranked #751 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
managing-incidents capabilities & compatibility
- Capabilities
- incident response · severity classification · on call management · postmortem
- Works with
- slack
- Use cases
- devops · debugging
- Runs
- Runs locally
- Pricing
- Free
What managing-incidents says it does
Guide incident response from detection to post-mortem using SRE principles, severity classification, on-call management, blameless culture, and communication protocols.
**Declare Early and Often:** Do not wait for certainty.
IC delegates tasks but does not do hands-on debugging.
npx skills add https://github.com/ancoleman/ai-design-components --skill managing-incidentsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 47 |
|---|---|
| repo stars | ★ 426 |
| Last updated | December 11, 2025 |
| Repository | ancoleman/ai-design-components ↗ |
What it does
Run incident response with severity classification, defined roles, escalation, and blameless post-mortems.
Who is it for?
Teams setting up incident response, on-call rotations, and post-mortem processes
Skip if: Debugging a single local bug with no production impact
When should I use this skill?
Setting up incident processes, designing escalation, or conducting a post-mortem
What you get
A severity model, defined incident roles, escalation policy, and blameless post-mortem process
- severity classification model
- incident role definitions
- escalation policy
By the numbers
- 4 severity levels (SEV0-SEV3)
- 4 incident roles defined (IC, Comms Lead, SME, Scribe)
- post-mortem within 48 hours
Files
Incident Management
Provide end-to-end incident management guidance covering detection, response, communication, and learning. Emphasizes SRE culture, blameless post-mortems, and structured processes for high-reliability operations.
When to Use This Skill
Apply this skill when:
- Setting up incident response processes for a team
- Designing on-call rotations and escalation policies
- Creating runbooks for common failure scenarios
- Conducting blameless post-mortems after incidents
- Implementing incident communication protocols (internal and external)
- Choosing incident management tooling and platforms
- Improving MTTR and incident frequency metrics
Core Principles
Incident Management Philosophy
Declare Early and Often: Do not wait for certainty. Declaring an incident enables coordination, can be downgraded if needed, and prevents delayed response.
Mitigation First, Root Cause Later: Stop customer impact immediately (rollback, disable feature, failover). Debug and fix root cause after stability restored.
Blameless Culture: Assume good intentions. Focus on how systems failed, not who failed. Create psychological safety for honest learning.
Clear Command Structure: Assign Incident Commander (IC) to own coordination. IC delegates tasks but does not do hands-on debugging.
Communication is Critical: Internal coordination via dedicated channels, external transparency via status pages. Update stakeholders every 15-30 minutes during critical incidents.
Severity Classification
Standard severity levels with response times:
SEV0 (P0) - Critical Outage:
- Impact: Complete service outage, critical data loss, payment processing down
- Response: Page immediately 24/7, all hands on deck, executive notification
- Example: API completely down, entire customer base affected
SEV1 (P1) - Major Degradation:
- Impact: Major functionality degraded, significant customer subset affected
- Response: Page during business hours, escalate off-hours, IC assigned
- Example: 15% error rate, critical feature unavailable
SEV2 (P2) - Minor Issues:
- Impact: Minor functionality impaired, edge case bug, small user subset
- Response: Email/Slack alert, next business day response
- Example: UI glitch, non-critical feature slow
SEV3 (P3) - Low Impact:
- Impact: Cosmetic issues, no customer functionality affected
- Response: Ticket queue, planned sprint
- Example: Visual inconsistency, documentation error
For detailed severity decision framework and interactive classifier, see references/severity-classification.md.
Incident Roles
Incident Commander (IC):
- Owns overall incident response and coordination
- Makes strategic decisions (rollback vs. debug, when to escalate)
- Delegates tasks to responders (does NOT do hands-on debugging)
- Declares incident resolved when stability confirmed
Communications Lead:
- Posts status updates to internal and external channels
- Coordinates with stakeholders (executives, product, support)
- Drafts post-incident customer communication
- Cadence: Every 15-30 minutes for SEV0/SEV1
Subject Matter Experts (SMEs):
- Hands-on debugging and mitigation
- Execute runbooks and implement fixes
- Provide technical context to IC
Scribe:
- Documents timeline, actions, decisions in real-time
- Records incident notes for post-mortem reconstruction
Assign roles based on severity:
- SEV2/SEV3: Single responder
- SEV1: IC + SME(s)
- SEV0: IC + Communications Lead + SME(s) + Scribe
For detailed role responsibilities, see references/incident-roles.md.
On-Call Management
Rotation Patterns
Primary + Secondary:
- Primary: First responder
- Secondary: Backup if primary doesn't ack within 5 minutes
- Rotation length: 1 week (optimal balance)
Follow-the-Sun (24/7):
- Team A: US hours, Team B: Europe hours, Team C: Asia hours
- Benefit: No night shifts, improved work-life balance
- Requires: Multiple global teams
Tiered Escalation:
- Tier 1: Junior on-call (common issues, runbook-driven)
- Tier 2: Senior on-call (complex troubleshooting)
- Tier 3: Team lead/architect (critical decisions)
Best Practices
- Rotation length: 1 week per rotation
- Handoff ceremony: 30-minute call to discuss active issues
- Compensation: On-call stipend + time off after major incidents
- Tooling: PagerDuty, Opsgenie, or incident.io
- Limits: Max 2-3 pages per night; escalate if exceeded
Incident Response Workflow
Standard incident lifecycle:
Detection → Triage → Declaration → Investigation
↓
Mitigation → Resolution → Monitoring → Closure
↓
Post-Mortem (within 48 hours)Key Decision Points
When to Declare: When in doubt, declare (can always downgrade severity)
When to Escalate:
- No progress after 30 minutes
- Severity increases (SEV2 → SEV1)
- Specialized expertise needed
When to Close:
- Issue resolved and stable for 30+ minutes
- Monitoring shows all metrics at baseline
- No customer-reported issues
For complete workflow details, see references/incident-workflow.md.
Communication Protocols
Internal Communication
Incident Slack Channel:
- Format:
#incident-YYYY-MM-DD-topic-description - Pin: Severity, IC name, status update template, runbook links
War Room: Video call for SEV0/SEV1 requiring real-time voice coordination
Status Update Cadence:
- SEV0: Every 15 minutes
- SEV1: Every 30 minutes
- SEV2: Every 1-2 hours or at major milestones
External Communication
Status Page:
- Tools: Statuspage.io, Instatus, custom
- Stages: Investigating → Identified → Monitoring → Resolved
- Transparency: Acknowledge issue publicly, provide ETAs when possible
Customer Email:
- When: SEV0/SEV1 affecting customers
- Timing: Within 1 hour (acknowledge), post-resolution (full details)
- Tone: Apologetic, transparent, action-oriented
Regulatory Notifications:
- Data Breach: GDPR requires notification within 72 hours
- Financial Services: Immediate notification to regulators
- Healthcare: HIPAA breach notification rules
For communication templates, see examples/communication-templates.md.
Runbooks and Playbooks
Runbook Structure
Every runbook should include: 1. Trigger: Alert conditions that activate this runbook 2. Severity: Expected severity level 3. Prerequisites: System state requirements 4. Steps: Numbered, executable commands (copy-pasteable) 5. Verification: How to confirm fix worked 6. Rollback: How to undo if steps fail 7. Owner: Team/person responsible 8. Last Updated: Date of last revision
Best Practices
- Executable: Commands copy-pasteable, not just descriptions
- Tested: Run during disaster recovery drills
- Versioned: Track changes in Git
- Linked: Reference from alert definitions
- Automated: Convert manual steps to scripts over time
For runbook templates, see examples/runbooks/ directory.
Blameless Post-Mortems
Blameless Culture Tenets
Assume Good Intentions: Everyone made the best decision with information available.
Focus on Systems: Investigate how processes failed, not who failed.
Psychological Safety: Create environment where honesty is rewarded.
Learning Opportunity: Incidents are gifts of organizational knowledge.
Post-Mortem Process
1. Schedule Review (Within 48 Hours): While memory is fresh
2. Pre-Work: Reconstruct timeline, gather metrics/logs, draft document
3. Meeting Facilitation:
- Timeline walkthrough
- 5 Whys Analysis to identify systemic root causes
- What Went Well / What Went Wrong
- Define action items with owners and due dates
4. Post-Mortem Document:
- Sections: Summary, Timeline, Root Cause, Impact, What Went Well/Wrong, Action Items
- Distribution: Engineering, product, support, leadership
- Storage: Archive in searchable knowledge base
5. Follow-Up: Track action items in sprint planning
For detailed facilitation guide and template, see references/blameless-postmortems.md and examples/postmortem-template.md.
Alert Design Principles
Actionable Alerts Only:
- Every alert requires human action
- Include graphs, runbook links, recent changes
- Deduplicate related alerts
- Route to appropriate team based on service ownership
Preventing Alert Fatigue:
- Audit alerts quarterly: Remove non-actionable alerts
- Increase thresholds for noisy metrics
- Use anomaly detection instead of static thresholds
- Limit: Max 2-3 pages per night
Tool Selection
Incident Management Platforms
PagerDuty:
- Best for: Established enterprises, complex escalation policies
- Cost: $19-41/user/month
- When: Team size 10+, budget $500+/month
Opsgenie:
- Best for: Atlassian ecosystem users, flexible routing
- Cost: $9-29/user/month
- When: Using Atlassian products, budget $200-500/month
incident.io:
- Best for: Modern teams, AI-powered response, Slack-native
- When: Team size 5-50, Slack-centric culture
For detailed tool comparison, see references/tool-comparison.md.
Status Page Solutions
Statuspage.io: Most trusted, easy setup ($29-399/month) Instatus: Budget-friendly, modern design ($19-99/month)
Metrics and Continuous Improvement
Key Incident Metrics
MTTA (Mean Time To Acknowledge):
- Target: < 5 minutes for SEV1
- Improvement: Better on-call coverage
MTTR (Mean Time To Recovery):
- Target: < 1 hour for SEV1
- Improvement: Runbooks, automation
MTBF (Mean Time Between Failures):
- Target: > 30 days for critical services
- Improvement: Root cause fixes
Incident Frequency:
- Track: SEV0, SEV1, SEV2 counts per month
- Target: Downward trend
Action Item Completion Rate:
- Target: > 90%
- Improvement: Sprint integration, ownership clarity
Continuous Improvement Loop
Incident → Post-Mortem → Action Items → Prevention
↑ ↓
└──────────── Fewer Incidents ─────────────┘Decision Frameworks
Severity Classification Decision Tree
Is production completely down or critical data at risk?
├─ YES → SEV0
└─ NO → Is major functionality degraded?
├─ YES → Is there a workaround?
│ ├─ YES → SEV1
│ └─ NO → SEV0
└─ NO → Are customers impacted?
├─ YES → SEV2
└─ NO → SEV3Use interactive classifier: python scripts/classify-severity.py
Escalation Matrix
For detailed escalation guidance, see references/escalation-matrix.md.
Mitigation vs. Root Cause
Prioritize Mitigation When:
- Active customer impact ongoing
- Quick fix available (rollback, disable feature)
Prioritize Root Cause When:
- Customer impact already mitigated
- Fix requires careful analysis
Default: Mitigation first (99% of cases)
Anti-Patterns to Avoid
- Delayed Declaration: Waiting for certainty before declaring incident
- Skipping Post-Mortems: "Small" incidents still provide learning
- Blame Culture: Punishing individuals prevents systemic learning
- Ignoring Action Items: Post-mortems without follow-through waste time
- No Clear IC: Multiple people leading creates confusion
- Alert Fatigue: Noisy, non-actionable alerts cause on-call to ignore pages
- Hands-On IC: IC should delegate debugging, not do it themselves
Implementation Checklist
Phase 1: Foundation (Week 1)
- [ ] Define severity levels (SEV0-SEV3)
- [ ] Choose incident management platform
- [ ] Set up basic on-call rotation
- [ ] Create incident Slack channel template
Phase 2: Processes (Weeks 2-3)
- [ ] Create first 5 runbooks for common incidents
- [ ] Set up status page
- [ ] Train team on incident response
- [ ] Conduct tabletop exercise
Phase 3: Culture (Weeks 4+)
- [ ] Conduct first blameless post-mortem
- [ ] Establish post-mortem cadence
- [ ] Implement MTTA/MTTR dashboards
- [ ] Track action items in sprint planning
Phase 4: Optimization (Months 3-6)
- [ ] Automate incident declaration
- [ ] Implement runbook automation
- [ ] Monthly disaster recovery drills
- [ ] Quarterly incident trend reviews
Integration with Other Skills
Observability: Monitoring alerts trigger incidents → Use incident-management for response
Disaster Recovery: DR provides recovery procedures → Incident-management provides operational response
Security Incident Response: Similar process with added compliance/forensics
Infrastructure-as-Code: IaC enables fast recovery via automated rebuild
Performance Engineering: Performance incidents trigger response → Performance team investigates post-mitigation
Examples and Templates
Runbook Templates:
examples/runbooks/database-failover.mdexamples/runbooks/cache-invalidation.mdexamples/runbooks/ddos-mitigation.md
Post-Mortem Template:
examples/postmortem-template.md- Complete blameless post-mortem structure
Communication Templates:
examples/communication-templates.md- Status updates, customer emails
On-Call Handoff:
examples/oncall-handoff-template.md- Weekly handoff format
Integration Scripts:
examples/integrations/pagerduty-slack.pyexamples/integrations/statuspage-auto-update.pyexamples/integrations/postmortem-generator.py
Scripts
Interactive Severity Classifier:
python scripts/classify-severity.pyAsks questions to determine appropriate severity level based on impact and urgency.
Further Reading
Books:
- Google SRE Book: "Postmortem Culture" (Chapter 15)
- "The Phoenix Project" by Gene Kim
- "Site Reliability Engineering" (Full book)
Online Resources:
- Atlassian: "How to Run a Blameless Postmortem"
- PagerDuty: "Incident Response Guide"
- Google SRE: "Postmortem Culture: Learning from Failure"
Standards:
- Incident Command System (ICS) - FEMA standard adapted for tech
- ITIL Incident Management - Traditional IT service management
Incident Communication Templates
Overview
Communication templates for internal coordination and external customer updates during incidents. Customize for your organization while maintaining clarity and consistency.
---
Internal Status Update Template (Slack)
Use: Post in incident Slack channel every 15-30 minutes during active incidents
**[TIMESTAMP] Update #[N] - [Status]**
**Current Status:** [Investigating | Identified | Implementing Fix | Monitoring | Resolved]
**Issue:** [One-sentence description of what's broken]
**Impact:** [Who/what is affected, e.g., "~5,000 customers unable to complete checkout"]
**Progress:** [What we've learned/done since last update]
**Next Steps:** [What we're doing next]
**ETA:** [Expected resolution time or "Unknown - investigating"]
**Next Update:** [TIMESTAMP + 15/30 min]Example:
14:45 PST Update #3 - Implementing Fix
Current Status: Implementing Fix
Issue: API experiencing elevated error rates (15% of requests failing)
Impact: ~5,000 customers unable to complete checkout
Progress: Root cause identified - database connection pool exhausted. Mitigation deployed: increased pool size from 100 to 200 connections. Error rate dropped from 15% to 5%.
Next Steps: Monitoring error rate trend. Investigating secondary issue with retry logic causing thundering herd.
ETA: Expect full resolution within 30 minutes
Next Update: 15:00 PST---
External Status Page Update Template
Use: Post to public status page (Statuspage.io, Instatus, etc.)
Status: Investigating
**[Timestamp] Investigating - [Component Name]**
We are investigating reports of [brief issue description]. Customers may experience [specific impact, e.g., "slow page loads" or "checkout errors"]. We are working to identify the root cause and will provide updates as we learn more.Example:
Dec 3, 14:20 PST Investigating - API
We are investigating reports of elevated API error rates. Customers may experience errors when attempting to complete checkout. We are working to identify the root cause and will provide updates every 15 minutes.---
Status: Identified
**[Timestamp] Identified - [Component Name]**
We have identified the issue: [brief technical description in plain language]. We are implementing a fix and expect resolution within [timeframe]. Customers are still experiencing [impact].Example:
Dec 3, 14:30 PST Identified - API
We have identified the issue: our database connection pool reached capacity during higher-than-expected traffic. We are increasing the pool size and expect resolution within 30 minutes. Customers are still experiencing occasional checkout errors (~5% failure rate, down from 15%).---
Status: Monitoring
**[Timestamp] Monitoring - [Component Name]**
The fix has been applied and [metric] has returned to normal levels. We are monitoring the situation to ensure stability before marking this incident as resolved.Example:
Dec 3, 15:00 PST Monitoring - API
The fix has been applied and API error rates have returned to normal levels (<0.1%). We are monitoring the situation for the next 30 minutes to ensure stability before marking this incident as resolved.---
Status: Resolved
**[Timestamp] Resolved - [Component Name]**
This incident has been resolved. [Component] is operating normally. Customers should no longer experience [impact]. We will be conducting a post-mortem to prevent recurrence and will share findings soon.Example:
Dec 3, 15:45 PST Resolved - API
This incident has been resolved. The API is operating normally. Customers should no longer experience checkout errors. We will be conducting a post-mortem to prevent recurrence and will share a summary with affected customers within 24 hours.---
Customer Email Template (Post-Incident)
Use: Send to affected customers after SEV0/SEV1 incidents
Timing: Within 24 hours of resolution
Subject Line: [Resolved] [Service Name] Incident on [Date]
Subject: [Resolved] API Outage on December 3, 2025
Dear [Customer Name / Customers],
We experienced a service disruption on [Date] from [Start Time] to [End Time] [Timezone]. During this time, [describe customer-facing impact in plain language].
**What Happened:**
[2-3 sentences explaining what broke in non-technical terms]
**Impact:**
- Duration: [X hours/minutes]
- Affected Users: [Approximately X customers or "All users"]
- Affected Functionality: [Specific features that were unavailable]
- Data: [Confirm no data loss or describe scope if any]
**How We Resolved It:**
[1-2 sentences describing the fix applied]
**What We're Doing to Prevent This:**
We take incidents seriously and are implementing the following improvements:
1. [Prevention measure 1]
2. [Prevention measure 2]
3. [Prevention measure 3]
**We Sincerely Apologize:**
We understand how disruptive service interruptions are and apologize for the inconvenience. If you have any questions or concerns, please contact our support team at [support email/phone].
Thank you for your patience and continued trust.
Sincerely,
[Name]
[Title]
[Company]Example:
Subject: [Resolved] API Outage on December 3, 2025
Dear Customers,
We experienced an API outage on December 3, 2025, from 2:15 PM to 3:45 PM PST. During this time, approximately 5,000 customers were unable to complete checkout transactions.
What Happened:
Our database connection pool reached capacity during higher-than-expected holiday traffic. This caused our API to reject requests and display error messages during checkout.
Impact:
- Duration: 90 minutes (2:15 PM - 3:45 PM PST)
- Affected Users: Approximately 5,000 customers
- Affected Functionality: Checkout and payment processing
- Data: No customer data was lost or compromised
How We Resolved It:
We increased our database connection pool capacity and implemented circuit breaker logic to prevent similar cascading failures.
What We're Doing to Prevent This:
1. Adding proactive monitoring alerts for connection pool saturation (before errors occur)
2. Implementing load testing for 2x traffic scenarios before all releases
3. Auto-scaling our database connection pool to handle traffic spikes
We Sincerely Apologize:
We understand how frustrating this disruption was, especially during the holiday shopping season. We apologize for the inconvenience and appreciate your patience. If you have any questions, please contact support@example.com or call 1-800-XXX-XXXX.
Thank you for your continued trust.
Sincerely,
Jane Smith
VP Engineering
Example Company---
Executive Brief Template (Internal)
Use: Brief C-level stakeholders during/after SEV0 incidents
Format: Email or Slack DM
Subject: Executive Brief: [Incident Title]
**Incident:** [One-line description]
**Status:** [Ongoing | Resolved]
**Severity:** [SEV0 | SEV1]
**Duration:** [X hours/minutes]
**Business Impact:**
- Customers Affected: [Number or percentage]
- Revenue Impact: [$ estimate or "Investigating"]
- SLA Breach: [Yes/No]
- PR/Media Risk: [High/Medium/Low]
**Current Status:**
[2-3 sentences on where we are in incident response]
**Root Cause:**
[If identified: brief explanation | If not: "Still investigating"]
**Resolution:**
[If ongoing: "Expected resolution: [timeframe]" | If resolved: "Resolved at [time]"]
**Customer Communication:**
[Status page updated? Customer email sent? Planned?]
**Next Steps:**
1. [Action 1]
2. [Action 2]
3. [Post-mortem scheduled for [date]]
**Contact:** [IC or Incident Manager name and Slack handle]Example:
Executive Brief: API Outage - Dec 3, 2025
Incident: API error rates reached 15%, preventing checkout
Status: Resolved
Severity: SEV1
Duration: 90 minutes (2:15 PM - 3:45 PM PST)
Business Impact:
- Customers Affected: ~5,000 (15% of active users during incident)
- Revenue Impact: Estimated $12,000 in lost transactions
- SLA Breach: Yes - 99.9% monthly SLA now at 99.85%
- PR/Media Risk: Low - proactive customer communication sent
Root Cause:
Database connection pool exhausted during 2x traffic spike (holiday shopping). Contributing factors: no proactive monitoring, aggressive retry logic amplified impact.
Resolution:
Resolved at 3:45 PM PST. Connection pool size increased, circuit breaker implemented.
Customer Communication:
- Status page updated throughout (6 updates)
- Post-incident email sent to all affected customers
- Support team briefed on expected follow-up questions
Next Steps:
1. Add proactive connection pool monitoring (by Dec 10)
2. Load test all releases for 2x traffic (checklist updated)
3. Post-mortem scheduled for Dec 5 at 10am
Contact: @bob (IC) in #incident-2025-12-03-api-outage---
On-Call Handoff Template
Use: Weekly on-call rotation handoff (e.g., every Monday)
Format: Slack post in #on-call channel or shared Google Doc
# On-Call Handoff - Week of [Date]
**Outgoing On-Call:** @[name]
**Incoming On-Call:** @[name]
**Handoff Date:** [Date and Time]
---
## Active Issues
### [Issue 1 Title]
- **Status:** [Monitoring | Investigating | Resolved]
- **Severity:** [SEV2/SEV3 typically for active issues]
- **Description:** [1-2 sentences]
- **Next Steps:** [What incoming on-call should do]
- **Resources:** [Link to ticket, dashboard, runbook]
### [Issue 2 Title]
[Same format as above]
**No Active Issues:** All systems normal ✅
---
## Upcoming Changes
### [Deploy/Maintenance 1]
- **Date/Time:** [When it's happening]
- **Impact:** [Expected customer impact or "No downtime expected"]
- **Risk Level:** [High/Medium/Low]
- **IC Assigned:** [Yes/No - who?]
- **Runbook:** [Link if applicable]
### [Deploy/Maintenance 2]
[Same format as above]
---
## New Runbooks / Updates
- **Added:** [Runbook name and link]
- **Updated:** [Runbook name and what changed]
**No Changes:** All runbooks current ✅
---
## System Health Notes
**Overall:** [Green/Yellow/Red - brief assessment]
- **[System 1]:** [Status and any notes]
- **[System 2]:** [Status and any notes]
- **[System 3]:** [Status and any notes]
**Monitoring Issues:**
- [Any dashboards down, alert issues, blind spots]
---
## Questions / Clarifications
**Q:** [Any questions from incoming on-call]
**A:** [Answers from outgoing on-call]
---
## Access Verification
- [ ] VPN connected
- [ ] PagerDuty app configured
- [ ] AWS/GCP console access verified
- [ ] Database access verified
- [ ] Monitoring dashboards accessible
- [ ] Slack notifications enabled
---
## Contact Info
**Outgoing On-Call:** @[handle] | [phone if needed]
**Incoming On-Call:** @[handle] | [phone if needed]
**Escalation:** @[team-lead-handle] | @[senior-oncall-handle]
**Handoff Complete:** ✅ [Timestamp when both parties confirm]Example:
# On-Call Handoff - Week of Dec 3-10, 2025
Outgoing On-Call: @alice
Incoming On-Call: @bob
Handoff Date: Monday, Dec 3, 10:00 AM PST
---
## Active Issues
### Database Replica Lag Spike
- Status: Monitoring
- Severity: SEV3
- Description: Replica lag spiking to 30 seconds every few hours, cause unknown. Primary unaffected.
- Next Steps: Monitor replica lag dashboard, escalate to @db-team if lag > 60 seconds
- Resources: https://monitoring.example.com/dashboard/db-replication
No other active issues. All systems normal ✅
---
## Upcoming Changes
### API Deploy v2.3.0
- Date/Time: Wednesday, Dec 5, 2:00 PM PST
- Impact: No downtime expected (rolling deploy)
- Risk Level: Medium (includes database migration)
- IC Assigned: Yes - @alice on standby
- Runbook: https://wiki.example.com/deploys/api-v2.3.0
### Database Maintenance Window
- Date/Time: Saturday, Dec 7, 2:00 AM PST
- Impact: Read-only mode for 30 minutes
- Risk Level: Low (routine maintenance)
- IC Assigned: No (automated)
- Runbook: RB-DB-003 (maintenance procedures)
---
## New Runbooks / Updates
- Updated: RB-DB-001 (Database Failover) - New DNS process added
- Added: RB-CACHE-001 (Redis Cache Invalidation) - New runbook for cache issues
---
## System Health Notes
Overall: Green (replica lag being monitored)
- API: Green - 99.99% uptime this week
- Database: Yellow - Replica lag spikes (under investigation)
- Cache: Green - All metrics normal
- CDN: Green - No issues
Monitoring Issues:
- None - all dashboards and alerts operational
---
## Questions / Clarifications
Q: What should I do if replica lag hits 60 seconds?
A: Page @db-team immediately. If they don't respond in 5 min, consider failing over to replica (use RB-DB-001).
Q: Is the v2.3.0 deploy high risk?
A: Medium risk due to DB migration. I'll be on Slack Wednesday afternoon as backup IC if needed.
---
## Access Verification
- [X] VPN connected
- [X] PagerDuty app configured
- [X] AWS console access verified
- [X] Database access verified
- [X] Monitoring dashboards accessible
- [X] Slack notifications enabled
---
## Contact Info
Outgoing On-Call: @alice | 555-0123 (cell)
Incoming On-Call: @bob | 555-0456 (cell)
Escalation: @team-lead | @senior-oncall
Handoff Complete: ✅ Dec 3, 10:15 AM PST---
Regulatory Notification Template
Use: Required for data breaches, security incidents (GDPR, HIPAA, PCI)
Consult Legal Team Before Sending
GDPR Data Breach Notification (72-hour requirement)
Subject: Data Breach Notification - [Company Name]
[Regulatory Authority Name]
[Address]
Date: [Date]
Re: Personal Data Breach Notification
Dear [Authority Name],
[Company Name] is writing to notify you of a personal data breach pursuant to Article 33 of the General Data Protection Regulation (GDPR).
1. Description of the Breach:
[Date and time breach discovered]
[Nature of the breach - unauthorized access, data loss, etc.]
2. Categories and Approximate Number of Data Subjects Concerned:
[E.g., "Approximately 5,000 EU residents"]
3. Categories and Approximate Number of Personal Data Records Concerned:
[E.g., "Names, email addresses, hashed passwords"]
4. Likely Consequences of the Breach:
[Describe potential impact on individuals]
5. Measures Taken to Address the Breach:
[Actions taken to stop breach and mitigate impact]
6. Measures Taken to Mitigate Adverse Effects:
[E.g., "Password reset required for all affected accounts"]
7. Contact Point:
[Name, title, email, phone of Data Protection Officer]
We will provide further information as our investigation progresses.
Sincerely,
[Name]
[Title]
[Company]
[Contact Information]---
Incident Closure Announcement
Use: Post in incident Slack channel when IC declares incident resolved
**🎉 INCIDENT RESOLVED 🎉**
**Incident:** [Title]
**Duration:** [X hours/minutes]
**Resolved At:** [Timestamp]
**Summary:**
[1-2 sentences on what happened and how it was fixed]
**Final Metrics:**
- MTTA: [X minutes]
- MTTR: [X minutes]
- Customer Impact: [Brief summary]
**Next Steps:**
1. Post-mortem scheduled: [Date/Time]
2. Action items will be tracked in [Jira/GitHub/etc.]
3. Customer communication: [Sent/Scheduled]
**Thank You:**
Thank you to everyone who responded! Special thanks to:
- IC: @[name]
- SMEs: @[name1], @[name2]
- Communications: @[name]
**Post-Mortem Doc:** [Link to post-mortem draft]
---
This incident channel will remain open for 24 hours, then will be archived.---
Tips for Effective Communication
Dos
✅ Be Clear: Use simple language, avoid jargon ✅ Be Honest: Don't over-promise on ETAs ✅ Be Timely: Update on schedule even if "no new information" ✅ Be Empathetic: Acknowledge customer frustration ✅ Be Specific: "15% error rate" vs. "some errors"
Don'ts
❌ Don't Speculate: Stick to confirmed facts ❌ Don't Blame: External communication should never blame individuals or teams ❌ Don't Over-Commit: Avoid "This will never happen again" ❌ Don't Ghost: Even "no update" is an update ❌ Don't Use Acronyms: Customers don't know "SEV1" or "MTTR"
---
Further Reading
- Atlassian: "How to Communicate During an Incident"
- PagerDuty: "Incident Communication Best Practices"
- Statuspage.io: "Writing Great Incident Updates"
#!/usr/bin/env python3
"""
PagerDuty to Slack Integration
Automatically creates incident Slack channels when PagerDuty incidents are triggered.
Uses PagerDuty webhooks to receive incident events and Slack API to create channels.
Dependencies:
pip install slack-sdk requests
Configuration:
Set environment variables:
- SLACK_BOT_TOKEN: Slack bot token with channels:manage scope
- PAGERDUTY_WEBHOOK_SECRET: Secret for validating PagerDuty webhooks
Usage:
# Run webhook server
python pagerduty-slack.py
# Configure PagerDuty webhook:
# URL: https://your-server.com/pagerduty-webhook
# Events: incident.triggered, incident.acknowledged, incident.resolved
"""
import os
import json
import hmac
import hashlib
from datetime import date
from typing import Optional
from flask import Flask, request, jsonify
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
app = Flask(__name__)
# Configuration
SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN")
PAGERDUTY_WEBHOOK_SECRET = os.environ.get("PAGERDUTY_WEBHOOK_SECRET")
slack_client = WebClient(token=SLACK_BOT_TOKEN)
def verify_pagerduty_signature(request_body: bytes, signature: str) -> bool:
"""
Verify PagerDuty webhook signature for security.
Args:
request_body: Raw request body
signature: X-PagerDuty-Signature header value
Returns:
True if signature is valid, False otherwise
"""
if not PAGERDUTY_WEBHOOK_SECRET:
return True # Skip verification if secret not configured
expected = hmac.new(
PAGERDUTY_WEBHOOK_SECRET.encode(),
request_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
def create_incident_channel(incident: dict) -> Optional[str]:
"""
Create Slack channel for incident.
Args:
incident: PagerDuty incident object
Returns:
Channel ID if created successfully, None otherwise
"""
incident_id = incident["id"]
incident_number = incident["incident_number"]
title = incident["title"].lower().replace(" ", "-")[:50]
# Generate channel name: #incident-YYYY-MM-DD-###-title
channel_name = f"incident-{date.today()}-{incident_number}-{title}"
# Slack channel names must be lowercase, no special chars
channel_name = "".join(c for c in channel_name if c.isalnum() or c == "-")
try:
# Create channel
response = slack_client.conversations_create(
name=channel_name,
is_private=False
)
channel_id = response["channel"]["id"]
# Post initial incident details
severity = incident.get("urgency", "unknown")
service = incident.get("service", {}).get("summary", "Unknown")
slack_client.chat_postMessage(
channel=channel_id,
text=f"🚨 *Incident Declared: SEV{severity.upper()}*",
blocks=[
{
"type": "header",
"text": {
"type": "plain_text",
"text": f"🚨 Incident #{incident_number}"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": f"*Severity:*\n{severity.upper()}"
},
{
"type": "mrkdwn",
"text": f"*Service:*\n{service}"
},
{
"type": "mrkdwn",
"text": f"*PagerDuty:*\n<{incident['html_url']}|View Incident>"
},
{
"type": "mrkdwn",
"text": f"*Status:*\nInvestigating"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*Description:*\n{incident['title']}"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Next Steps:*\n1. Incident Commander (IC) to be assigned\n2. Begin investigation\n3. Post status updates every 15-30 minutes"
}
}
]
)
# Pin important information
slack_client.pins_add(
channel=channel_id,
timestamp=response["ts"]
)
print(f"Created incident channel: {channel_name} (ID: {channel_id})")
return channel_id
except SlackApiError as e:
print(f"Error creating Slack channel: {e.response['error']}")
return None
def post_incident_update(incident: dict, event_type: str):
"""
Post incident status update to existing channel.
Args:
incident: PagerDuty incident object
event_type: Type of event (acknowledged, resolved, etc.)
"""
# Find incident channel by searching for incident number
# In production, store channel_id → incident_id mapping in database
incident_number = incident["incident_number"]
try:
# Search for channel
channels_response = slack_client.conversations_list()
channel_id = None
for channel in channels_response["channels"]:
if f"incident-" in channel["name"] and str(incident_number) in channel["name"]:
channel_id = channel["id"]
break
if not channel_id:
print(f"Channel not found for incident #{incident_number}")
return
# Post update based on event type
if event_type == "incident.acknowledged":
slack_client.chat_postMessage(
channel=channel_id,
text=f"✅ Incident acknowledged by {incident.get('assigned_to', 'on-call')}"
)
elif event_type == "incident.resolved":
slack_client.chat_postMessage(
channel=channel_id,
text="🎉 *INCIDENT RESOLVED*",
blocks=[
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🎉 INCIDENT RESOLVED"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"Incident #{incident_number} has been resolved.\n\n*Next Steps:*\n1. Post-mortem to be scheduled within 48 hours\n2. Action items will be tracked\n3. Thank you to all responders!"
}
}
]
)
# Archive channel after 24 hours (would need scheduled job)
# For now, just add note
slack_client.chat_postMessage(
channel=channel_id,
text="_This channel will be archived in 24 hours._"
)
except SlackApiError as e:
print(f"Error posting update: {e.response['error']}")
@app.route("/pagerduty-webhook", methods=["POST"])
def pagerduty_webhook():
"""
Handle PagerDuty webhook events.
Events:
- incident.triggered: Create Slack channel
- incident.acknowledged: Post acknowledgment
- incident.resolved: Post resolution
"""
# Verify signature
signature = request.headers.get("X-PagerDuty-Signature", "")
if not verify_pagerduty_signature(request.get_data(), signature):
return jsonify({"error": "Invalid signature"}), 401
data = request.get_json()
# Handle webhook event
for message in data.get("messages", []):
event_type = message.get("event")
incident = message.get("incident", {})
if event_type == "incident.triggered":
create_incident_channel(incident)
elif event_type in ["incident.acknowledged", "incident.resolved"]:
post_incident_update(incident, event_type)
return jsonify({"status": "ok"}), 200
@app.route("/health", methods=["GET"])
def health_check():
"""Health check endpoint."""
return jsonify({"status": "healthy"}), 200
if __name__ == "__main__":
# Run webhook server
port = int(os.environ.get("PORT", 5000))
app.run(host="0.0.0.0", port=port, debug=False)
#!/usr/bin/env python3
"""
Post-Mortem Auto-Generator
Automatically generates post-mortem documents from incident data.
Pulls timeline from Slack, metrics from monitoring, and creates pre-filled post-mortem.
Dependencies:
pip install slack-sdk google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client
Configuration:
Set environment variables:
- SLACK_BOT_TOKEN: Slack bot token
- GOOGLE_CREDENTIALS_PATH: Path to Google service account credentials
Usage:
python postmortem-generator.py --incident-channel incident-2025-12-03-api-outage
"""
import os
import argparse
from datetime import datetime
from typing import List, Dict
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
from google.oauth2 import service_account
from googleapiclient.discovery import build
# Configuration
SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN")
GOOGLE_CREDS_PATH = os.environ.get("GOOGLE_CREDENTIALS_PATH", "credentials.json")
POSTMORTEM_TEMPLATE_ID = os.environ.get("POSTMORTEM_TEMPLATE_ID") # Google Doc template
slack_client = WebClient(token=SLACK_BOT_TOKEN)
def get_incident_timeline(channel_name: str) -> List[Dict]:
"""
Extract incident timeline from Slack channel history.
Args:
channel_name: Name of incident Slack channel
Returns:
List of timeline events with timestamps and messages
"""
try:
# Find channel by name
channels = slack_client.conversations_list()
channel_id = None
for channel in channels["channels"]:
if channel["name"] == channel_name:
channel_id = channel["id"]
break
if not channel_id:
raise ValueError(f"Channel not found: {channel_name}")
# Get channel history
history = slack_client.conversations_history(channel=channel_id, limit=1000)
timeline = []
for message in reversed(history["messages"]):
# Skip bot messages and reactions
if message.get("subtype") in ["bot_message", "message_replied"]:
continue
timestamp = datetime.fromtimestamp(float(message["ts"]))
user_id = message.get("user", "system")
# Get user name
user_name = "System"
if user_id != "system":
try:
user_info = slack_client.users_info(user=user_id)
user_name = user_info["user"]["real_name"]
except:
user_name = user_id
timeline.append({
"time": timestamp.strftime("%H:%M"),
"event": message.get("text", ""),
"actor": user_name
})
return timeline
except SlackApiError as e:
print(f"Error fetching Slack history: {e.response['error']}")
return []
def extract_incident_metadata(channel_name: str) -> Dict:
"""
Extract incident metadata from channel name and pinned messages.
Args:
channel_name: Incident channel name
Returns:
Dict with incident ID, severity, date, etc.
"""
# Parse channel name: incident-YYYY-MM-DD-###-title
parts = channel_name.replace("incident-", "").split("-")
incident_date = f"{parts[0]}-{parts[1]}-{parts[2]}" if len(parts) >= 3 else "Unknown"
return {
"incident_id": f"INC-{incident_date}-001",
"date": incident_date,
"severity": "SEV1", # Default, can be extracted from pinned message
"channel_name": channel_name
}
def create_postmortem_document(incident_data: Dict, timeline: List[Dict]) -> str:
"""
Create Google Doc from post-mortem template with incident data.
Args:
incident_data: Incident metadata
timeline: Timeline events
Returns:
URL of created Google Doc
"""
# Authenticate with Google
creds = service_account.Credentials.from_service_account_file(
GOOGLE_CREDS_PATH,
scopes=["https://www.googleapis.com/auth/documents",
"https://www.googleapis.com/auth/drive"]
)
docs_service = build("docs", "v1", credentials=creds)
drive_service = build("drive", "v3", credentials=creds)
# Copy template
if POSTMORTEM_TEMPLATE_ID:
file_metadata = {
"name": f"Post-Mortem: {incident_data['incident_id']}",
"parents": [] # Configure folder ID if needed
}
copied_file = drive_service.files().copy(
fileId=POSTMORTEM_TEMPLATE_ID,
body=file_metadata
).execute()
doc_id = copied_file["id"]
else:
# Create new document
doc = docs_service.documents().create(body={
"title": f"Post-Mortem: {incident_data['incident_id']}"
}).execute()
doc_id = doc["documentId"]
# Populate document with incident data
requests = []
# Insert incident summary
requests.append({
"insertText": {
"location": {"index": 1},
"text": f"""Post-Mortem: {incident_data['incident_id']}
Incident ID: {incident_data['incident_id']}
Severity: {incident_data['severity']}
Date: {incident_data['date']}
Status: Draft
## Timeline
"""
}
})
# Insert timeline
timeline_text = "\n".join([
f"{event['time']} - {event['event']} (@{event['actor']})"
for event in timeline
])
requests.append({
"insertText": {
"location": {"index": 200}, # After summary
"text": timeline_text + "\n\n"
}
})
# Execute batch update
docs_service.documents().batchUpdate(
documentId=doc_id,
body={"requests": requests}
).execute()
# Get shareable link
doc_url = f"https://docs.google.com/document/d/{doc_id}/edit"
print(f"✅ Post-mortem document created: {doc_url}")
return doc_url
def schedule_postmortem_meeting(incident_data: Dict, doc_url: str):
"""
Schedule post-mortem meeting in Google Calendar (placeholder).
Args:
incident_data: Incident metadata
doc_url: URL to post-mortem document
"""
# In production, use Google Calendar API to create meeting
print(f"📅 Reminder: Schedule post-mortem meeting within 48 hours")
print(f" Suggested attendees: IC, SMEs, stakeholders")
print(f" Document: {doc_url}")
def main():
"""Generate post-mortem from incident Slack channel."""
parser = argparse.ArgumentParser(description="Generate post-mortem document")
parser.add_argument(
"--incident-channel",
required=True,
help="Incident Slack channel name (e.g., incident-2025-12-03-api-outage)"
)
args = parser.parse_args()
print(f"🔍 Fetching incident data from #{args.incident_channel}...")
# Extract incident metadata
incident_data = extract_incident_metadata(args.incident_channel)
# Get incident timeline from Slack
timeline = get_incident_timeline(args.incident_channel)
if not timeline:
print("❌ Failed to fetch incident timeline")
return
print(f"✅ Extracted {len(timeline)} timeline events")
# Create post-mortem document
doc_url = create_postmortem_document(incident_data, timeline)
# Schedule post-mortem meeting
schedule_postmortem_meeting(incident_data, doc_url)
print("\n✅ Post-mortem generation complete!")
print(f" Document: {doc_url}")
print(f" Next steps:")
print(f" 1. Review and complete sections: Root Cause, Impact, What Went Well/Wrong")
print(f" 2. Schedule post-mortem meeting (within 48 hours)")
print(f" 3. Define action items with owners and due dates")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Statuspage.io Auto-Update Integration
Automatically posts status page updates from incident Slack channel messages.
Monitors incident channels and posts updates to Statuspage.io when Communications Lead posts updates.
Dependencies:
pip install slack-sdk requests
Configuration:
Set environment variables:
- SLACK_BOT_TOKEN: Slack bot token
- STATUSPAGE_API_KEY: Statuspage.io API key
- STATUSPAGE_PAGE_ID: Statuspage.io page ID
Usage:
python statuspage-auto-update.py
"""
import os
import re
from typing import Optional
from slack_sdk import WebClient
from slack_sdk.socket_mode import SocketModeClient
from slack_sdk.socket_mode.request import SocketModeRequest
from slack_sdk.socket_mode.response import SocketModeResponse
import requests
# Configuration
SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN")
SLACK_APP_TOKEN = os.environ.get("SLACK_APP_TOKEN")
STATUSPAGE_API_KEY = os.environ.get("STATUSPAGE_API_KEY")
STATUSPAGE_PAGE_ID = os.environ.get("STATUSPAGE_PAGE_ID")
STATUSPAGE_API_BASE = f"https://api.statuspage.io/v1/pages/{STATUSPAGE_PAGE_ID}"
def parse_status_update(message_text: str) -> Optional[dict]:
"""
Parse status update message from Slack.
Expected format:
[TIMESTAMP] Update #N - [Status]
Current Status: [status]
Issue: [description]
Impact: [impact]
Progress: [progress]
ETA: [eta]
Args:
message_text: Slack message text
Returns:
Parsed update dict or None if not a valid status update
"""
# Check if message matches status update format
if not re.match(r"\d{2}:\d{2}.*Update #\d+", message_text):
return None
lines = message_text.split("\n")
# Extract status
status_line = [l for l in lines if l.startswith("Current Status:")]
if not status_line:
return None
status_text = status_line[0].split(":", 1)[1].strip()
# Map status to Statuspage.io status
status_mapping = {
"Investigating": "investigating",
"Identified": "identified",
"Monitoring": "monitoring",
"Resolved": "resolved"
}
status = status_mapping.get(status_text, "investigating")
# Extract issue description
issue_line = [l for l in lines if l.startswith("Issue:")]
issue = issue_line[0].split(":", 1)[1].strip() if issue_line else "Service disruption"
# Extract impact
impact_line = [l for l in lines if l.startswith("Impact:")]
impact = impact_line[0].split(":", 1)[1].strip() if impact_line else "Under investigation"
# Combine into status message
message = f"{issue}\n\nImpact: {impact}"
return {
"status": status,
"message": message
}
def get_or_create_incident(name: str) -> str:
"""
Get existing incident or create new one on Statuspage.io.
Args:
name: Incident name (from Slack channel name)
Returns:
Incident ID
"""
headers = {
"Authorization": f"OAuth {STATUSPAGE_API_KEY}",
"Content-Type": "application/json"
}
# Check for unresolved incidents
response = requests.get(
f"{STATUSPAGE_API_BASE}/incidents/unresolved",
headers=headers
)
if response.status_code == 200:
incidents = response.json()
# Find incident matching name
for incident in incidents:
if name.lower() in incident.get("name", "").lower():
return incident["id"]
# Create new incident
incident_data = {
"incident": {
"name": name,
"status": "investigating",
"impact_override": "major",
"body": "We are investigating reports of service disruption.",
"components": {}, # Auto-detect or configure
"component_ids": []
}
}
response = requests.post(
f"{STATUSPAGE_API_BASE}/incidents",
headers=headers,
json=incident_data
)
if response.status_code == 201:
return response.json()["id"]
raise Exception(f"Failed to create incident: {response.text}")
def update_statuspage(incident_id: str, status: str, message: str):
"""
Post update to Statuspage.io incident.
Args:
incident_id: Statuspage incident ID
status: Incident status (investigating, identified, monitoring, resolved)
message: Update message
"""
headers = {
"Authorization": f"OAuth {STATUSPAGE_API_KEY}",
"Content-Type": "application/json"
}
update_data = {
"incident_update": {
"body": message,
"status": status
}
}
response = requests.patch(
f"{STATUSPAGE_API_BASE}/incidents/{incident_id}",
headers=headers,
json=update_data
)
if response.status_code == 200:
print(f"✅ Status page updated: {status}")
else:
print(f"❌ Failed to update status page: {response.text}")
def handle_slack_message(client: SocketModeClient, req: SocketModeRequest):
"""
Handle Slack message events.
Monitors incident channels for status updates from Communications Lead
and automatically posts to Statuspage.io.
"""
if req.type == "events_api":
# Acknowledge request
response = SocketModeResponse(envelope_id=req.envelope_id)
client.send_socket_mode_response(response)
event = req.payload["event"]
# Only process messages in incident channels
if event.get("type") == "message" and "incident-" in event.get("channel_name", ""):
message_text = event.get("text", "")
# Parse status update
update = parse_status_update(message_text)
if not update:
return
# Get or create Statuspage incident
channel_name = event.get("channel_name", "")
incident_title = channel_name.replace("incident-", "").replace("-", " ").title()
try:
incident_id = get_or_create_incident(incident_title)
update_statuspage(incident_id, update["status"], update["message"])
# React to message to confirm posted
slack_client = WebClient(token=SLACK_BOT_TOKEN)
slack_client.reactions_add(
channel=event["channel"],
timestamp=event["ts"],
name="white_check_mark"
)
except Exception as e:
print(f"Error updating status page: {e}")
def main():
"""
Start Slack event listener for automatic status page updates.
"""
if not all([SLACK_APP_TOKEN, STATUSPAGE_API_KEY, STATUSPAGE_PAGE_ID]):
print("Error: Missing required environment variables")
print("Required: SLACK_APP_TOKEN, STATUSPAGE_API_KEY, STATUSPAGE_PAGE_ID")
return
client = SocketModeClient(
app_token=SLACK_APP_TOKEN,
web_client=WebClient(token=SLACK_BOT_TOKEN)
)
client.socket_mode_request_listeners.append(handle_slack_message)
print("🚀 Statuspage auto-update bot started")
print("Monitoring incident channels for status updates...")
client.connect()
# Keep running
from slack_sdk.socket_mode.builtin import SocketModeClient
import time
while True:
time.sleep(1)
if __name__ == "__main__":
main()
On-Call Handoff Template
Overview
Structure weekly on-call handoffs to ensure smooth transition of responsibilities, context transfer, and continuity of operational awareness. Conduct handoff ceremony at consistent time (e.g., Monday 10am) with 30-minute dedicated meeting.
---
Pre-Handoff Checklist
Outgoing On-Call:
- [ ] Review all incidents from past week
- [ ] Identify ongoing issues requiring attention
- [ ] Check monitoring for degraded services
- [ ] Review upcoming maintenance windows
- [ ] Prepare notes on known issues
- [ ] Update runbooks if needed
Incoming On-Call:
- [ ] Verify PagerDuty/Opsgenie account active
- [ ] Test alert delivery (phone, SMS, push)
- [ ] Review on-call procedures and escalation paths
- [ ] Have laptop and VPN ready
- [ ] Review recent incident post-mortems
---
Handoff Meeting Template
Meeting Details
Date: [YYYY-MM-DD] Outgoing On-Call: [@outgoing-person] Incoming On-Call: [@incoming-person] Duration: 30 minutes Video Call: [Zoom/Meet link]
---
Section 1: Week in Review
Incident Summary
Total Incidents: [N]
| Date | Severity | Duration | Issue | Status |
|---|---|---|---|---|
| Dec 1 | SEV1 | 45 min | API error spike | Resolved |
| Dec 2 | SEV2 | 2 hours | Cache degradation | Resolved |
| Dec 3 | SEV3 | 30 min | UI layout bug | Resolved |
Key Incidents to Discuss:
Incident 1: [Brief Title]
- When: [Date/Time]
- Severity: [SEV0/SEV1/SEV2/SEV3]
- Impact: [Customer/service impact]
- Root Cause: [Technical cause]
- Resolution: [How resolved]
- Follow-Up Actions: [Open action items]
- Post-Mortem: [Link to doc if applicable]
Incident 2: [Brief Title]
- When: [Date/Time]
- Severity: [SEV0/SEV1/SEV2/SEV3]
- Impact: [Customer/service impact]
- Root Cause: [Technical cause]
- Resolution: [How resolved]
- Follow-Up Actions: [Open action items]
---
Section 2: Ongoing Issues
Active Monitoring Items
Issue 1: [Brief Description]
- Status: [Monitoring | Under Investigation | Escalated]
- Since: [Date issue started]
- Impact: [Current impact, if any]
- Context: [Background information]
- Next Steps: [What to watch for or do]
- Escalation: [Who to contact if worsens]
Example:
Issue: Database Replication Lag Elevated
Status: Monitoring
Since: Nov 30, 2025
Impact: No customer impact, but lag 2-3 seconds vs normal 0.5s
Context: Traffic increased 20% due to holiday shopping. Replication keeping up but slower than usual. DBA team aware.
Next Steps: Monitor lag metric. Escalate if > 5 seconds or query errors appear.
Escalation: @db-sre-oncall
Alert: database-replication-lag-warning
Dashboard: https://monitoring.example.com/dashboard/database-health---
Known Issues (Non-Urgent)
Issue 1: [Brief Description]
- Impact: [Minor/cosmetic/low priority]
- Ticket: [JIRA-123]
- Notes: [Any context]
Example:
Issue: Slow Dashboard Load Times (EU Region)
Impact: Dashboards load in 3-5 seconds vs normal 1-2 seconds. Non-critical feature.
Ticket: PERF-456
Notes: Performance team investigating. No customer complaints. CDN optimization planned for Sprint 42.---
Section 3: Scheduled Maintenance
Upcoming Maintenance Windows
Maintenance 1: [Brief Description]
- When: [Date/Time with timezone]
- Duration: [Expected duration]
- Impact: [Customer impact, if any]
- Team Responsible: [@team]
- Your Role: [What on-call needs to do]
- Runbook: [Link to maintenance procedure]
Example:
Maintenance: Database Upgrade (PostgreSQL 14 → 15)
When: Dec 7, 2025 02:00-04:00 PST (Sunday night)
Duration: 2 hours (max)
Impact: 5-minute read-only mode during cutover. No downtime expected.
Team Responsible: @db-sre-team
Your Role: Monitor alerts during window. Be available for escalation if issues occur.
Runbook: https://wiki.example.com/database-upgrade-procedure
Slack Channel: #maintenance-dec7-db-upgrade
Rollback Plan: Available in runbook (revert to snapshot if critical issues)---
Section 4: System Health Overview
Service Status Dashboard
Core Services:
- API: ✅ Healthy (error rate < 0.1%, latency p99 < 200ms)
- Database: ⚠️ Monitoring (replication lag 2-3s, see "Ongoing Issues")
- Cache (Redis): ✅ Healthy (hit rate 94%)
- Job Queue: ✅ Healthy (processing rate normal)
- CDN: ✅ Healthy (cache hit rate 97%)
Dashboard: https://monitoring.example.com/dashboard/overview
---
Recent Configuration Changes
Change 1: [Description]
- When: [Date/Time]
- Who: [@person]
- What Changed: [Brief description]
- Monitoring: [What to watch]
Example:
Change: Increased API Rate Limits for Enterprise Tier
When: Dec 2, 2025 14:00 PST
Who: @backend-team
What Changed: Increased rate limit from 1000 → 5000 req/min for enterprise customers
Monitoring: Watch API error rate and database connection pool utilization
Risk: Potential for database overload if multiple enterprise customers spike simultaneously
Rollback: Feature flag "enterprise_rate_limit_v2" can be disabled instantly---
Section 5: Key Contacts and Resources
Escalation Paths
By Service Area:
| Service | Primary Contact | Secondary Contact | Escalation |
|---|---|---|---|
| API | @backend-oncall | @backend-lead | @vp-engineering |
| Database | @db-sre-oncall | @senior-dba | @infrastructure-director |
| Frontend | @frontend-oncall | @frontend-lead | @vp-engineering |
| Infrastructure | @platform-oncall | @platform-lead | @infrastructure-director |
Executive Escalation (SEV0 Only):
- VP Engineering: @vp-eng (Slack + Phone: +1-555-0100)
- CTO: @cto (Phone: +1-555-0101)
- CEO: @ceo (Phone: +1-555-0102) - Only for business-critical SEV0
---
Key Resources
Runbooks:
- Master Index: https://wiki.example.com/runbooks
- Database Failover:
runbooks/database-failover.md - Cache Invalidation:
runbooks/cache-invalidation.md - DDoS Mitigation:
runbooks/ddos-mitigation.md
Dashboards:
- Overall Health: https://monitoring.example.com/dashboard/overview
- API Metrics: https://monitoring.example.com/dashboard/api
- Database Health: https://monitoring.example.com/dashboard/database
- Infrastructure: https://monitoring.example.com/dashboard/infra
Documentation:
- Incident Response Guide: https://wiki.example.com/incident-response
- Severity Classification: https://wiki.example.com/severity-levels
- On-Call Procedures: https://wiki.example.com/oncall-guide
Slack Channels:
- #incidents (automated incident creation)
- #oncall-general (on-call team coordination)
- #oncall-handoff (handoff notes archive)
- #database-operations (database team)
- #platform-engineering (infrastructure team)
---
Section 6: Tips and Lessons Learned
What Went Well This Week
Example:
- Quick response to API incident (MTTA 3 minutes, MTTR 45 minutes)
- Database team proactive about replication lag, no surprises
- Runbook for cache invalidation worked perfectly, no escalation neededChallenges and Learnings
Example:
- Alert fatigue: Received 12 non-actionable "warning" alerts for disk space that were false positives
→ Action: Opened ticket to adjust thresholds (INFRA-789)
- Cache invalidation runbook missing step for verifying propagation to all regions
→ Action: Updated runbook with verification step
- Unclear when to escalate for replication lag - waited longer than needed
→ Learning: Escalate to @db-sre-oncall if lag > 5 seconds, don't waitAdvice for Incoming On-Call
Example:
- Keep an eye on database replication lag metric - it's been elevated all week
- EU region performance slower than usual, but no customer complaints yet
- Database maintenance Sunday night - be available 2am-4am PST
- New runbook for DDoS mitigation just added - review before your shift
- Coffee machine on floor 3 is broken, use floor 5 :)---
Section 7: Action Items
Follow-Up from Last Week
- [ ] [ITEM-1] - [Description] - Owner: [@person] - Due: [Date]
- [ ] [ITEM-2] - [Description] - Owner: [@person] - Due: [Date]
Example:
- [x] Update cache-invalidation runbook with verification step - Owner: @alice - Due: Dec 4
- [ ] Adjust disk space alert thresholds to reduce false positives - Owner: @bob - Due: Dec 10
- [ ] Schedule disaster recovery drill for Q1 2026 - Owner: @oncall-lead - Due: Dec 15---
New Action Items from Handoff
- [ ] [NEW-ITEM-1] - [Description] - Owner: [@person] - Due: [Date]
- [ ] [NEW-ITEM-2] - [Description] - Owner: [@person] - Due: [Date]
---
Section 8: Questions and Clarifications
Incoming On-Call Questions:
Q: [Question from incoming on-call] A: [Answer from outgoing on-call]
Example:
Q: What's the typical response time for database team during off-hours?
A: Usually 5-10 minutes. They're very responsive. Use @db-sre-oncall in Slack first, escalate to phone if no response in 10 min.
Q: Is there a runbook for the replication lag issue?
A: Yes - RB-DB-003 (Database Replication Repair). But consult @db-sre-oncall before executing since they're already monitoring it.
Q: What's the threshold for escalating to VP Engineering?
A: SEV0 incidents lasting > 30 minutes, or any data breach/security incident, or if customer-facing impact is severe and we need executive communication.---
Post-Handoff Actions
Outgoing On-Call:
- [ ] Post handoff notes in #oncall-handoff Slack channel
- [ ] Update on-call schedule if any coverage gaps
- [ ] Archive handoff document in wiki: https://wiki.example.com/oncall-handoffs/YYYY-MM-DD
- [ ] Complete on-call feedback survey (if applicable)
Incoming On-Call:
- [ ] Acknowledge receipt of handoff
- [ ] Review all linked dashboards and runbooks
- [ ] Test alert delivery immediately after handoff
- [ ] Introduce yourself in #oncall-general Slack channel
- [ ] Review action items and clarify ownership
---
Handoff Confirmation
Outgoing On-Call Confirmation:
I confirm that I have:
- Reviewed all incidents from the past week
- Briefed @[incoming] on ongoing issues
- Provided context on upcoming maintenance
- Shared key contacts and escalation paths
- Answered all questions to the best of my ability
Signed: [@outgoing-person], [Date]Incoming On-Call Confirmation:
I confirm that I have:
- Reviewed all handoff materials
- Asked clarifying questions
- Tested alert delivery (PagerDuty/phone/SMS)
- Reviewed key dashboards and runbooks
- Understand escalation procedures
Ready to assume on-call responsibility.
Signed: [@incoming-person], [Date]---
Template Usage Notes
Customization
Adapt sections based on organization size:
- Small teams (< 10 engineers): Focus on Sections 1-3, 5-6
- Medium teams (10-50 engineers): Use all sections
- Large teams (50+ engineers): Add service-specific handoffs
Frequency:
- Weekly rotation: Full handoff every week
- Daily rotation: Abbreviated handoff (Sections 1-2, 6 only)
- Follow-the-sun: Twice daily brief handoff (15 minutes)
---
Handoff Best Practices
Timing:
- Schedule at consistent time (e.g., Monday 10am)
- Allow 30 minutes minimum
- No multitasking - give full attention
Documentation:
- Archive handoff notes for future reference
- Update runbooks based on learnings
- Track common issues for process improvement
Communication:
- Use video call, not just Slack
- Screen share dashboards and runbooks
- Encourage questions - no question is too basic
Cultural:
- Recognize outgoing on-call's efforts
- Share positive incidents (fast resolution, good teamwork)
- Acknowledge challenges without blame
---
Related Templates
- Incident Response:
communication-templates.md - Post-Mortem:
postmortem-template.md - Runbooks:
runbooks/directory
---
Changelog
| Date | Version | Changes | Author |
|---|---|---|---|
| 2025-12-05 | 1.0 | Initial template | @incident-management-team |
---
Contact
Owner: SRE Team Slack: #oncall-general Documentation: https://wiki.example.com/oncall-guide
For template improvements: Open PR or post in #sre-team
Post-Mortem: [Incident Title]
Incident ID: INC-YYYY-MM-DD-### Severity: [SEV0 | SEV1 | SEV2] Duration: [X hours/minutes] ([Start Time] - [End Time] [Timezone]) Date: [Month Day, Year] Author: [Name] Attendees: [List all post-mortem meeting participants] Status: [Draft | Final | Archived]
---
Incident Summary
In 2-3 sentences, describe what happened in plain language accessible to non-technical readers.
Example:
On December 3, 2025, our API experienced elevated error rates (15% failure) due to database connection pool exhaustion. Approximately 5,000 customers were unable to complete checkout during the 90-minute incident. The issue was resolved by increasing the connection pool size and implementing circuit breaker logic.
---
Impact Assessment
Customer Impact
- Users Affected: [Number or percentage] ([X% of active users])
- Duration: [Hours/minutes of customer-facing impact]
- Affected Functionality: [Specific features unavailable]
- Data Loss: [None | Describe scope if any]
- Geographic Spread: [Regions affected: US-East, EU, Global, etc.]
Business Impact
- Revenue Impact: [Estimated $ amount or "Negligible"]
- SLA Breach: [Yes/No - If yes, describe SLA violation]
- Support Load: [Number of tickets filed during incident]
- Reputation: [Social media mentions, news coverage if any]
- Contractual: [Customer SLA violations, penalties if applicable]
Technical Impact
- Services Affected: [List all affected services/components]
- Infrastructure: [Database, API servers, network, etc.]
- Dependencies: [Third-party services impacted, upstream/downstream]
- Data Integrity: [Data loss, corruption, or inconsistency]
---
Timeline
All times in [Timezone]
Provide a chronological record of events from detection to full recovery.
| Time | Event | Actor |
|---|---|---|
| HH:MM | First alert: "[Alert Name]" fired in [monitoring system] | [System] |
| HH:MM | @[engineer] acknowledged alert, began investigation | @[engineer] |
| HH:MM | Incident declared [severity], IC @[name] assigned | @[IC] |
| HH:MM | Root cause identified: [Brief description] | @[SME] |
| HH:MM | Mitigation applied: [Action taken] | @[SME] |
| HH:MM | [Metric] improved from [X%] to [Y%] | [System] |
| HH:MM | Secondary issue discovered: [Description] | @[SME] |
| HH:MM | Additional mitigation: [Action taken] | @[SME] |
| HH:MM | All metrics returned to baseline | [System] |
| HH:MM | IC declared incident resolved after [X min] stability | @[IC] |
Key Milestones:
- Detection: [HH:MM] (Time from incident start to first alert)
- Acknowledgment: [HH:MM] (MTTA: [X minutes])
- Mitigation: [HH:MM] (Time to stop customer impact)
- Resolution: [HH:MM] (MTTR: [X minutes])
---
Root Cause Analysis
Primary Root Cause
What was the direct trigger or failure point that caused the incident?
Example:
Database connection pool size (100 connections) was insufficient for peak traffic load (2x normal due to holiday shopping season).
Contributing Factors
What conditions worsened the impact or prevented faster resolution?
1. [Factor 1: e.g., Aggressive retry logic caused "thundering herd" when database slowed] 2. [Factor 2: e.g., No monitoring/alerting on connection pool saturation] 3. [Factor 3: e.g., Recent code deploy increased database query frequency by 20%] 4. [Factor 4: e.g., Runbook for database scaling was outdated]
5 Whys Analysis
Dig deeper to identify systemic root causes:
1. Why did [incident occur]?
- [Answer]
2. Why [answer to #1]?
- [Answer]
3. Why [answer to #2]?
- [Answer]
4. Why [answer to #3]?
- [Answer]
5. Why [answer to #4]?
- [Answer]
Systemic Root Cause: [The process, tool, or cultural issue revealed by 5 Whys]
Example:
Release process lacks load testing requirement for traffic spike scenarios, allowing performance-impacting changes to reach production without validation.
---
What Went Well
Celebrate effective incident response behaviors to reinforce positive actions.
1. [Positive aspect]: [Description]
- Example: "Fast detection: Alert fired within 2 minutes of error rate increase"
2. [Positive aspect]: [Description]
- Example: "Clear IC leadership: @bob immediately took IC role and delegated tasks"
3. [Positive aspect]: [Description]
- Example: "Effective communication: Status updates posted every 15 minutes"
4. [Positive aspect]: [Description]
- Example: "Quick mitigation: Connection pool increase deployed within 10 minutes"
5. [Positive aspect]: [Description]
- Example: "No data loss: All customer data remained intact"
---
What Went Wrong
Identify improvement areas, focusing on process, tools, or training gaps (NOT individuals).
Important: Frame as learning opportunities, not blame. Use "How did the system fail?" not "Who failed?"
1. [Improvement area]: [Description]
- Example: "No proactive monitoring: Connection pool saturation not monitored, only reactive error alerts"
2. [Improvement area]: [Description]
- Example: "Untested retry logic: Circuit breaker not tested under database degradation scenarios"
3. [Improvement area]: [Description]
- Example: "Missing load testing: Recent deploy not load tested for 2x traffic spike"
4. [Improvement area]: [Description]
- Example: "Communication delay: First status page update 30 minutes into incident"
5. [Improvement area]: [Description]
- Example: "Outdated runbook: Database scaling runbook referenced deprecated configuration"
---
Action Items
Define specific, actionable improvements with clear owners and due dates.
Requirements for each action item:
- Specific: Clear task description, not vague ("Improve monitoring" ❌ | "Add connection pool saturation alert" ✅)
- Owned: Single person responsible (not "team")
- Dated: Realistic due date
- Prioritized: High/Medium/Low based on impact
| Action | Owner | Due Date | Priority | Status | Notes |
|---|---|---|---|---|---|
| [Action item 1] | @[name] | YYYY-MM-DD | High | Not Started | [Optional context] |
| [Action item 2] | @[name] | YYYY-MM-DD | High | In Progress | [Optional context] |
| [Action item 3] | @[name] | YYYY-MM-DD | Medium | Complete | [Optional context] |
| [Action item 4] | @[name] | YYYY-MM-DD | Medium | Not Started | [Optional context] |
| [Action item 5] | @[name] | YYYY-MM-DD | Low | Not Started | [Optional context] |
Example Action Items:
| Action | Owner | Due Date | Priority | Status |
|---|---|---|---|---|
| Add connection pool saturation alert (threshold: 80%, Slack notification) | @alice | 2025-12-10 | High | In Progress |
| Implement circuit breaker library for database queries | @bob | 2025-12-15 | High | Not Started |
| Add "load test for 2x traffic" step to release checklist | @charlie | 2025-12-05 | Medium | Complete |
| Create runbook for database connection pool scaling | @alice | 2025-12-12 | Medium | Not Started |
| Send post-incident email to affected customers (draft in examples/communication-templates.md) | @comms | 2025-12-04 | High | Complete |
| Review and update all database runbooks for accuracy | @db-team | 2025-12-20 | Low | Not Started |
---
Lessons Learned
Key takeaways for organizational knowledge and future prevention.
1. [Lesson]: [Description of insight gained]
- Example: "Connection pool metrics are critical leading indicators: Monitor saturation before errors occur"
2. [Lesson]: [Description of insight gained]
- Example: "Retry logic needs circuit breakers: Prevent thundering herd in database degradation scenarios"
3. [Lesson]: [Description of insight gained]
- Example: "Load testing must include traffic spike scenarios: 2x traffic should be standard test case"
4. [Lesson]: [Description of insight gained]
- Example: "Proactive monitoring > Reactive alerting: Leading indicators (saturation) better than lagging (errors)"
5. [Lesson]: [Description of insight gained]
- Example: "Runbooks are living documents: Quarterly review and update cycle needed"
---
Supporting Data
Attach relevant logs, metrics, screenshots for reference.
Graphs and Metrics
- API Error Rate: [Link to Grafana dashboard or screenshot]
- Database Connection Pool: [Link to metrics]
- Traffic Patterns: [Link to traffic graphs]
- CPU/Memory/Disk: [Link to infrastructure metrics]
Log Excerpts
[Paste relevant log entries showing error states, warnings, or key events]
Example:
2025-12-03 14:15:32 ERROR: Database connection pool exhausted (max: 100, active: 100)
2025-12-03 14:15:33 ERROR: Checkout API failed: connection timeoutConfiguration Changes
[Show before/after configuration if relevant]
Example:
Before: connection_pool_size = 100
After: connection_pool_size = 200Alerts Fired
- 14:15:32 PST:
API-Error-Rate-High(Datadog) - 14:17:45 PST:
Database-Connection-Pool-Low(manual check, not automated) - 14:30:15 PST:
API-Error-Rate-Normal(recovery)
---
Prevention Measures
Summarize how action items will prevent recurrence.
Short-Term (Completed within 2 weeks): 1. [Immediate fix applied, e.g., "Connection pool size increased to 200"] 2. [Monitoring added, e.g., "Alert added for 80% pool saturation"]
Medium-Term (Completed within 1-2 months): 1. [Process improvement, e.g., "Load testing requirement added to release checklist"] 2. [Tool improvement, e.g., "Circuit breaker library implemented"]
Long-Term (Completed within 3-6 months): 1. [Architectural change, e.g., "Auto-scaling connection pool implementation"] 2. [Training, e.g., "Team training on database performance tuning"]
---
Communication
Internal Communication
Incident Channel: [#incident-YYYY-MM-DD-topic] Status Updates: [Number of updates posted, cadence] Stakeholders Notified: [Engineering, product, support, leadership]
External Communication
Status Page: [Link to public status page updates] Customer Email: [Sent? Yes/No - If yes, link to email draft] Social Media: [Twitter, LinkedIn updates if applicable] Press/Media: [Any media coverage or press releases]
Regulatory Notifications
Required: [Yes/No - GDPR, HIPAA, PCI, etc.] Notification Sent: [Yes/No - If yes, date and recipient] Documentation: [Link to regulatory notification records]
---
Follow-Up Actions
Action Item Tracking
- Review Cadence: [Weekly in sprint planning]
- Tracking Tool: [Jira, GitHub Issues, Linear, etc.]
- Completion Target: [Date by which all high-priority items should be complete]
Post-Mortem Distribution
- [X] Engineering team (via Slack #engineering)
- [X] Product team (via email)
- [X] Support team (via Slack #support)
- [X] Leadership (via email)
- [X] Knowledge base (archived in Confluence/Wiki)
Post-Mortem Review
Next Post-Mortem Review: [Quarterly incident review meeting] Discussion: [Will this post-mortem be discussed in team meeting? When?]
---
Related Incidents
Previous Similar Incidents:
- [INC-YYYY-MM-DD-###: Brief description, link]
- [INC-YYYY-MM-DD-###: Brief description, link]
Pattern Detection: [If this is a recurring issue, note the pattern and systemic cause]
Example:
This is the third database connection pool exhaustion incident in 6 months. Indicates need for auto-scaling connection pool implementation (action item added).
---
Appendix
Glossary
[Term 1]: [Definition for non-technical readers] [Term 2]: [Definition for non-technical readers]
Example:
- Connection Pool: A cache of database connections maintained by the application to avoid creating a new connection for each query
- Circuit Breaker: A design pattern that prevents cascading failures by stopping requests to a failing service
References
- Runbook: [Link to relevant runbook, e.g., RB-DB-001 Database Failover]
- Monitoring Dashboard: [Link to Grafana/Datadog dashboard]
- Incident Channel: [Link to Slack incident channel archive]
- Related Documentation: [Links to architecture docs, configuration guides, etc.]
---
Sign-Off
Approved By:
- Incident Commander: @[IC name] - [Date]
- Engineering Manager: @[Manager name] - [Date]
- VP Engineering: @[VP name] - [Date] (for SEV0 only)
Post-Mortem Meeting Held: [Yes/No - Date and attendees] Document Status: [Draft → Final → Archived] Archive Location: [Link to knowledge base location]
---
Document Changelog
| Date | Version | Changes | Author |
|---|---|---|---|
| YYYY-MM-DD | 1.0 | Initial draft | @[name] |
| YYYY-MM-DD | 1.1 | Added [section] after review | @[name] |
| YYYY-MM-DD | 2.0 | Final version approved | @[IC] |
---
Remember:
- Blameless: Focus on systems, not people
- Specific: Action items must be clear and actionable
- Honest: Psychological safety requires honesty
- Timely: Complete within 48 hours while memory fresh
- Follow-Through: Track action items to completion
Questions? Contact @[IC or post-mortem facilitator]
Runbook: Cache Invalidation
Metadata
- Runbook ID: RB-CACHE-001
- Owner: Platform Engineering Team (@platform-team)
- Last Updated: 2025-12-05
- Version: 1.3
- Estimated Duration: 5-10 minutes
---
Trigger Conditions
Execute this runbook when:
- Stale data detected in production (users seeing outdated content)
- Code deployment requires cache clear (API schema changes, pricing updates)
- Configuration changes not reflecting (feature flags, A/B test variants)
- Corrupted cache entries causing application errors
- Emergency data correction requiring immediate propagation
Alert Names:
Cache-Stale-Data-DetectedCache-Hit-Rate-AnomalyAPI-Serving-Old-Data
Manual Triggers:
- Post-deployment cache clear requested by engineering team
- Customer support escalation reporting stale data
- Product team requesting immediate content update propagation
---
Severity Classification
Expected Severity: SEV2 (Minor Issues) - SEV1 (Major Degradation)
Reasoning:
- SEV2: Limited data staleness, affects subset of users, workaround available (hard refresh)
- SEV1: Critical data incorrect (pricing, inventory, permissions), widespread user impact
- SEV0: Escalate only if incorrect cached data causing financial loss or security vulnerability
Severity Decision:
Is cached data causing security vulnerability or financial loss?
├─ YES → SEV0 (Page immediately, declare incident)
└─ NO → Is critical business data incorrect (pricing, inventory, auth)?
├─ YES → SEV1 (Page during business hours, IC assigned)
└─ NO → SEV2 (Slack alert, execute runbook during business hours)---
Prerequisites
Before executing, verify:
1. Cache System Health:
# Check Redis cluster health
redis-cli -h cache-cluster.example.com PING
# Expected: PONG
# Check cluster info
redis-cli -h cache-cluster.example.com CLUSTER INFO
# Expected: cluster_state:ok2. Identify Cache Scope: Determine what needs invalidation:
- Global: Entire cache (all keys) - RARE, high impact
- Namespace: Specific service or feature (
user:*,product:*) - Pattern: Keys matching pattern (
product:123:*,session:abc*) - Single Key: Specific cache entry (
product:456,user:789:profile)
3. Impact Assessment:
# Check current cache hit rate
redis-cli -h cache-cluster.example.com INFO stats | grep keyspace_hits
# Estimate keys affected
redis-cli -h cache-cluster.example.com --scan --pattern "product:*" | wc -l4. Required Access:
- Redis CLI access or admin credentials
- Kubernetes access (if invalidating via API)
- Monitoring dashboard access
- Slack channel: #platform-engineering
5. Database Load Check:
# Verify database can handle cache miss traffic
curl https://monitoring.example.com/api/v1/query?query=database_cpu_percent
# Expected: < 60% (buffer for cache miss spike)WARNING: Invalidating large cache volumes causes database load spike. If DB CPU > 70%, consult DBA before proceeding.
---
Steps
Step 1: Announce Cache Invalidation
Purpose: Notify team and prepare for potential brief performance impact.
Post in #platform-engineering (or incident channel if SEV1):
"@here Executing cache invalidation for [scope: global/namespace/pattern].
Expected impact: Potential brief latency increase (1-2 minutes) as cache rebuilds.
Reason: [Brief reason: stale data, deployment, config change]
ETA for completion: [TIMESTAMP + 5-10 minutes]"If SEV1/SEV0: Post status page update (see communication-templates.md).
---
Step 2: Verify Database Can Handle Load
Purpose: Prevent cascading failure from database overload.
# Check database connection pool
curl https://monitoring.example.com/api/v1/query?query=database_connection_pool_usage
# Check database CPU
curl https://monitoring.example.com/api/v1/query?query=database_cpu_percent
# Check query queue depth
curl https://monitoring.example.com/api/v1/query?query=database_query_queue_lengthHealth Criteria:
- Database CPU < 60%
- Connection pool usage < 70%
- Query queue depth < 100
If Database Unhealthy:
- Option A: Partial invalidation (invalidate keys in batches)
- Option B: Scale database read replicas first
- Option C: Escalate to @db-sre-oncall for guidance
---
Step 3: Choose Invalidation Method
Purpose: Select appropriate method based on scope and urgency.
Option A: Pattern-Based Invalidation (RECOMMENDED)
Use when: Need to invalidate specific namespace or pattern (most common).
# Connect to Redis
redis-cli -h cache-cluster.example.com
# Scan and delete keys matching pattern
redis-cli -h cache-cluster.example.com --scan --pattern "product:*" | xargs redis-cli -h cache-cluster.example.com DEL
# For large datasets (> 10,000 keys), use batching
redis-cli -h cache-cluster.example.com --scan --pattern "product:*" | while read key; do
redis-cli -h cache-cluster.example.com DEL "$key"
sleep 0.01 # Rate limit to avoid overwhelming Redis
doneVerification:
# Confirm keys deleted
redis-cli -h cache-cluster.example.com --scan --pattern "product:*" | wc -l
# Expected: 0 (or significantly reduced count)---
Option B: Single Key Invalidation
Use when: Need to invalidate specific cache entry.
# Delete single key
redis-cli -h cache-cluster.example.com DEL "product:456"
# Verify deletion
redis-cli -h cache-cluster.example.com EXISTS "product:456"
# Expected: (integer) 0---
Option C: Namespace Invalidation via API
Use when: Application provides cache invalidation API endpoint.
# POST to invalidation endpoint
curl -X POST https://api.example.com/admin/cache/invalidate \
-H "Authorization: Bearer $ADMIN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"namespace": "product",
"reason": "Product pricing update",
"force": true
}'
# Expected Response
# {"status": "success", "keys_invalidated": 1234, "duration_ms": 543}---
Option D: Time-Based Expiration Update (GENTLE)
Use when: Not urgent, prefer gradual invalidation.
# Reduce TTL to 60 seconds for gradual invalidation
redis-cli -h cache-cluster.example.com --scan --pattern "product:*" | while read key; do
redis-cli -h cache-cluster.example.com EXPIRE "$key" 60
doneBenefit: Avoids sudden cache miss spike, cache naturally expires over 1 minute.
---
Option E: Global Cache Flush (NUCLEAR OPTION)
Use when: Complete cache corruption, all data stale, no other option.
WARNING: Causes massive database load spike. Only use in emergencies after consulting IC and DBA.
# DANGER: Flushes entire Redis database
redis-cli -h cache-cluster.example.com FLUSHDB
# For multi-node cluster, flush all nodes
for node in cache-node-1 cache-node-2 cache-node-3; do
redis-cli -h $node.example.com FLUSHDB
donePre-Flush Requirements:
- [ ] IC approval obtained
- [ ] Database team notified and standing by
- [ ] Monitoring dashboard open
- [ ] Rollback plan ready (restore from Redis persistence if available)
---
Step 4: Verify Cache Invalidation
Purpose: Confirm keys deleted and cache rebuilding correctly.
# Check keys deleted
redis-cli -h cache-cluster.example.com --scan --pattern "product:*" | wc -l
# Expected: 0 or significantly reduced
# Monitor cache miss rate (should spike briefly)
curl https://monitoring.example.com/api/v1/query?query=rate(cache_misses_total[1m])
# Monitor cache hit rate (should recover within 2-5 minutes)
curl https://monitoring.example.com/api/v1/query?query=cache_hit_rate
# Check API latency (may increase briefly)
curl https://monitoring.example.com/api/v1/query?query=http_request_duration_seconds{quantile="0.99"}Expected Behavior:
- Cache miss rate spikes immediately (2-10x normal)
- API latency increases briefly (1.5-2x normal)
- Cache hit rate recovers within 2-5 minutes
- Database CPU increases temporarily (10-20% increase)
---
Step 5: Verify Data Freshness
Purpose: Confirm new data correctly propagating to users.
Test with API Request
# Fetch data via API (should trigger cache rebuild)
curl -v https://api.example.com/v1/products/456 | jq '.updated_at'
# Verify response contains fresh data
# Expected: Timestamp shows recent update (not stale value)Test from User Perspective
# Test from multiple regions (if multi-region cache)
for region in us-east us-west eu-west; do
echo "Testing region: $region"
curl -H "X-Region: $region" https://api.example.com/v1/products/456 | jq '.updated_at'
done
# Expected: All regions return fresh dataManual Verification Checklist
- [ ] Load affected page/API endpoint in browser/Postman
- [ ] Verify data shows expected values (not stale)
- [ ] Check multiple users/sessions if user-specific cache
- [ ] Verify across all regions if multi-region deployment
---
Step 6: Monitor for Stability
Purpose: Ensure cache invalidation didn't cause secondary issues.
Monitor for 10-15 minutes:
# Watch cache hit rate recovery
watch -n 5 'redis-cli -h cache-cluster.example.com INFO stats | grep keyspace_hits'
# Monitor API error rate
curl https://monitoring.example.com/api/v1/query?query=rate(api_errors_total[5m])
# Monitor database CPU
curl https://monitoring.example.com/api/v1/query?query=database_cpu_percent
# Monitor API latency
curl https://monitoring.example.com/api/v1/query?query=http_request_duration_seconds{quantile="0.99"}Monitoring Dashboard: https://monitoring.example.com/dashboard/cache-health
Stability Criteria:
- Cache hit rate > 85% (recovered to normal)
- API error rate < 0.5% (normal baseline)
- Database CPU < 70% (normal range)
- API p99 latency < 500ms (acceptable)
If Unstable: See "Rollback" section below.
---
Step 7: Update Team and Close
Purpose: Notify team of completion and document outcome.
Post in #platform-engineering (or incident channel):
"Cache invalidation complete.
- Keys invalidated: [N keys or pattern]
- Duration: [X minutes]
- Cache hit rate recovered: [Y%]
- API latency returned to normal: p99 [Z]ms
- Database CPU stable: [N%]
- No errors detected.
Monitoring for next 30 minutes. Will close if stable."If SEV1/SEV0: Post status page update: "Monitoring - Issue resolved, watching for stability."
---
Step 8: Document in Incident Timeline (If SEV1+)
Purpose: Record actions for post-mortem.
Post in incident channel:
"[TIMESTAMP] Cache invalidation executed
- Scope: [Pattern/namespace]
- Method: [Pattern-based / API / Manual]
- Keys invalidated: [N]
- Impact: Brief latency increase (1-2 minutes), cache rebuilt successfully
- Verification: Fresh data confirmed across all regions
- Monitoring: Stable"---
Rollback Procedure
When to Rollback:
- Database overload (CPU > 90%, query queue growing)
- API error rate spike (> 5%)
- Cache not rebuilding (hit rate stuck < 50% after 10 minutes)
- Application errors from malformed cache data
Rollback Steps:
Option A: Restore from Redis Persistence (If Available)
# Stop Redis
redis-cli -h cache-cluster.example.com SHUTDOWN SAVE
# Restore from last RDB snapshot (if persistence enabled)
# SSH to Redis server
ssh admin@cache-cluster.example.com
sudo cp /var/lib/redis/dump.rdb.backup /var/lib/redis/dump.rdb
sudo systemctl start redis
# Verify restoration
redis-cli -h cache-cluster.example.com DBSIZE
# Expected: Previous key count restored---
Option B: Disable Cache Temporarily
Use when: Cache causing more problems than solving.
# Set feature flag to bypass cache
curl -X POST https://api.example.com/admin/feature-flags \
-H "Authorization: Bearer $ADMIN_API_TOKEN" \
-d '{"flag": "cache_enabled", "value": false}'
# Verify flag set
curl https://api.example.com/admin/feature-flags/cache_enabled
# Expected: {"flag": "cache_enabled", "value": false}Impact: API latency increases 2-3x, database load increases 3-5x. Only sustainable short-term.
Recovery: Fix underlying issue, re-enable cache gradually:
# Re-enable cache
curl -X POST https://api.example.com/admin/feature-flags \
-d '{"flag": "cache_enabled", "value": true}'---
Option C: Rate-Limit Cache Rebuilds
Use when: Cache rebuilding too aggressively, overwhelming database.
# Implement application-level rate limiting for cache writes
# (Requires code change or runtime config if supported)
# Temporary: Reduce Redis connection pool to slow cache writes
kubectl set env deployment/api REDIS_POOL_SIZE=10 -n production---
Escalation Criteria
Escalate to Senior Engineer (@platform-lead) if:
- Database CPU > 90% after invalidation
- API error rate > 5%
- Cache hit rate not recovering (< 50% after 15 minutes)
- Unclear which keys to invalidate
- Global flush being considered
Escalate to VP Engineering if:
- SEV0 incident (financial/security impact)
- Customer data integrity concerns
- Multi-region cache synchronization failure
---
Common Issues and Solutions
Issue 1: Cache Not Rebuilding
Symptom: Cache hit rate remains low (< 50%) 10+ minutes after invalidation.
Solutions:
- Check Application Health: Verify API pods running:
kubectl get pods -n production - Check Redis Connection: Verify app can connect to Redis:
kubectl logs deployment/api -n production | grep redis - Check Database: Verify DB responding:
pg_isready -h primary-db.example.com - Manual Cache Warm: Trigger cache population via script or API calls
Manual Cache Warm:
# Warm cache with top 100 product pages
for id in {1..100}; do
curl https://api.example.com/v1/products/$id > /dev/null
sleep 0.1
done---
Issue 2: Database Overload
Symptom: Database CPU > 80%, query queue growing, API timeouts.
Solutions:
- Pause Invalidation: Stop if using batched approach
- Scale Read Replicas: Add temporary read replicas (AWS RDS/GCP Cloud SQL)
- Enable Query Caching: Database-level query cache if available
- Rate Limit Traffic: Temporarily rate limit API requests
Emergency Database Relief:
# Scale RDS read replicas (AWS)
aws rds create-db-instance-read-replica \
--db-instance-identifier mydb-replica-temp \
--source-db-instance-identifier mydb-primary \
--db-instance-class db.r5.large
# Takes 5-10 minutes to provision---
Issue 3: Multi-Region Cache Desync
Symptom: Some regions showing fresh data, others showing stale data.
Solutions:
- Invalidate Each Region Separately:
for region in us-east us-west eu-west; do
redis-cli -h cache-$region.example.com --scan --pattern "product:*" | xargs redis-cli -h cache-$region.example.com DEL
done- Check Replication Lag: If using Redis replication between regions
- Verify CDN Purge: If using CDN, purge CDN cache separately:
# Cloudflare purge
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-d '{"tags": ["product"]}'---
Issue 4: Accidentally Deleted Wrong Keys
Symptom: Unintended cache keys deleted, unrelated functionality broken.
Solutions:
- Restore from Backup: If Redis persistence enabled, restore from RDB snapshot
- Rebuild Specific Namespace: Re-populate accidentally deleted keys:
# Trigger cache rebuild for specific namespace
curl -X POST https://api.example.com/admin/cache/rebuild \
-d '{"namespace": "user", "async": true}'- Wait for Natural Rebuild: Cache will rebuild on-demand as users access data
---
Post-Execution Checklist
After completing cache invalidation, verify:
- [ ] Targeted keys successfully deleted
- [ ] Fresh data verified via API/browser
- [ ] Cache hit rate recovered (> 85%)
- [ ] API latency returned to normal (< 200ms p99)
- [ ] Database CPU returned to baseline (< 60%)
- [ ] No application errors detected
- [ ] Multi-region consistency verified (if applicable)
- [ ] Team notified of completion
- [ ] Incident timeline updated (if SEV1+)
- [ ] Runbook updated if steps changed
---
Related Runbooks
- RB-DB-001: Database Failover (if cache overload causes DB issues)
- RB-PERF-001: Performance Troubleshooting (if latency remains high)
- RB-CDN-001: CDN Cache Purge (if CDN also caching stale data)
---
Cache Invalidation Patterns
Pattern 1: Deployment-Triggered Invalidation
Use when: Code deployment changes API schema or data format.
Automation:
# GitHub Actions workflow
- name: Invalidate cache post-deployment
run: |
curl -X POST https://api.example.com/admin/cache/invalidate \
-H "Authorization: Bearer ${{ secrets.ADMIN_API_TOKEN }}" \
-d '{"namespace": "api_v1", "reason": "Deployment ${{ github.sha }}"}'---
Pattern 2: Time-Based Auto-Expiration
Use when: Data naturally becomes stale over time (news, prices).
Implementation:
# Set TTL when writing to cache (application code)
redis-cli SET "product:456:price" "29.99" EX 3600 # 1 hour TTL
# Or update TTLs in bulk
redis-cli --scan --pattern "product:*" | while read key; do
redis-cli EXPIRE "$key" 3600
done---
Pattern 3: Event-Driven Invalidation
Use when: Backend data changes should immediately invalidate cache.
Implementation (Pseudocode):
# After database write
def update_product(product_id, data):
db.update(product_id, data)
cache.delete(f"product:{product_id}")
cache.delete(f"product:{product_id}:*") # Invalidate all related keys---
Pattern 4: Gradual Rollout Invalidation
Use when: Want to test cache invalidation impact on subset of traffic.
Implementation:
# Invalidate 10% of keys (testing)
redis-cli --scan --pattern "product:*" | head -n 100 | xargs redis-cli DEL
# Monitor impact for 15 minutes
# Invalidate remaining 90%
redis-cli --scan --pattern "product:*" | xargs redis-cli DEL---
Testing and Validation
Last Tested: 2025-11-20 (disaster recovery drill) Test Frequency: Monthly (simulate cache invalidation) Next Test: 2026-01-05
Test Procedure: 1. Announce test in #platform-engineering (off-hours) 2. Invalidate non-critical cache namespace (e.g., "test:*") 3. Monitor cache hit rate recovery 4. Verify no impact to production traffic 5. Document any runbook issues discovered 6. Update runbook if needed
---
Changelog
| Date | Version | Changes | Author |
|---|---|---|---|
| 2025-12-05 | 1.3 | Added multi-region invalidation, gradual patterns | @platform-team |
| 2025-10-10 | 1.2 | Added database load check prerequisite | @alice |
| 2025-08-15 | 1.1 | Added API-based invalidation method | @bob |
| 2025-06-01 | 1.0 | Initial version | @platform-team |
---
Contact
Owner: Platform Engineering Team Slack: #platform-engineering On-Call: @platform-oncall Escalation: @platform-lead
For questions or improvements: Open PR against runbooks/cache-invalidation.md
Runbook: Database Failover
Metadata
- Runbook ID: RB-DB-001
- Owner: Database SRE Team (@db-sre-team)
- Last Updated: 2025-12-03
- Version: 2.1
- Estimated Duration: 10-15 minutes
---
Trigger Conditions
Execute this runbook when:
- Primary database becomes unreachable (alert: "PostgreSQL Primary Down")
- Primary database performance severely degraded (> 10 seconds query latency)
- Planned maintenance requiring failover
- Database corruption detected on primary
Alert Names:
PostgreSQL-Primary-DownDatabase-Failover-RequiredPrimary-DB-Health-Critical
---
Severity Classification
Expected Severity: SEV1 (Major Degradation)
Reasoning: Application remains functional with degraded performance until failover completes. Becomes SEV0 if secondary unavailable or replication lag > 1 minute.
---
Prerequisites
Before executing, verify:
1. Secondary Database Health:
pg_isready -h secondary-db.example.com -p 5432Expected: secondary-db.example.com:5432 - accepting connections
2. Replication Lag:
-- Run on secondary
SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp())) AS lag_seconds;Expected: < 5 seconds
If lag > 5 seconds: Wait for replication to catch up or accept potential data loss (consult IC).
3. Disk Space on Secondary:
df -h /var/lib/postgresqlExpected: > 20% free space
4. Required Access:
- SSH access to database servers
- Sudo privileges for PostgreSQL commands
- AWS/GCP console access for DNS updates
- Slack channel: #database-operations
---
Steps
Step 1: Verify Primary is Truly Down
Purpose: Prevent unnecessary failover due to transient network issues.
# From monitoring server
ping -c 5 primary-db.example.com
# Check PostgreSQL connectivity
pg_isready -h primary-db.example.com -p 5432
# Check last heartbeat in monitoring
curl https://monitoring.example.com/api/v1/query?query=up{job="postgresql-primary"}Expected Result: Ping fails OR PostgreSQL not accepting connections
If Primary Responds: Do NOT proceed with failover. Investigate performance issue instead (see RB-DB-002).
---
Step 2: Announce Failover in Incident Channel
Purpose: Notify team and stakeholders of impending failover.
Post in #incident-YYYY-MM-DD-database:
"@here Starting database failover from primary-db to secondary-db.
Expected downtime: 2-5 minutes.
ETA for completion: 15:30 PST (in 10 minutes)."---
Step 3: Check Replication Status on Secondary
Purpose: Ensure secondary is up-to-date before promotion.
# SSH to secondary database
ssh admin@secondary-db.example.com
# Check replication status
sudo -u postgres psql -c "SELECT * FROM pg_stat_wal_receiver;"Expected Output:
status | streaming
received | <recent timestamp>If NOT streaming: Do NOT promote. Investigate replication failure first.
---
Step 4: Promote Secondary to Primary
Purpose: Make secondary the new writable database.
# On secondary database server
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/data
# Verify promotion
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"Expected Result: f (false = not in recovery = primary mode)
If Promotion Fails:
- Check PostgreSQL logs:
tail -f /var/log/postgresql/postgresql.log - Verify no standby.signal file blocking promotion
- Escalate to senior DBA if unclear
---
Step 5: Update DNS to Point to New Primary
Purpose: Route application traffic to new primary database.
Option A: Route53 (AWS)
# Update DNS record via AWS CLI
aws route53 change-resource-record-sets \
--hosted-zone-id Z1234567890ABC \
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "primary-db.example.com",
"Type": "A",
"TTL": 60,
"ResourceRecords": [{"Value": "10.0.2.20"}]
}
}]
}'Option B: Terraform
# Update terraform variable
cd infrastructure/terraform/database
echo 'primary_db_ip = "10.0.2.20"' >> terraform.tfvars
# Apply change
terraform plan -target=aws_route53_record.db_primary
terraform apply -target=aws_route53_record.db_primaryVerification:
# Verify DNS propagation
dig primary-db.example.com +short
# Expected: 10.0.2.20 (new primary IP)
# Wait for DNS TTL (60 seconds)
sleep 60---
Step 6: Update Application Configuration
Purpose: Ensure application connects to correct database endpoint.
Option A: No Action Required (if using DNS)
If application uses primary-db.example.com hostname, DNS update is sufficient.
Option B: Update Environment Variables
# Update Kubernetes configmap
kubectl edit configmap app-config -n production
# Update DATABASE_HOST value
# DATABASE_HOST: secondary-db.example.com → primary-db.example.com
# Restart application pods to pick up new config
kubectl rollout restart deployment/api -n production---
Step 7: Verify Application Connectivity
Purpose: Confirm application successfully connecting to new primary.
# Check application error rates in monitoring
curl https://monitoring.example.com/api/v1/query?query=rate(api_errors_total[5m])
# Check database connection pool
curl https://monitoring.example.com/api/v1/query?query=database_connections_active
# Test write operation
curl -X POST https://api.example.com/health/write-testExpected Result:
- Error rate returns to baseline (< 0.1%)
- Database connections active (> 0)
- Write test succeeds:
{"status": "ok", "write_success": true}
If Errors Persist:
- Check application logs for connection errors
- Verify DNS propagation:
nslookup primary-db.example.com - Verify firewall rules allow app → new primary
- Escalate to application team if needed
---
Step 8: Monitor for Stability
Purpose: Ensure failover was successful and system is stable.
# Monitor for 10 minutes
# - Database CPU, memory, disk I/O
# - Application error rates
# - Query latencyMonitoring Dashboard: https://monitoring.example.com/dashboard/database-health
Stability Criteria:
- Query latency < 100ms (p99)
- Database CPU < 70%
- Application error rate < 0.1%
- No new database alerts
If Unstable: See "Rollback" section below.
---
Step 9: Document Failover in Incident Timeline
Purpose: Record actions for post-mortem.
Post in #incident-YYYY-MM-DD-database:
"Failover complete. New primary: secondary-db (10.0.2.20).
- Promotion: 15:20 PST
- DNS Update: 15:22 PST
- App connectivity verified: 15:25 PST
- Monitoring for stability."---
Step 10: Update Runbook (If Steps Changed)
Purpose: Keep runbook accurate for future use.
# If any steps failed or needed modification
# Update this runbook in Git
git checkout -b update-db-failover-runbook
# Edit runbook
git add runbooks/database-failover.md
git commit -m "Update database failover runbook based on 2025-12-03 incident"
git push origin update-db-failover-runbook
# Create PR for review---
Rollback Procedure
When to Rollback:
- New primary experiencing high error rates (> 5%)
- New primary performance worse than before failover
- Data inconsistency detected
Rollback Steps:
1. Announce Rollback:
Post in incident channel:
"@here Rollback initiated. Reverting to original primary."2. Revert DNS Change:
# Point DNS back to original primary IP
aws route53 change-resource-record-sets \
--hosted-zone-id Z1234567890ABC \
--change-batch '{...}' # Original IP3. Demote Current Primary Back to Secondary:
# On current primary (being demoted)
sudo -u postgres pg_ctl stop -D /var/lib/postgresql/data
# Reconfigure as replica (create standby.signal)
touch /var/lib/postgresql/data/standby.signal
sudo -u postgres pg_ctl start -D /var/lib/postgresql/data4. Verify Rollback:
- Check application error rates
- Verify database connectivity
- Monitor for 10 minutes
5. Post-Incident Action:
- Schedule post-mortem to understand why rollback was needed
- Investigate secondary database issues before retry
---
Escalation Criteria
Escalate to Senior DBA (@senior-dba-oncall) if:
- Replication lag > 1 minute (data loss risk)
- Secondary database not healthy
- Promotion fails with unclear error
- Application connectivity issues after failover
- Rollback required
Escalate to VP Engineering if:
- Estimated downtime > 30 minutes
- Data loss occurred
- Customer-facing impact severe (SEV0)
---
Common Issues and Solutions
Issue 1: Replication Lag High
Symptom: Replication lag > 5 seconds
Solutions:
- Wait: Allow replication to catch up (if lag increasing, investigate network)
- Accept Data Loss: Consult IC, promote anyway if critical
- Investigate: Check network latency, disk I/O on secondary
---
Issue 2: Secondary Not Responding
Symptom: pg_isready fails on secondary
Solutions:
- Check PostgreSQL Status:
systemctl status postgresql - Restart PostgreSQL:
sudo systemctl restart postgresql - Check Disk Space:
df -h /var/lib/postgresql - Escalate: If restart fails, escalate to senior DBA
---
Issue 3: DNS Not Propagating
Symptom: Application still connecting to old primary after DNS update
Solutions:
- Verify DNS Change:
dig primary-db.example.com +short - Wait for TTL: 60 seconds for DNS cache expiration
- Flush Application DNS Cache: Restart application pods
- Temporary Fix: Update application config directly with new IP
---
Issue 4: Application Connection Errors
Symptom: API returning database connection errors after failover
Solutions:
- Check Connection String: Verify hostname/IP correct
- Check Firewall Rules:
telnet primary-db.example.com 5432 - Verify PostgreSQL Accepting Connections:
sudo -u postgres psql -c "SHOW listen_addresses;" - Restart Application:
kubectl rollout restart deployment/api
---
Post-Execution Checklist
After completing failover, verify:
- [ ] Secondary promoted to primary successfully
- [ ] DNS updated to point to new primary
- [ ] Application connectivity verified
- [ ] Error rates returned to baseline
- [ ] Monitoring stable for 10+ minutes
- [ ] Incident timeline documented
- [ ] Old primary (now secondary) replication configured
- [ ] Runbook updated if steps changed
- [ ] Post-mortem scheduled (within 48 hours)
---
Related Runbooks
- RB-DB-002: Database Performance Troubleshooting
- RB-DB-003: Database Replication Repair
- RB-DB-004: Database Backup and Restore
---
Testing and Validation
Last Tested: 2025-11-15 (disaster recovery drill) Test Frequency: Quarterly (every 3 months) Next Test: 2026-02-15
Test Procedure: 1. Announce planned test in #database-operations 2. Execute failover during low-traffic window (2am PST) 3. Verify application functionality 4. Fail back to original primary 5. Document any runbook issues discovered
---
Changelog
| Date | Version | Changes | Author |
|---|---|---|---|
| 2025-12-03 | 2.1 | Added DNS verification step, updated IPs | @alice |
| 2025-10-15 | 2.0 | Added Terraform option for DNS update | @bob |
| 2025-08-01 | 1.5 | Added replication lag check | @charlie |
| 2025-06-01 | 1.0 | Initial version | @db-team |
---
Contact
Owner: Database SRE Team Slack: #database-operations On-Call: @db-sre-oncall Escalation: @senior-dba-oncall
For questions or improvements: Open PR against runbooks/database-failover.md
skill: "managing-incidents"
version: "1.0"
domain: "devops"
base_outputs:
- path: "incidents/"
must_contain:
- "postmortem-*.md"
- "incident-*.md"
description: "Core incident documentation including post-mortems and incident reports"
- path: "runbooks/"
must_contain:
- "*.md"
description: "Executable runbooks for common failure scenarios with numbered steps, verification, and rollback procedures"
- path: "on-call/"
must_contain:
- "schedule.yaml"
- "escalation-policy.yaml"
description: "On-call rotation schedules, escalation policies, and handoff documentation"
- path: ".github/ISSUE_TEMPLATE/incident.md"
must_contain:
- "Severity:"
- "Incident Commander:"
- "Timeline:"
description: "GitHub issue template for declaring and tracking incidents"
conditional_outputs:
maturity:
starter:
- path: "incidents/severity-levels.md"
description: "Severity classification definitions (SEV0-SEV3) with response times and examples"
- path: "incidents/postmortem-template.md"
description: "Blameless post-mortem template with sections for timeline, root cause, action items"
- path: "on-call/rotation.md"
description: "Basic on-call rotation schedule with primary and secondary responders"
- path: "runbooks/incident-response-checklist.md"
description: "Generic incident response checklist covering detection through closure"
- path: ".slack/"
must_contain:
- "incident-channel-template.md"
description: "Slack incident channel template with pinned information structure"
intermediate:
- path: "incidents/communication-protocols.md"
description: "Internal and external communication protocols with update cadences and templates"
- path: "on-call/escalation-matrix.yaml"
description: "Multi-tier escalation policies with timeout rules and notification routing"
- path: "runbooks/"
must_contain:
- "database-failover.md"
- "cache-invalidation.md"
- "api-degradation.md"
description: "Service-specific runbooks for common failure scenarios (5+ runbooks minimum)"
- path: "statuspage/"
must_contain:
- "config.yaml"
- "update-templates.md"
description: "Status page configuration and customer communication templates"
- path: "incidents/metrics-dashboard.yaml"
description: "MTTA, MTTR, MTBF tracking dashboard configuration"
- path: "scripts/incident-declare.sh"
description: "Automated incident declaration script that creates channels, assigns roles, and notifies on-call"
advanced:
- path: "incidents/postmortems/"
must_contain:
- "*.md"
description: "Archive of completed post-mortems (searchable knowledge base)"
- path: "on-call/follow-the-sun.yaml"
description: "24/7 follow-the-sun rotation across multiple global teams"
- path: "runbooks/automated/"
must_contain:
- "*.py"
- "*.sh"
description: "Automated runbook scripts that execute mitigation steps programmatically"
- path: "chaos-engineering/"
must_contain:
- "gameday-scenarios.md"
- "disaster-recovery-drills.md"
description: "Chaos engineering scenarios and monthly disaster recovery drill procedures"
- path: "incidents/trend-analysis.yaml"
description: "Quarterly incident trend analysis dashboard with pattern detection"
- path: "integrations/"
must_contain:
- "pagerduty-*.py"
- "statuspage-*.py"
- "slack-bot.py"
description: "Tool integrations for automated incident workflows (PagerDuty, Statuspage, Slack)"
- path: "slo-breach-runbooks/"
description: "SLO breach detection and automated incident declaration from observability"
observability:
basic:
- path: "alerts/incident-triggers.yaml"
description: "Alert rules configured to trigger incidents with severity classification and runbook links"
standard:
- path: "alerts/routing-rules.yaml"
description: "Alert routing rules that page on-call teams based on service ownership"
- path: "runbooks/alert-investigation.md"
description: "Runbook template for investigating alerts with graphs, recent changes, and common causes"
advanced:
- path: "slo-incident-bridge/"
must_contain:
- "slo-breach-to-incident.yaml"
- "auto-severity-classification.py"
description: "Automated SLO breach detection that creates incidents with AI-powered severity classification"
scaffolding:
- path: "incidents/"
reason: "Central directory for all incident documentation and post-mortems"
- path: "runbooks/"
reason: "Repository for all operational runbooks and playbooks"
- path: "on-call/"
reason: "On-call rotation schedules, escalation policies, and handoff documentation"
- path: "incidents/postmortems/"
reason: "Archived post-mortems for organizational learning and pattern analysis"
- path: "statuspage/"
reason: "Status page configuration and customer communication templates"
- path: "scripts/"
reason: "Automation scripts for incident declaration, metrics tracking, and integrations"
- path: ".slack/"
reason: "Slack-specific templates and configurations for incident channels"
metadata:
primary_blueprints:
- "observability"
contributes_to:
- "Incident management lifecycle (detection, response, resolution, post-mortem)"
- "On-call rotation management and escalation policies"
- "Blameless post-mortem culture and organizational learning"
- "MTTA, MTTR, MTBF metrics tracking and improvement"
- "Runbook creation and automated mitigation"
- "Status page management and customer communication"
- "Alert routing and actionable alert design"
- "Disaster recovery preparedness through chaos engineering"
related_skills:
- "monitoring-observability: Provides alert triggers that initiate incident response"
- "implementing-disaster-recovery: Provides recovery procedures used during incidents"
- "securing-cloud-infrastructure: Security incidents require adapted incident response process"
- "implementing-cicd: CI/CD rollback capabilities enable fast mitigation"
key_deliverables:
- "Severity classification framework (SEV0-SEV3)"
- "On-call rotation schedules with primary/secondary coverage"
- "Service-specific runbooks (5+ for intermediate, 15+ for advanced)"
- "Blameless post-mortem templates and archived learnings"
- "Incident communication protocols (internal Slack, external status page)"
- "MTTA/MTTR/MTBF metrics dashboards"
- "Automated incident declaration and notification workflows"
typical_file_structure: |
incidents/
severity-levels.md
communication-protocols.md
metrics-dashboard.yaml
postmortems/
2025-01-15-database-outage.md
2025-02-03-api-degradation.md
runbooks/
database-failover.md
cache-invalidation.md
ddos-mitigation.md
api-degradation.md
on-call/
rotation.md
schedule.yaml
escalation-policy.yaml
handoff-template.md
statuspage/
config.yaml
update-templates.md
scripts/
incident-declare.sh
classify-severity.py
integrations/
pagerduty-slack.py
statuspage-auto-update.py
postmortem-generator.py
.github/ISSUE_TEMPLATE/
incident.md
.slack/
incident-channel-template.md
Escalation Matrix
Table of Contents
- Overview
- Escalation Decision Matrix
- Escalation Triggers
- Time-Based Escalation
- Severity-Based Escalation
- Expertise-Based Escalation
- Escalation Contacts
- On-Call Hierarchy
- Specialized Escalation Paths
- Escalation Communication
- Internal Notification
- Executive Notification
- Avoiding Over-Escalation
- Further Reading
Overview
Escalation ensures incidents get appropriate resources and attention based on duration, complexity, and impact. This matrix defines when and how to escalate incidents.
Escalation Decision Matrix
| Time Since Start | No Progress | Severity Increase | Expertise Needed | Action |
|---|---|---|---|---|
| < 15 minutes | Primary on-call | - | - | Continue investigation |
| 15-30 minutes | Secondary on-call | SEV2 → SEV1 | Specialist (DB, Network) | Escalate to secondary |
| 30-60 minutes | Team Lead | SEV1 → SEV0 | Architect | Escalate to team lead |
| 60+ minutes | Director/VP Eng | - | Senior leadership | Escalate to director |
Escalation Triggers
Time-Based Escalation
15 Minutes - No Acknowledgment:
- Primary on-call hasn't acknowledged alert
- Auto-escalate to secondary on-call via PagerDuty/Opsgenie
30 Minutes - No Progress:
- Investigation ongoing but no mitigation path identified
- Escalate to team lead or senior engineer for guidance
60 Minutes - Extended Duration:
- Incident unresolved after 1 hour
- Escalate to engineering manager or director
- Consider declaring SEV0 if customer impact severe
Severity-Based Escalation
SEV2 → SEV1:
- Impact expands (5% → 25% of customers)
- Escalate to team lead, assign IC
SEV1 → SEV0:
- Complete outage or critical functionality lost
- Escalate to VP Engineering, notify executives
Expertise-Based Escalation
Database Issues:
- Escalate to @db-team within 15 minutes if database-related
Network/Infrastructure:
- Escalate to @infra-team if network or infrastructure issue
Security Incident:
- Escalate to @security-team immediately for security issues
Third-Party Vendor:
- Open vendor support ticket within 15 minutes
- Escalate through TAM (Technical Account Manager) if available
Escalation Contacts
On-Call Hierarchy
Primary On-Call
↓ (5 min no ack)
Secondary On-Call
↓ (15 min no resolution)
Team Lead / Senior Engineer
↓ (30 min no resolution OR SEV0)
Engineering Manager
↓ (60 min OR high business impact)
Director / VP EngineeringSpecialized Escalation Paths
Database:
- Primary: @db-oncall
- Secondary: @senior-dba
- Escalation: @db-team-lead
Security:
- Primary: @security-oncall
- Immediate: @security-team-lead (for all security incidents)
- Legal: @legal-team (for data breaches)
Infrastructure:
- Primary: @infra-oncall
- Secondary: @sre-team
- Escalation: @infra-lead
Escalation Communication
Internal Notification
When escalating, post in incident channel:
@team-lead Escalating to you - no progress after 30 minutes.
Current status: [Brief summary]
Need: [Specific help needed]Executive Notification
For SEV0 incidents, notify executives within 15 minutes:
- Who to notify: CTO, VP Engineering, CEO (for major outages)
- How: Slack DM + brief email
- What to include: Business impact, ETA, current actions
Executive Brief Template:
SEV0 Incident: [Title]
Duration: [X minutes]
Impact: [Y% of customers unable to [action]]
Current Status: [Investigating/Mitigating]
ETA: [Best estimate or "Unknown"]
IC: @[name]Avoiding Over-Escalation
Do NOT escalate if:
- Issue being actively mitigated
- Clear path to resolution identified
- Escalation would distract without adding value
Example: Database slow, root cause identified, mitigation in progress → Do NOT escalate unless mitigation fails.
Further Reading
- PagerDuty: "Escalation Policies Best Practices"
- Google SRE: "Incident Management"
Related skills
FAQ
When should you declare an incident?
When in doubt, declare early; severity can always be downgraded, and declaring enables coordination and prevents delayed response.
Who does hands-on debugging during an incident?
Subject Matter Experts do the debugging and mitigation; the Incident Commander coordinates and delegates but does not debug.