Your app goes down on Friday night. You don’t find out until Monday when a customer complains.
This doesn’t have to happen. The same SRE principles that keep large systems alive (observability, threat detection, and disaster recovery) work at any scale. They’re the same tools, just sized to your infrastructure.
This is the complete SRE stack: observe your cluster, catch security threats before they spread, and restore from backups in minutes. All open source. Deployable on any Kubernetes cluster.
What We’re Building
SRE has four pillars:
- Observability: Know what’s happening (metrics, logs, dashboards, alerts)
- Security: Know who’s doing bad things (threat detection, network policies)
- Incident Response: Know how to fix it (alerting, runbooks, debugging)
- Disaster Recovery: Know how to rebuild it (backups, recovery testing)
Here’s the stack:
┌──────────────────────────────────────────────────────────────────┐
│ Your Kubernetes Cluster │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ OBSERVABILITY ──────────────────────────────────────────────┐│
│ │ ││
│ │ Prometheus ◄── kube-state, node-exporter, app metrics ││
│ │ │ ││
│ │ ├──► Grafana (dashboards) ││
│ │ └──► Alertmanager ││
│ │ ││
│ │ Promtail ──► Loki (centralized logs) ││
│ │ │ ││
│ │ └──► Grafana (log search) ││
│ │ ││
│ │ Alertmanager ──► ntfy (notifications) ││
│ │ ││
│ └──────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ SECURITY ───────────────────────────────────────────────────┐│
│ │ ││
│ │ Falco (runtime threat detection, DaemonSet on all nodes) ││
│ │ │ ││
│ │ └──► falcosidekick ──► notifications ││
│ │ ││
│ │ Network Policies (Cilium CNI) ││
│ │ default-deny ingress + explicit allows ││
│ │ ││
│ └──────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ DISASTER RECOVERY ──────────────────────────────────────────┐│
│ │ ││
│ │ Velero (daily backups) ──► Cloud storage ││
│ │ schedule: daily at 2am ││
│ │ retention: 30 days ││
│ │ namespaces: configurable per workload ││
│ │ ││
│ │ Disaster recovery test: validate restore capability ││
│ │ ││
│ └──────────────────────────────────────────────────────────────┘│
│ │
When something breaks:
- Falco detects suspicious behavior (unauthorized process, privilege escalation)
- Prometheus detects metrics problems (node down, pod restarting, disk full)
- Alertmanager groups alerts, routes to your notification system
- You get a notification, check dashboards, debug via logs
- If catastrophic: Velero restores from yesterday’s backup
Let’s build each layer.
Part 1: Observability
The Foundation: Prometheus
Prometheus scrapes metrics from your cluster every 15 seconds and stores them for 15 days.
| |
Why 15d retention? Retention is a tradeoff. 15 days of metrics typically requires 15-20Gi of storage at moderate scale. Long enough to spot trends and debug historical issues. Adjust based on your pod density and cardinality.
What gets scraped:
- kube-state-metrics: Pod restarts, node conditions, PVC usage, deployment replicas
- node-exporter: CPU, memory, disk I/O, network stats on each node
- External nodes: Monitor external VMs, GPU workers, or other infrastructure
- pushgateway: For cron jobs and batch operations that finish before Prometheus scrapes them
PromQL lets you ask questions:
| |
Grafana: Make It Visual
Prometheus has data. Grafana turns it into dashboards.
| |
kube-prometheus-stack provides 40+ pre-built dashboards:
- Kubernetes Cluster: node count, total CPU, total memory, storage usage
- Node Exporter: per-node view of CPU, memory, disk, network
- Pod CPU/Memory: which workloads consume the most resources
- Prometheus: is Prometheus itself healthy?
You’ll build custom dashboards specific to your applications:
- Infrastructure Health At a Glance: Node count, pod count, resource usage, active alerts
- Storage Utilization: PVC trends, growth rate, capacity planning
- Pod Restarts: Which workloads are unstable, restart patterns
- Application Metrics: Request rate, error rate, latency percentiles
Loki: Centralized Logs
Metrics tell you when something broke. Logs tell you why.
| |
Promtail runs as a DaemonSet on every node. It tails pod logs and ships them to Loki. Logs are indexed by pod name, namespace, and app label: the same labels you use for orchestration.
In Grafana, search logs by their labels:
{namespace="production", app="api"}
| json
| level="error"
This finds all error-level JSON logs from the api app in production. 20Gi of storage typically holds 2-4 weeks of logs depending on verbosity. When a pod crashes with CrashLoopBackOff, query the logs to understand why.
Alertmanager + Notifications
Dashboards are useless if you have to remember to check them. Alerts are what actually save you.
When Prometheus detects a problem, Alertmanager groups related alerts and sends them to your notification system. We use ntfy (HTTP webhooks to push notifications), but you can route to Slack, PagerDuty, email, or any webhook receiver.
| |
When Prometheus fires an alert (node down, pod restarting, disk full), Alertmanager:
- Waits 10s to see if more related alerts fire
- Groups them by alert type
- Sends a single webhook to your notification system
- Your system routes to the right channel (on-call, team slack, etc.)
send_resolved: true means you also get notified when the alert clears:
[Infrastructure] Node Offline (1 active)
worker-3 - up == 0
[Infrastructure] RESOLVED: Node Offline (1 resolved)
worker-3 - up == 1
Part 2: Security
Observability tells you what’s happening. Security tells you if something malicious is happening.
Falco: Runtime Threat Detection
Falco is a runtime security engine. It watches for suspicious behavior: unauthorized process execution, privilege escalation, sensitive file access, container escape attempts.
| |
Falco runs as a DaemonSet on every node. It monitors syscalls using eBPF (kernel mode inspection). When suspicious behavior is detected, falcosidekick sends alerts to your notification system.
Example alerts Falco catches:
- Shell spawned in container (usually indicates compromise or debugging)
- Sensitive files read (
/etc/passwd, host certificates, cloud credentials) - Privilege escalation attempts
- Unauthorized port binding
- Container attempting to escape to host
When you get an alert:
[Security] Sensitive file opened
pod: production/api
file: /etc/shadow
user: root
time: 2026-05-26T14:23:45Z
You know something’s suspicious immediately. Not after data exfiltration.
Network Policies: Default-Deny Access
Falco detects threats. Network policies prevent unauthorized traffic from flowing.
Using Cilium CNI, your cluster enforces explicit network policies:
| |
Apply this to sensitive namespaces: production applications, databases, identity services. Each namespace can only receive traffic from:
- Its own ingress controller (for external HTTP/HTTPS)
- Kube-system (for DNS, kubelet healthchecks)
- Itself (pod-to-pod communication within the namespace)
Result: If a compromised application tries to SSH to another service or exfiltrate data, the network drops it. Traffic never leaves the namespace.
Part 3: Disaster Recovery
Observability + security prevent most problems. But catastrophe happens. Hardware fails. Storage corrupts. You get compromised. You need backups.
Velero: Automated Daily Backups
Velero is a backup tool for Kubernetes. It snapshots your cluster’s desired state and stores it off-cluster.
| |
Every night at 2am, Velero:
- Snapshots all Kubernetes resources (Deployments, StatefulSets, ConfigMaps, Secrets, PVCs)
- Exports PVC data (if configured with snapshots)
- Uploads to cloud storage (S3, GCS, B2, or compatible service)
- Keeps a rolling 30-day retention
What gets backed up:
- Kubernetes manifests: All resource definitions in YAML
- ConfigMaps: Application configuration
- Secrets: API keys, TLS certificates (encrypted at rest in cloud storage)
- PVC data: Databases, file storage (if snapshots are enabled)
- Velero metadata: Backup history and restore plans
Disaster recovery scenario:
Your primary region goes down. Data corruption detected. Or ransomware hits and you need to restore from a clean state.
| |
Complete recovery: 10-30 minutes depending on cluster size and data volume. Without backup: hours of manual reconfiguration, or total data loss.
Real Scenario: The Alert That Saved Your Recovery Time
Worker node fails unexpectedly. Hardware issue, drive error, or kernel panic. The node is unreachable.
Without Monitoring
6 hours later, your team notices deployment failures. Pods keep pending: “node selector can’t find a matching node.” Someone starts investigating. The failed node has been down for hours. Manual recovery takes 2+ hours. In the meantime, your SLA is breached.
With Monitoring
2 minutes after node goes down:
Prometheus scraper fails. After 1 minute of missed scrapes, Alertmanager fires:
[Infrastructure] Node Offline
worker-3 - up == 0
You get notified and check Grafana:
- Node CPU/memory graphs show the node went dark
- Pods scheduled there show
NodeNotReadystatus - Affected deployments have pending pods
You immediately:
- Check the node status:
kubectl get node worker-3→NotReady - Decide: Hardware issue → replace node, or use spare capacity
- If critical: restore workloads via Velero backup
- If capacity-constrained: scale other nodes or update node selector
Result: You fixed it within 10 minutes. No SLA breach. No customer notice.
That’s what SRE monitoring is for: knowing when something breaks before it impacts your customer.
How It All Deploys (GitOps)
Your entire SRE stack is version-controlled and deployed via Flux CD (or your GitOps tool of choice).
Directory Structure
your-gitops-repo/
├── clusters/prod/
│ └── flux-system/
│ ├── kustomization.yaml # Root entry point
│ ├── infrastructure.yaml # Kustomization reference
│ └── monitoring.yaml # Kustomization reference
├── infrastructure/prod/
│ ├── falco/
│ │ ├── helmrelease.yaml
│ │ ├── namespace.yaml
│ │ └── kustomization.yaml
│ ├── cilium/
│ │ └── helmrelease.yaml
│ └── velero/
│ ├── helmrelease.yaml
│ ├── schedule.yaml
│ └── kustomization.yaml
├── monitoring/prod/
│ ├── kube-prometheus-stack-helmrelease.yaml
│ ├── loki-helmrelease.yaml
│ ├── ntfy/ntfy.yaml
│ ├── network-policies/
│ │ ├── default-deny-ingress.yaml
│ │ ├── allow-from-ingress-nginx.yaml
│ │ └── kustomization.yaml
│ └── kustomization.yaml
└── .sops.yaml # SOPS encryption config
To deploy:
| |
All changes are git commits. Auditing is git log. Rolling back is git revert. Every config change is tracked forever.
Storage Backend
Prometheus and Loki both need fast, persistent storage. NFS is too slow for time-series databases. Use:
- Cloud block storage: EBS, GCP Persistent Disks, Azure Managed Disks
- iSCSI: TrueNAS, NETAPP, or compatible
- Local volumes: For on-premise deployments (with replication)
| |
Quick Start: Deploy This Yourself
You need:
- A Kubernetes cluster (1.20+)
- Persistent storage (fast block storage, not NFS)
- A LoadBalancer or Ingress controller
- Helm 3.x
Step 1: Add Helm Repos
| |
Step 2: Create Namespaces & Secrets
| |
Step 3: Deploy Monitoring Stack
| |
Step 4: Deploy Falco
| |
Step 5: Deploy Velero
| |
Step 6: Verify Everything Works
| |
Lessons Learned
Retention Tuning
Prometheus retention is a tradeoff: longer history requires more storage. Start with 15d, monitor disk usage, adjust based on your pod density. Higher cardinality (many unique label combinations) drives more storage.
Alert Fatigue
Define alert thresholds carefully. Too sensitive = 100 alerts a day (ignored). Too loose = missing real issues. Start conservative, adjust as you understand your baseline.
Network Policy Debugging
“Pod X can’t reach Pod Y” is hard to debug with network policies enabled. Test connectivity before enforcing policies:
| |
Storage Backend Matters
We benchmarked: NFS writes are 10-50x slower than block storage for time-series databases. Use cloud block storage, iSCSI, or local volumes with replication. Don’t use NFS for Prometheus or Loki.
Backup Testing
Backups only matter if you can restore. Test quarterly: restore Velero backup to a dev cluster and verify everything works. A backup you’ve never tested is not a backup. It’s a hope.
What’s Next
This covers the core SRE stack. From here, you’ll build:
Custom Alerts
Define SLOs for your applications. Example for an API service:
| |
Custom Dashboards
Build dashboards specific to your applications: request rate, error rate, latency percentiles, cache hit ratio, database connection pool size.
Incident Runbooks
For each alert type, document the recovery steps:
Alert: PodCrashLooping
1. Get pod logs: kubectl logs -n NAMESPACE POD
2. Check recent events: kubectl describe pod -n NAMESPACE POD
3. Check resource requests: kubectl get pod -o json | jq .spec.containers[].resources
4. If OOMKilled: increase memory request
5. If ImagePullBackOff: check registry credentials and image availability
Regular Testing
Quarterly: restore a Velero backup to a staging cluster. Verify all workloads come back correctly. Fix any issues before you need a real disaster recovery.
Summary
You now have a production-grade SRE stack:
- Observability: Metrics, logs, dashboards, alerts that wake you up when things break
- Security: Runtime threat detection, network policies, zero-trust access control
- Disaster Recovery: Automated daily backups, tested restores, clear recovery procedures
- Auditability: GitOps version control of all infrastructure code
This works whether you’re running 10 nodes or 1000. The principles scale. The tools scale. The practices scale.
When the next crisis hits (hardware fails, a deployment goes bad, or security is questioned) you won’t scramble. You’ll have observability telling you exactly what happened. Backups letting you restore. And a runbook telling you what to do.
That’s what production SRE looks like.
Building out your SRE stack? We take on short-term infrastructure engagements and platform work.