SOP: Incident Response
Status: Active. Created 2026-03-14 as part of Phase 12 (Incident Management).
Purpose: Structured incident response reduces MTTR. This SOP defines the detection → triage → diagnosis → remediation → postmortem pipeline. Every incident follows this flow.
Severity Levels
| Severity |
Definition |
Examples |
Response Time |
Notification |
| <strong>P1 — Service Down</strong> |
User-facing service completely unavailable |
pal-e-docs 502, Forgejo unreachable, Keycloak down, ArgoCD sync loop |
Immediate |
Telegram alert (automatic) |
| <strong>P2 — Degraded</strong> |
Service functional but impaired (slow, partial failures, elevated errors) |
Response time >5s, pod restarts, CNPG failover, CI pipeline stuck |
<1 hour |
Telegram alert (automatic) |
| <strong>P3 — Cosmetic / Non-blocking</strong> |
Visible issue that doesn't affect functionality |
Dashboard rendering glitch, stale metric, log noise |
Next session |
None (discovered during work) |
Detection Sources
| Source |
What It Detects |
Alert Channel |
Dashboard |
| <strong>Prometheus Alertmanager</strong> |
Pod restarts, OOMKilled, disk pressure, target down |
Telegram + Slack |
<a href="https://alertmanager.tail5b443a.ts.net">Alertmanager UI</a> |
| <strong>Blackbox Exporter</strong> |
Endpoint unreachable (probe_success == 0) |
Telegram (via PrometheusRule) |
Service Uptime dashboard |
| <strong>Grafana dashboards</strong> |
Latency spikes, error rate increase, saturation |
Visual (manual check) |
<a href="https://grafana.tail5b443a.ts.net">Grafana</a> |
| <strong>DORA exporter</strong> |
Deployment failures, pipeline stuck |
DORA dashboard anomaly |
DORA Metrics dashboard |
| <strong>Woodpecker CI</strong> |
Pipeline failures |
Woodpecker UI notification |
<a href="https://woodpecker.tail5b443a.ts.net">Woodpecker UI</a> |
| <strong>Manual discovery</strong> |
User reports, session observation |
Lucas notices |
N/A |
Incident Response Flow
Step 1: Detection
Incident is detected via one of the sources above. Automatic alerts (Telegram) provide the alert name, namespace, and severity. If detected manually, note the time and symptoms.
Step 2: Triage (1-2 minutes)
Determine severity level (P1/P2/P3) and scope:
- Is a user-facing service down? → P1
- Is a service degraded but functional? → P2
- Is it cosmetic or non-blocking? → P3
- What is the blast radius? Single service, namespace, or cluster-wide?
- Is this a known failure mode? Check runbooks below.
Step 3: Diagnosis (5-15 minutes)
Use the diagnostic toolkit to identify root cause:
kubectl get pods -n <namespace> — check pod status
kubectl describe pod <pod> -n <namespace> — events, conditions
kubectl logs <pod> -n <namespace> --tail=100 — recent logs
kubectl get events -n <namespace> --sort-by=.lastTimestamp — cluster events
- Grafana → service dashboard → check traffic/latency/errors/saturation
- Loki →
{namespace="<ns>"} → search for error patterns
- Prometheus → direct PromQL queries for specific metrics
Apply the appropriate fix based on root cause. Common remediation actions:
- Pod crash: Check logs, fix code/config, redeploy
- Resource exhaustion: Increase limits, optimize, or scale
- Bad deploy: Rollback via ArgoCD (sync to previous commit) or revert PR
- Infrastructure:
tofu apply to reconcile state, or manual kubectl fix
- Database: Follow
sop-postgres-restore if data loss, or sop-db-migration-recovery if migration failure
- Board tracking: Create a board item on the affected project's board representing the fix action (not the incident itself). Use
create_board_item(board_slug="board-PROJECT", item_type="todo", title="Fix: [description of remediation]", column="in_progress", labels="type:incident"). Incidents start in in_progress because they're already being worked when discovered. Move to done after Step 5 verification passes.
Step 5: Verification
Confirm the fix:
- Service responding (check dashboard or curl)
- Alert resolved in Alertmanager (auto-resolves when condition clears)
- No new errors in logs
- Metrics returning to baseline
Step 6: Postmortem (P1 and P2 only)
Create an incident note in pal-e-docs:
create_note(title="Incident: ...", slug="incident-YYYY-MM-DD-description", note_type="incident", tags="incident", project="pal-e-platform")
- Include: timeline, root cause, remediation, lessons learned, action items
- Link to relevant Grafana dashboard screenshots
- Update this SOP if a new failure mode was discovered
Common Failure Runbooks
Pod CrashLoopBackOff
| Step |
Command / Action |
| 1. Check logs |
<code>kubectl logs <pod> -n <ns> --previous</code> (previous = crashed container) |
| 2. Check events |
<code>kubectl describe pod <pod> -n <ns></code> → look at Events section |
| 3. Common causes |
Bad migration (Alembic), missing secret, OOMKilled, config error |
| 4. Fix |
Fix code/config → push → CI builds → ArgoCD syncs. Or rollback: <code>argocd app rollback <app></code> |
| 5. Dashboard |
Grafana → golden signals dashboard for the service |
CNPG Database Failover
| Step |
Command / Action |
| 1. Check cluster status |
<code>kubectl get cluster -n <ns></code> — check READY and STATUS |
| 2. Check pods |
<code>kubectl get pods -n <ns> -l cnpg.io/cluster=<name></code> |
| 3. Check timeline |
<code>kubectl cnpg status <cluster> -n <ns></code> (if cnpg plugin installed) |
| 4. If data loss |
Follow <code>sop-postgres-restore</code> — restore from MinIO backup |
| 5. Dashboard |
Grafana → CNPG dashboard (PodMonitor metrics) |
Woodpecker Pipeline Stuck
| Step |
Command / Action |
| 1. Check queue |
Woodpecker MCP: <code>get_queue_status</code> |
| 2. Check agent |
<code>kubectl get pods -n woodpecker -l app=woodpecker-agent</code> |
| 3. Agent PVC stale? |
Delete agent PVC + restart: <code>kubectl delete pvc agent-config -n woodpecker</code> then <code>kubectl rollout restart deployment woodpecker-agent -n woodpecker</code> |
| 4. Cancel stuck pipeline |
Woodpecker MCP: <code>cancel_pipeline(repo_id, pipeline_number)</code> |
| 5. Dashboard |
<a href="https://woodpecker.tail5b443a.ts.net">Woodpecker UI</a> → check pipeline logs |
ArgoCD Sync Failure
| Step |
Command / Action |
| 1. Check app status |
<code>kubectl get application -n argocd</code> — look for Degraded/OutOfSync |
| 2. Check sync details |
<code>argocd app get <app></code> or ArgoCD UI |
| 3. Common causes |
Invalid manifests, missing namespace, SOPS decryption failure, image pull error |
| 4. Fix manifests |
Fix in app repo → push → ArgoCD auto-syncs |
| 5. Force sync |
<code>argocd app sync <app> --force</code> (last resort) |
| 6. Dashboard |
<a href="https://argocd.tail5b443a.ts.net">ArgoCD UI</a> |
Tailscale Funnel Unreachable
| Step |
Command / Action |
| 1. Check Tailscale operator |
<code>kubectl get pods -n tailscale</code> |
| 2. Check ingress |
<code>kubectl get ingress -n <ns></code> — verify ingress exists and has correct class |
| 3. Check backend service |
<code>kubectl get svc -n <ns></code> — verify service exists and has endpoints |
| 4. Check Tailscale proxy |
<code>kubectl get pods -n tailscale -l app.kubernetes.io/name=tailscale</code> — look for the proxy pod for this funnel |
| 5. Restart proxy |
Delete the Tailscale proxy pod — it will be recreated by the operator |
Disk Pressure (Node)
| Step |
Command / Action |
| 1. Check disk usage |
<code>df -h</code> on the node |
| 2. Container images |
<code>crictl images</code> — prune unused: <code>crictl rmi --prune</code> |
| 3. PVC usage |
Check large PVCs: <code>kubectl get pv --sort-by=.spec.capacity.storage</code> |
| 4. Log rotation |
Check <code>/var/log</code> and Loki/Promtail storage |
| 5. Dashboard |
Grafana → Node dashboard → disk panels |
Alerting Rules Reference
| Alert |
Condition |
Severity |
Runbook |
| PodRestartStorm |
>3 restarts in 15m |
warning |
Pod CrashLoopBackOff runbook |
| OOMKilled |
Container OOMKilled |
critical |
Check resource limits, increase or optimize |
| DiskPressure |
<15% free space |
critical |
Disk Pressure runbook |
| TargetDown |
Scrape target unreachable >5m |
warning |
Check service + ServiceMonitor |
| EndpointDown |
probe_success == 0 for >2m |
critical |
Tailscale Funnel Unreachable runbook |
| EndpointSlowResponse |
probe_duration_seconds > 5s for >5m |
warning |
Check service resource saturation |
Key Links
sop-postgres-restore — CNPG backup restore procedure
sop-db-migration-recovery — failed Alembic migration recovery
sop-ci-pipeline-recovery — CI pipeline failure triage
sop-deploy-recovery — deployment failure recovery
sop-mcp-server-recovery — MCP server failure recovery
deployment-lessons — operational lessons learned
dora-framework — DORA metrics (MTTR measurement)