🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build a Slack DevOps Bot with Claude API: Alerts, Runbooks, and Incident Help

Build a Slack bot that uses Claude AI to explain alerts, fetch runbooks, suggest fixes, and help during incidents. Full Python project with Slack Bolt and Anthropic API.

DevOpsBoysMay 28, 20266 min read
Share:Tweet

Imagine getting a PagerDuty alert at 2am, typing /ask why is memory high on prod-api in Slack, and getting a specific, actionable response based on your runbooks and recent alerts. That's what we're building.

A Slack bot powered by Claude API that:

  • Answers DevOps questions in plain English
  • Explains Kubernetes errors
  • Suggests fixes based on your runbooks
  • Summarizes recent alerts

What We're Building

Engineer: @devbot what does OOMKilled mean and how do I fix it?

DevBot: OOMKilled means your container was killed by the kernel because 
it exceeded its memory limit. Here's what to check:

1. Check current memory usage: kubectl top pod <pod-name> -n <namespace>
2. Check current limit: kubectl describe pod <pod-name> | grep -A5 Limits
3. Short-term fix: increase memory limit in your deployment
4. Long-term: find the memory leak or optimize your app

Based on your team's runbook, the most common cause in prod-api is 
the /export endpoint loading large datasets into memory...

Stack

  • Python 3.11+
  • Slack Bolt (official Slack SDK)
  • Anthropic Claude API
  • Optional: your runbooks as text files

Setup

bash
mkdir devops-slack-bot && cd devops-slack-bot
pip install slack-bolt anthropic python-dotenv aiohttp

Create .env:

SLACK_BOT_TOKEN=xoxb-...
SLACK_SIGNING_SECRET=...
ANTHROPIC_API_KEY=sk-ant-...

Create a Slack App:

  1. Go to https://api.slack.com/apps → New App
  2. Features → OAuth & Permissions → Add Bot Token Scopes:
    • app_mentions:read
    • chat:write
    • commands
    • im:read, im:write
  3. Enable Socket Mode (for development) or Events API
  4. Install to workspace → copy Bot Token
  5. Basic Information → copy Signing Secret

Step 1: Core Bot Setup

python
# bot.py
import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from dotenv import load_dotenv
from ai_handler import DevOpsAI
 
load_dotenv()
 
app = App(token=os.environ["SLACK_BOT_TOKEN"])
ai = DevOpsAI()
 
# Handle @mentions
@app.event("app_mention")
def handle_mention(event, say, client):
    user = event["user"]
    text = event["text"]
    channel = event["channel"]
    thread_ts = event.get("thread_ts", event["ts"])
    
    # Remove the bot mention from the text
    question = text.split(">", 1)[-1].strip()
    
    if not question:
        say(
            text="Hey! Ask me anything about DevOps, Kubernetes, alerts, or paste an error message.",
            thread_ts=thread_ts
        )
        return
    
    # Show typing indicator
    client.reactions_add(
        channel=channel,
        name="thinking_face",
        timestamp=event["ts"]
    )
    
    try:
        response = ai.answer(question)
        say(text=response, thread_ts=thread_ts)
    except Exception as e:
        say(text=f"❌ Error: {str(e)}", thread_ts=thread_ts)
    finally:
        client.reactions_remove(
            channel=channel,
            name="thinking_face",
            timestamp=event["ts"]
        )
 
# Handle direct messages
@app.event("message")
def handle_dm(event, say):
    # Only respond to DMs (not channel messages)
    if event.get("channel_type") != "im":
        return
    
    text = event.get("text", "").strip()
    if not text:
        return
    
    response = ai.answer(text)
    say(text=response)
 
# Slash command /ask
@app.command("/ask")
def handle_ask_command(ack, respond, command):
    ack()
    question = command["text"]
    
    if not question:
        respond("Usage: `/ask <your devops question>`")
        return
    
    respond("🤔 Thinking...")
    response = ai.answer(question)
    respond(response)
 
# Slash command /explain-error
@app.command("/explain-error")
def handle_explain_error(ack, respond, command):
    ack()
    error = command["text"]
    
    if not error:
        respond("Paste the error message after the command: `/explain-error <error>`")
        return
    
    respond("🔍 Analyzing error...")
    response = ai.explain_error(error)
    respond(response)
 
if __name__ == "__main__":
    handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
    handler.start()

Step 2: Claude AI Handler

python
# ai_handler.py
import anthropic
import os
from pathlib import Path
 
class DevOpsAI:
    def __init__(self):
        self.client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
        self.runbooks = self._load_runbooks()
        
    def _load_runbooks(self) -> str:
        """Load runbooks from the runbooks/ directory"""
        runbooks_dir = Path("runbooks")
        if not runbooks_dir.exists():
            return ""
        
        content = []
        for file in runbooks_dir.glob("*.md"):
            content.append(f"## Runbook: {file.stem}\n{file.read_text()}")
        
        return "\n\n".join(content)
    
    def _system_prompt(self) -> str:
        base = """You are a DevOps expert assistant in Slack. You help engineers:
- Understand Kubernetes errors and alerts
- Debug CI/CD pipeline failures  
- Explain cloud infrastructure concepts
- Suggest fixes for common DevOps problems
- Interpret monitoring alerts
 
Rules:
- Be concise — Slack messages should be scannable, not essays
- Use bullet points and code blocks for commands
- Give the most likely cause first, then alternatives
- Include the exact commands to diagnose/fix when possible
- Format code with backticks or triple backticks
- If you're not sure, say so and suggest who to escalate to"""
        
        if self.runbooks:
            base += f"\n\nTeam Runbooks (use these for context):\n{self.runbooks}"
        
        return base
    
    def answer(self, question: str) -> str:
        """Answer a general DevOps question"""
        message = self.client.messages.create(
            model="claude-opus-4-7",
            max_tokens=1024,
            system=self._system_prompt(),
            messages=[
                {"role": "user", "content": question}
            ]
        )
        return message.content[0].text
    
    def explain_error(self, error_text: str) -> str:
        """Explain an error message and suggest fixes"""
        prompt = (
            "Explain this error and how to fix it:\n\n"
            f"{error_text}\n\n"
            "Format your response as:\n"
            "**What happened:** (1-2 sentences)\n"
            "**Most likely cause:**\n"
            "**How to fix it:**\n"
            "1. ...\n"
            "2. ...\n"
            "**Commands to run:** (include bash commands)"
        )
 
        message = self.client.messages.create(
            model="claude-opus-4-7",
            max_tokens=1024,
            system=self._system_prompt(),
            messages=[
                {"role": "user", "content": prompt}
            ]
        )
        return message.content[0].text
    
    def summarize_alerts(self, alerts: list[dict]) -> str:
        """Summarize a list of alerts"""
        alerts_text = "\n".join([
            f"- [{a.get('severity', 'unknown')}] {a.get('name', '')}: {a.get('description', '')}"
            for a in alerts
        ])
        
        prompt = f"""Summarize these monitoring alerts and suggest priority order for investigation:
 
{alerts_text}
 
Group by: critical (fix now), warning (fix today), info (investigate when time allows)"""
 
        message = self.client.messages.create(
            model="claude-opus-4-7",
            max_tokens=512,
            system=self._system_prompt(),
            messages=[
                {"role": "user", "content": prompt}
            ]
        )
        return message.content[0].text

Step 3: Add Runbooks

Create a runbooks/ directory with your team's runbooks as markdown files:

markdown
# runbooks/high-memory.md
 
## High Memory Usage Alert
 
### Symptoms
- Alert: `MemoryUsage > 80%`
- Pod may show `OOMKilled` in describe output
 
### Investigation Steps
1. `kubectl top pods -n production | sort -k3 -hr`
2. `kubectl describe pod <pod-name> -n production | grep -A10 "Last State"`
3. Check recent deploys: `kubectl rollout history deployment/<name>`
 
### Common Causes
- Memory leak in the /export endpoint (large CSV generation)
- Uncached database queries returning large result sets
- Redis connection pool not being released
 
### Fix
- Short term: `kubectl rollout restart deployment/<name>`
- Long term: add Redis caching for expensive queries
 
### Escalation
If memory stays above 90% after restart, page the backend team.

The bot will automatically include these in its context.


Step 4: PagerDuty / Alertmanager Integration

python
# webhook_handler.py
from fastapi import FastAPI, Request
from ai_handler import DevOpsAI
from slack_sdk import WebClient
import os
 
fastapi_app = FastAPI()
slack_client = WebClient(token=os.getenv("SLACK_BOT_TOKEN"))
ai = DevOpsAI()
 
ALERT_CHANNEL = "#alerts"
 
@fastapi_app.post("/webhook/alertmanager")
async def handle_alertmanager(request: Request):
    """Receive Alertmanager webhook and post to Slack with AI analysis"""
    payload = await request.json()
    alerts = payload.get("alerts", [])
    
    if not alerts:
        return {"status": "no alerts"}
    
    # Get AI summary
    summary = ai.summarize_alerts([
        {
            "name": a["labels"].get("alertname"),
            "severity": a["labels"].get("severity"),
            "description": a["annotations"].get("description", "")
        }
        for a in alerts
    ])
    
    # Post to Slack
    slack_client.chat_postMessage(
        channel=ALERT_CHANNEL,
        blocks=[
            {
                "type": "header",
                "text": {"type": "plain_text", "text": f"🚨 {len(alerts)} Alert(s) Firing"}
            },
            {
                "type": "section",
                "text": {"type": "mrkdwn", "text": summary}
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "Ask me for help: `/ask how to investigate high memory` or DM me directly"
                }
            }
        ]
    )
    
    return {"status": "posted"}

Run It

bash
# Development (Socket Mode)
python bot.py
 
# With webhook server
uvicorn webhook_handler:fastapi_app --port 8080 &
python bot.py

Deploy to Kubernetes

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: devops-bot
  namespace: monitoring
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: bot
        image: your-registry/devops-bot:latest
        env:
        - name: SLACK_BOT_TOKEN
          valueFrom:
            secretKeyRef:
              name: slack-secrets
              key: bot-token
        - name: ANTHROPIC_API_KEY
          valueFrom:
            secretKeyRef:
              name: anthropic-secret
              key: api-key

Example Interactions

# Kubernetes error
/explain-error Error: ImagePullBackOff

# Alert investigation  
@devbot our CPU alert is firing on prod-api what should I check?

# General questions
@devbot what's the difference between liveness and readiness probes?

# Runbook lookup
@devbot the high memory runbook says to restart — what command?

What's Next

  • Add conversation history (multi-turn context per thread)
  • Connect to your metrics API for real-time data
  • Build a /status command showing cluster health
  • Add approval workflows for risky operations (e.g., /restart pod prod-api)

Anthropic Claude APIclaude-opus-4-7 handles complex technical questions with nuance. Perfect for DevOps assistants.

KodeKloud — if this project got you interested in Kubernetes + monitoring deeper, their SRE and platform engineering tracks are the best hands-on path.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments