DEV Community

Emery Huang
Emery Huang

Posted on

Runbooks with a Pulse: Wiring Free AI Models to Your On-Call Alert Flow

Picture this: a 2:51 a.m. page hits your phone, and your only resource is a runbook that was written in a Google Doc three years ago. The database is cursing, the call is cryptic, and the escalation policy reads like a flowchart from a regulatory filing. I built a different kind of runbook—one that executes on a free server, calls MonkeyCode's free models for context, and replies with the first command before your coffee is even cold.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

The idea is simple: route every alert through a tiny serverless function that holds your runbook's decision table, parses the alert text, asks an LLM what to do, and returns a concrete starting point. MonkeyCode provides a free server to host that function and a ten-million-token allowance to power the LLM calls, so the entire experiment fits inside a free tier without leaking dollars into your incident budget.

Why a runbook should talk back

Most runbooks are passive documents; they sit in a wiki until someone remembers to open them during an incident. A runbook with a pulse, by contrast, accepts an alert payload, applies a freeze rule, and returns a command that a tired human can run without second-guessing the syntax. The goal is not to replace judgment—it's to compress the gap between a raw page and a useful action, especially when the alert text is ambiguous or the service topology has shifted since the last incident review.

The trick is treating the LLM as a circumstantial interpreter, not an oracle. You decide the paths, and the model helps classify the alert into one of those paths. That separation keeps behavior auditable while still letting natural language bridge the gap between what your monitoring says and what your engineers actually need to type.

The architecture in one diagram

A webhook from PagerDuty or Alertmanager lands on a small HTTP endpoint. That endpoint looks up the team's freeze window, extracts a few fields from the alert, and sends a compact prompt to MonkeyCode's model. The model returns a recommendation, and the function either prints it on the channel or sends it back to the ticketing system. Here's the shape I used:

# alert_bridge.py
import os, datetime, requests

def handler(alert):
    if is_frozen(alert):
        return {"action": "acknowledge", "message": "Change freeze in effect; no runbook action."}
    runbook = load_runbook_table()
    endpoint = os.getenv("MONKEYCODE_ENDPOINT")
    key = os.getenv("MONKEYCODE_KEY")
    prompt = build_prompt(alert, runbook)
    r = requests.post(endpoint, headers={"Authorization": f"Bearer {key}"}, json={"messages": prompt})
    return r.json()

def is_frozen(alert):
    now = datetime.datetime.now(datetime.timezone.utc)
    return now.month == 12 and now.hour < 8  # example freeze rule

def build_prompt(alert, runbook):
    return [
        {"role": "system", "content": "You turn alerts into runbook steps. Answer with one command and one escalation note."},
        {"role": "user", "content": f"Alert: {alert['text']}\nRunbook: {runbook}"}
    ]
Enter fullscreen mode Exit fullscreen mode

The function is deliberately short; the decision table does the heavy lifting, and the model only needs to map the alert to a row. I stored the table in a separate YAML file so non-engineers can review it during planning sessions.

A decision table for first response

Severity Signal First action Escalate if
P1 API error rate > 5% Restart the gateway service No recovery in 5 minutes
P2 Disk usage > 85% List largest files with du -h Growth continues after 30 minutes
P3 Latency p99 > 300 ms Check recent deploy and rollback one revision p99 > 800 ms for 10 minutes
P4 Metric noise Add a comment, do not notify humans Threshold breached twice in an hour

That table is the contract between monitoring and action. The LLM never invents new steps; it just selects the right row and formats the command with the current hostname and cluster ID from the alert payload.

Adding a freeze and unfreeze rule

Freezes are the Achilles' heel of automation—the same model that suggests a restart can easily push a production change during a release blackout. My freeze rule lives in the same handler and checks two dimensions: calendar windows and a cheap flag stored in a local file or a volatile key-value store. When a freeze is active, the bridge suppresses all actionable output and only sends an acknowledgment, which prevents the bot from acting on a system that is already in a fragile state.

# freeze.sh — toggle by writing a timestamp to a volume
curl -X POST http://localhost:8080/freeze -d '{"enabled": true, "until": "2026-09-05T00:00:00Z"}'
Enter fullscreen mode Exit fullscreen mode

The refreeze path is just as simple: the handler checks until on every invocation and auto-clears itself when the timestamp passes. I added an explicit unfreeze endpoint for emergencies, because relying on time math alone is a quiet way to wake up in the middle of a freeze and discover the rule never turned off.

Deploying to a free server without losing your weekend

MonkeyCode's free server tier is meant for experiments, not production traffic, and that's exactly what this runbook bridge deserves. I deployed the function as a lightweight container and pointed a free HTTPS domain at it, then added webhook URLs from two monitoring platforms. The whole process, including writing the Dockerfile and configuring environment variables, took about twenty minutes.

FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install flask requests pyyaml
CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

Expose the port, bind the webhook endpoints, and test with a fake alert using curl:

curl -X POST http://localhost:8080/alert \
  -H "Content-Type: application/json" \
  -d '{"text": "disk usage 87% on db-01", "severity": "P2"}'
Enter fullscreen mode Exit fullscreen mode

A sane response would be: du -h /var/lib/mysql | sort -hr | head -20 plus a note to check slow queries. If the model returns something off-topic, you tighten the system prompt or simplify the decision table.

Where this approach breaks

This free-tier setup is not for organizations with strict audit requirements or for alerts that demand a human's hands-on debugging before a command runs. The token allowance resets periodically, so high-volume incident streams could exhaust it mid-crisis, and the server has no SLA, so do not place it on the critical path for life-or-death infrastructure. Also, the model can misread a terse alert even with a good table; that is why the output is a suggestion, not an action that executes automatically.

I also would not use this for regulatory compliance incidents where every step needs a signed-off owner. Keep the AI as a front-line interpreter, not a decision authority, and always log the raw prompt and response for post-incident review.

Try it with your own alerts

The pain of a static runbook is universal, and the fix is now cheap enough to test in a spare afternoon. Pull the MonkeyCode project, grab your free token allowance, and point a webhook at this tiny bridge—then let the first page you receive be a reminder that on-call exhaustion is optional.

Top comments (0)