I Built an AI Agent That Joins Docker + GitHub + Slack in One SQL Query — Here's How
A deep dive into Coral Watchdog: autonomous DevOps incident detection using Coral SQL, gpt-4o-mini, and a custom Docker source spec

I Built an AI Agent That Joins Docker + GitHub + Slack in One SQL Query
Built for the Pirates of the Coral-bean Hackathon · May 2026
It's 3 AM. Your phone buzzes. A container is down.
You open five different tabs — Docker logs, GitHub recent PRs, Slack for who's been notified, Sentry for errors, and your team's on-call schedule. By the time you've pieced together what happened and why, 20 minutes have passed. Your users have been staring at a broken screen the whole time.
What if a single SQL query could answer all of that instantly?
That's exactly what I set out to build. This is the story of Coral Watchdog — an autonomous DevOps incident agent I built for the Pirates of the Coral-bean hackathon by WeMakeDevs.
🪸 What is Coral?
Before we get into the build, let me introduce the technology that made this possible.
Coral is an open-source query layer that lets you run SQL across any API, database, or file — with cross-source JOINs. No ETL pipelines. No data engineering. No glue code.
Instead of writing this:
docker_data = docker_api.get_containers()
github_prs = github_api.list_pulls(owner, repo)
slack_msgs = slack_api.get_messages(channel)
# ...200 lines of joining, transforming, correlating...
You write this:
SELECT
dc.status,
gp.title AS last_pr,
gp.user__login AS deployed_by
FROM docker.containers dc
LEFT JOIN github.pulls gp
ON gp.owner = 'ghosthouse7' AND gp.repo = 'CORAL-WATCHDOG'
WHERE dc.status LIKE '%Exited%'
ORDER BY gp.merged_at DESC
LIMIT 10
One query. Three live data sources. Real-time results.
That's the magic. And it became the core of everything I built.
🏗️ The Architecture
Here's what Coral Watchdog does end-to-end:
Every 60 seconds:
↓
Docker Engine API → find unhealthy containers
↓
If found → run Coral SQL query
(joins Docker + GitHub + Slack simultaneously)
↓
Send context to gpt-4o-mini
↓
Root cause analysis generated
↓
Slack alert posted automatically
↓
Live dashboard updated at localhost:8080
And it's all written in Go — a single binary, no external dependencies beyond Coral itself.
🐳 The Custom Docker Source Spec
Here's where things got interesting.
Docker is not a built-in Coral source. So I had to teach Coral how to speak to the Docker Engine REST API.
Coral has a YAML-based DSL for defining custom sources. Here's a simplified version of what I built:
name: docker
version: 0.1.0
dsl_version: 3
backend: http
inputs:
DOCKER_API_BASE:
kind: variable
default: http://localhost:2375
base_url: "{{input.DOCKER_API_BASE}}"
tables:
- name: containers
description: "All Docker containers including stopped ones"
request:
method: GET
path: /containers/json?all=true
columns:
- name: id
type: Utf8
- name: image
type: Utf8
- name: status
type: Utf8
- name: state
type: Utf8
- name: created
type: Int64
With this spec added via coral source add --file ./sources/docker.yaml, I can now run:
SELECT id, image, status, state FROM docker.containers LIMIT 10
And get live container data as SQL rows. The Docker Engine becomes a queryable database. That's the power of Coral's source spec system.
🔗 The Star Query — Three Sources, One JOIN
This is the query that runs every time a container crashes. It's the centrepiece of the entire project:
SELECT
dc.id AS container_id,
dc.status AS container_status,
dc.image AS image,
gp.title AS last_merged_pr,
gp.merged_at AS deploy_time,
gp.user__login AS deployed_by,
sc.name AS slack_channel
FROM docker.containers dc
LEFT JOIN github.pulls gp
ON gp.owner = 'ghosthouse7'
AND gp.repo = 'CORAL-WATCHDOG'
AND gp.state = 'closed'
LEFT JOIN slack.channels sc
ON sc.name = 'social'
WHERE dc.status LIKE '%Exited%'
OR dc.status LIKE '%unhealthy%'
ORDER BY gp.merged_at DESC
LIMIT 20
In a single query, I instantly know:
✅ Which container crashed and what its current state is
✅ What PR was last merged (what code was deployed)
✅ Who deployed it
✅ Which Slack channel the incident will be reported to
Without Coral, this would be three separate API calls, authentication for each, 100+ lines of joining logic, and likely a microservice to maintain. With Coral, it's 25 lines of SQL.
🤖 The AI Root Cause Analysis
Once Coral returns the cross-source data, I pass everything to gpt-4o-mini via the GitHub Models API (free for students with the GitHub Student Pack — no credit card needed!):
func Summarize(ctx IncidentContext) (string, error) {
system := `You are a senior DevOps engineer analysing
production incidents. Be concise, specific, and focus
on actionable remediation.`
user := fmt.Sprintf(`
Container: %s
State: %s
CPU: %.1f%% Memory: %.1fMB
Last 50 log lines:
%s
Cross-source Coral query result:
%s`,
ctx.ContainerName,
ctx.ContainerState,
ctx.CPUPercent,
ctx.MemoryMB,
strings.Join(ctx.Logs, "\n"),
ctx.CoralQueryResult,
)
return callGHModels(system, user)
}
The model receives the container state, recent logs, memory/CPU stats, AND the cross-source Coral result — all in one context window. It comes back with a structured root cause analysis in seconds.
💬 The Slack Alert
The analysis gets posted directly to your incident channel:
🚨 Incident Detected — Container `jaeger` is `Exited (255)`
🐳 Container: jaeger
📦 Image: jaegertracing/all-in-one:latest
⚠️ Status: Exited (255)
🕐 Detected: 2026-05-28 03:12:43 UTC
🤖 AI Root Cause Analysis:
Root cause: OOM kill — the container exceeded its 512MB
memory limit after SAMPLING_RATE was increased from 10%
to 100% in PR #42. Memory usage spiked to 618MB.
Recommended fix: Increase memory limit to 1GB in your
docker-compose.yml, or revert the sampling rate change.
Prevention: Add memory alerts before hitting the limit.
Container ID: `a3f8c2d91b47`
Powered by Coral Watchdog 🏴☠️
Zero manual intervention. Zero context switching. The on-call engineer gets everything they need in one Slack message.
🖥️ The Dashboard
I also built a live web dashboard served directly from the Go binary:
Overview tab — container health, incident count, sources connected
Containers tab — full fleet status with CPU/memory bars
Incidents tab — AI-analysed incident feed with summaries
Query tab — the NL query interface (my favourite feature)
The dashboard lives at http://localhost:8080 and auto-refreshes every 30 seconds.
✨ The Natural Language Query Interface
The feature I'm most proud of: you type a question in plain English, it becomes a Coral SQL query, runs against your live infrastructure, and shows you results.
Type:
"which containers crashed after the last PR merge?"
gpt-4o-mini generates:
SELECT dc.id, dc.status, gp.title AS pr_title
FROM docker.containers dc
LEFT JOIN github.pulls gp
ON gp.owner = 'ghosthouse7' AND gp.state = 'closed'
WHERE dc.status LIKE '%Exited%'
ORDER BY gp.merged_at DESC
LIMIT 10
Coral runs it. Results appear. No SQL knowledge required.
This works because of a carefully crafted system prompt that knows the exact table schemas for Docker, GitHub, and Slack:
You are a Coral SQL expert. Convert natural language
to Coral SQL. Available tables:
- docker.containers: id, image, status, state, created
- github.pulls: title, state, merged_at, user__login
- slack.channels: id, name
RULES: Return ONLY the SQL. No backticks. LIMIT 20 always.
🔧 Tech Stack
| Layer | Technology |
|---|---|
| Language | Go 1.22 |
| Query Layer | Coral (custom Docker source spec) |
| AI | gpt-4o-mini via GitHub Models API |
| Notifications | Slack Incoming Webhooks |
| Dashboard | Vanilla HTML/CSS/JS (single file) |
| Sources | Docker Engine API, GitHub API, Slack API |
💡 Key Learnings
1. Coral's power is in the JOIN
Any single source is just an API wrapper. The magic happens when you JOIN Docker container state with the GitHub PR that deployed the code. Suddenly you have context that no single monitoring tool can give you.
2. Building a custom source spec is approachable
I had a working Docker source in about 2 hours. The YAML DSL is well-designed. The key mental model: think of any REST API as a database, and each endpoint as a table.
3. Students don't need to pay for AI
GitHub Student Pack gives you access to gpt-4o-mini and many other models through GitHub Models for free. No credit card, no API billing anxiety during a hackathon.
4. Go is perfect for this kind of agent
Single binary, excellent HTTP client, goroutines for the dashboard server running alongside the watcher. The whole agent compiles to one executable.
🚀 Try It Yourself
# Clone
git clone https://github.com/ghosthouse7/CORAL-WATCHDOG
cd CORAL-WATCHDOG
# Download coral.exe from github.com/withcoral/coral/releases
# Place in project folder
# Create .env with your tokens
cp .env.example .env # then fill in values
# Add Coral sources
.\coral source add github
.\coral source add slack
.\coral source add --file .\sources\docker.yaml
# Run
go run main.go
# Open dashboard
# http://localhost:8080
The full setup guide is in the README.
🏴☠️ What's Next
A few things I'd love to add:
Azure Monitor integration — join cloud metrics with container state
PagerDuty auto-escalation — create incidents automatically
Historical incident patterns — "this container crashes every Monday"
Slack slash commands —
/watchdog statusfrom any channel
Links
Coral: withcoral.com
Hackathon: wemakedevs.org/hackathons/coral
Built with 🏴☠️ for the Pirates of the Coral-bean hackathon. If this was useful, star the repo and drop a comment — I read every one!
— @ghosthouse7
