AI-Powered DevOps Incident Copilot
Build an AI copilot that analyzes Kubernetes, Docker, and Jenkins logs.
Introduction
30 Second Summary
When something breaks in production, engineers scramble through walls of error logs trying to figure out what went wrong and how to fix it. The faster you can diagnose an incident, the less downtime your users experience.
In this project, you will build an AI-powered DevOps Incident Copilot that analyzes error logs from Kubernetes, Docker, and Jenkins, then instantly provides root cause analysis, severity classification, and remediation steps.
What You'll Build
A full-stack web application that accepts pasted error logs and uses AI to deliver structured incident reports with failure explanations, actionable fix commands, and relevant diagnostic commands.
By the end of this project, you'll have:
- A Next.js web application with a log analysis interface deployed live on Vercel.
- An API route that streams AI-powered analysis using Groq's Llama 3.3 70B model.
- A built-in DevOps runbook (knowledge base) that gives the AI expert-level context for diagnosing Kubernetes, Docker, and Jenkins incidents.
- Secret Mission: Add incident history with local storage so users can review past analyses.
Are there any prerequisites?
You need a GitHub account and VS Code installed on your computer. No prior JavaScript or DevOps experience is required.
Set Up Your Development Environment
Before you can build your AI-powered incident copilot, you need three things on your machine: a JavaScript runtime, an AI API key, and a project scaffold.
In this step, you will install Node.js, grab a free Groq API key, and scaffold a Next.js project that will become your incident analysis tool.
In this step, get ready to:
- Install Node.js and verify it works.
- Create a free Groq API key for AI-powered analysis.
- Scaffold a Next.js project called incident-copilot.
Install Node.js
Node.js is the JavaScript runtime that powers Next.js. Your project requires Node.js version 22 or higher because the AI SDK library depends on it.
- Open a terminal in VS Code by pressing Ctrl+` (macOS and Windows).
- Check if Node.js is already installed by running this command:
node -v
✔️ I see version 22 or higher
You are good to go. Your Node.js version satisfies the project requirements.
- Confirm npm is also available by running:
npm -v
You should see a version number printed. Both tools are ready.
ⓧ I see an older version
You have Node.js installed, but it is below version 22. The AI SDK package this project uses requires Node.js 22 or higher.
- Visit https://nodejs.org/en/download in your browser.
- Download the LTS installer (v24.18.0) for your operating system.
- Run the installer and accept all default options.
- Close and reopen your VS Code terminal so the new version takes effect.
- Run node -v again to confirm you see v24 or higher.
macOS users with an older system Node
If node -v still shows the old version after installing, your system may be resolving a previously installed version. Try running /usr/local/bin/node -v to confirm the new install is present, then restart your terminal.
ⓧ Command not found
Node.js is not installed on your machine yet. Let's install it now.
- Visit https://nodejs.org/en/download in your browser.
- Download the LTS installer (v24.18.0) for your operating system.
- Run the installer and accept all default options.
- Close and reopen your VS Code terminal so the PATH updates take effect.
- Run node -v to confirm you see v24 or higher.
- Run npm -v to confirm npm is also available.
Still seeing command not found?
Make sure you fully closed and reopened your terminal after installing. If the issue persists, try restarting VS Code entirely.
help me troubleshoot Node.js not being recognized after installation
Create a free Groq API key
Groq provides ultra-fast AI inference. You will use their free tier to power your incident copilot with the Llama 3.3 70B model. No credit card is required.
- Open https://console.groq.com/keys in your browser.
- Sign up for a free account or sign in if you already have one.
- Click Create API Key.
- Give it any name (for example, incident-copilot).
- Copy the key immediately and paste it somewhere safe (a notes app or password manager). You will not be able to see the full key again after closing the dialog.
Why Groq?
Groq's free tier gives you access to powerful large language models with no charges. If you exceed the rate limits (30 requests per minute for Llama 3.3 70B), you will receive a 429 error instead of a surprise bill.
Having trouble creating an account?
Groq supports sign-up with Google or GitHub. If one method has issues, try the other.
help me sign up for a Groq account and create an API key
Scaffold the Next.js project
Next.js is a full-stack React framework that handles routing, server-side rendering, and API routes out of the box. You will use it to build both the frontend interface and the backend AI endpoint.
- Move to your Desktop by running this command:
cd ~/Desktop
- Scaffold your project by running this command:
npx create-next-app@latest incident-copilot --typescript --tailwind --use-npm
What will this command ask you?
The CLI will first ask if you want to proceed with installing the create-next-app package. Type y and press Enter to confirm.
Next, it will ask whether you want to use the recommended defaults. Select Yes, use recommended defaults and press Enter. This sets up TypeScript, ESLint, Tailwind CSS, and the App Router for you automatically.
- Once scaffolding finishes, move into your new project folder by running:
cd incident-copilot
- Confirm the project was created by running:
ls
You should see files like package.json, tsconfig.json, and a src folder in the output.
Seeing an error during scaffolding?
Make sure your Node.js version is 22 or higher by running node -v. The create-next-app tool and its dependencies require a modern Node.js runtime.
help me fix errors when running npx create-next-app
Your environment is ready. Next up, you will build the API route that analyzes error logs using AI.
Build the Log Analysis API Route
Your project is scaffolded and your Groq API key is saved. Now it's time to build the core intelligence behind your copilot.
When an engineer pastes error logs, your app needs to understand what went wrong and suggest specific fixes. You'll create an API route that sends those logs to Groq's Llama 3.3 70B model, along with a DevOps runbook containing common failure patterns.
In this step, get ready to:
- Install the Vercel AI SDK packages for streaming AI responses.
- Create a DevOps runbook knowledge base with common failure patterns.
- Build an API route that streams AI-powered incident analysis.
Install the AI SDK packages
The Vercel AI SDK provides streaming helpers that let your frontend display AI responses word by word as they arrive. The @ai-sdk/groq package connects the SDK to Groq's ultra-fast inference API.
- Install both packages by running this command:
npm install ai @ai-sdk/groq
- The install should complete in a few seconds with no errors.
- You should see ai and @ai-sdk/groq listed under dependencies in your package.json file.
Seeing an error during install?
Make sure your terminal is inside the incident-copilot folder. Run pwd (macOS/Linux) or cd (Windows) to check your current directory.
If you see network errors, check your internet connection and try again.
Help me troubleshoot this install error
Create the DevOps runbook knowledge base
Your AI model is powerful, but it works even better with domain-specific context. A runbook is a reference guide that DevOps teams keep for common incident patterns and their fixes.
You'll create a runbook file that gets injected into every AI prompt as additional context. This technique is called RAG (Retrieval-Augmented Generation).
Why a hardcoded runbook instead of a vector database?
Full RAG systems use vector databases like Pinecone or Weaviate to dynamically retrieve relevant context. That approach adds significant complexity and cost.
For a portfolio demo with a fixed set of failure patterns, injecting a hardcoded runbook into the system prompt teaches the same concept (providing domain knowledge to an LLM) without the infrastructure overhead.
- In VS Code, create a new file at src/app/api/analyze/runbook.ts.
- Right-click the src/app folder in the Explorer sidebar, select New File, and type api/analyze/runbook.ts. VS Code creates the intermediate folders automatically.
- Paste the following code into runbook.ts:
export const devopsRunbook = `
## Kubernetes Common Failures
### CrashLoopBackOff
- Cause: Container repeatedly crashes after starting
- Common reasons: Application error, missing config/secrets, insufficient resources, failed health checks
- Commands: kubectl describe pod <pod-name> -n <namespace>, kubectl logs <pod-name> -n <namespace> --previous
- Fix: Check logs for application errors, verify ConfigMaps/Secrets exist, increase resource limits
### ImagePullBackOff
- Cause: Kubernetes cannot pull the container image
- Common reasons: Wrong image name/tag, private registry without credentials, network issues
- Commands: kubectl describe pod <pod-name> -n <namespace>, kubectl get events -n <namespace>
- Fix: Verify image name and tag, create/check imagePullSecrets, test registry connectivity
### OOMKilled
- Cause: Container exceeded its memory limit
- Commands: kubectl describe pod <pod-name> -n <namespace>, kubectl top pods -n <namespace>
- Fix: Increase memory limits in deployment spec, optimize application memory usage, check for memory leaks
### Pod Evicted
- Cause: Node under resource pressure (disk, memory)
- Commands: kubectl describe node <node-name>, kubectl get events --field-selector reason=Evicted
- Fix: Clean up disk space, add resource requests/limits, consider node scaling
## Docker Common Failures
### Container Exit Code 137
- Cause: Container killed by OOM killer or SIGKILL
- Commands: docker inspect <container-id>, docker stats
- Fix: Increase memory limits in docker run or compose, optimize application memory
### Container Exit Code 1
- Cause: Application error during startup or runtime
- Commands: docker logs <container-id>, docker inspect <container-id>
- Fix: Check application logs, verify environment variables, test entrypoint/cmd locally
### Network Connectivity Issues
- Cause: Containers cannot communicate
- Commands: docker network ls, docker network inspect <network>, docker exec <container> ping <target>
- Fix: Ensure containers are on the same network, check DNS resolution, verify port mappings
### Build Failures
- Cause: Dockerfile errors during image build
- Commands: docker build --no-cache -t <tag> ., docker history <image>
- Fix: Check Dockerfile syntax, verify base image exists, ensure build context includes required files
## Jenkins Pipeline Common Failures
### Build Timeout
- Cause: Pipeline exceeds configured timeout
- Fix: Increase timeout in pipeline config, optimize build steps, check for hanging processes
### Agent Connection Failed
- Cause: Jenkins agent/node is offline or unreachable
- Fix: Check agent connectivity, restart agent service, verify network/firewall rules
### Git Clone Failed
- Cause: Cannot clone source repository
- Common reasons: Invalid credentials, network issues, repository moved/deleted
- Fix: Update credentials in Jenkins, verify repository URL, check network connectivity
### Test Failures
- Cause: Unit/integration tests failing in pipeline
- Fix: Review test output, check for environment-specific issues, verify test dependencies are installed
### Permission Denied
- Cause: Insufficient permissions for file/command operations
- Fix: Check workspace permissions, verify user running Jenkins agent, update file ownership
`;
What does this code do?
This file exports a single string constant called devopsRunbook that contains structured troubleshooting knowledge for three platforms:
- Kubernetes failures (CrashLoopBackOff, ImagePullBackOff, OOMKilled, Pod Evicted).
- Docker failures (exit codes, network issues, build failures).
- Jenkins pipeline failures (timeouts, agent issues, test failures).
Each entry lists the cause, relevant diagnostic commands, and a fix. The AI model uses this context to give more specific and actionable advice than it would from general training data alone.
- Save runbook.ts.
- In the VS Code Explorer sidebar, expand the src folder, then app, then api, then analyze. You should see runbook.ts listed there.
Don't see the analyze folder?
Make sure you typed the full path api/analyze/runbook.ts when creating the file. If VS Code did not create the intermediate folders, create them manually by right-clicking src/app and selecting New Folder for each level.
Help me create the runbook file in the right location
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
export const devopsRunbook = `
## Kubernetes Common Failures
### CrashLoopBackOff
- Cause: Container repeatedly crashes after starting
- Common reasons: Application error, missing config/secrets, insufficient resources, failed health checks
- Commands: kubectl describe pod <pod-name> -n <namespace>, kubectl logs <pod-name> -n <namespace> --previous
- Fix: Check logs for application errors, verify ConfigMaps/Secrets exist, increase resource limits
### ImagePullBackOff
- Cause: Kubernetes cannot pull the container image
- Common reasons: Wrong image name/tag, private registry without credentials, network issues
- Commands: kubectl describe pod <pod-name> -n <namespace>, kubectl get events -n <namespace>
- Fix: Verify image name and tag, create/check imagePullSecrets, test registry connectivity
### OOMKilled
- Cause: Container exceeded its memory limit
- Commands: kubectl describe pod <pod-name> -n <namespace>, kubectl top pods -n <namespace>
- Fix: Increase memory limits in deployment spec, optimize application memory usage, check for memory leaks
### Pod Evicted
- Cause: Node under resource pressure (disk, memory)
- Commands: kubectl describe node <node-name>, kubectl get events --field-selector reason=Evicted
- Fix: Clean up disk space, add resource requests/limits, consider node scaling
## Docker Common Failures
### Container Exit Code 137
- Cause: Container killed by OOM killer or SIGKILL
- Commands: docker inspect <container-id>, docker stats
- Fix: Increase memory limits in docker run or compose, optimize application memory
### Container Exit Code 1
- Cause: Application error during startup or runtime
- Commands: docker logs <container-id>, docker inspect <container-id>
- Fix: Check application logs, verify environment variables, test entrypoint/cmd locally
### Network Connectivity Issues
- Cause: Containers cannot communicate
- Commands: docker network ls, docker network inspect <network>, docker exec <container> ping <target>
- Fix: Ensure containers are on the same network, check DNS resolution, verify port mappings
### Build Failures
- Cause: Dockerfile errors during image build
- Commands: docker build --no-cache -t <tag> ., docker history <image>
- Fix: Check Dockerfile syntax, verify base image exists, ensure build context includes required files
## Jenkins Pipeline Common Failures
### Build Timeout
- Cause: Pipeline exceeds configured timeout
- Fix: Increase timeout in pipeline config, optimize build steps, check for hanging processes
### Agent Connection Failed
- Cause: Jenkins agent/node is offline or unreachable
- Fix: Check agent connectivity, restart agent service, verify network/firewall rules
### Git Clone Failed
- Cause: Cannot clone source repository
- Common reasons: Invalid credentials, network issues, repository moved/deleted
- Fix: Update credentials in Jenkins, verify repository URL, check network connectivity
### Test Failures
- Cause: Unit/integration tests failing in pipeline
- Fix: Review test output, check for environment-specific issues, verify test dependencies are installed
### Permission Denied
- Cause: Insufficient permissions for file/command operations
- Fix: Check workspace permissions, verify user running Jenkins agent, update file ownership
`;
Create the API route handler
Next.js App Router uses file-based routing for API endpoints. A file at src/app/api/analyze/route.ts automatically becomes accessible at /api/analyze.
Your route will accept log text, combine it with the runbook context, and stream a structured analysis back to the browser using Groq's Llama 3.3 70B model.
- Create a new file at src/app/api/analyze/route.ts (in the same analyze folder as your runbook).
- Paste the following code into route.ts:
import { groq } from '@ai-sdk/groq';
import { streamText } from 'ai';
import { devopsRunbook } from './runbook';
export const maxDuration = 30;
export async function POST(req: Request) {
const { logs } = await req.json();
const systemPrompt = `You are an expert DevOps incident response copilot. You analyze error logs from Kubernetes, Docker, and Jenkins environments.
When given logs, you must provide a structured incident analysis in the following format:
## Source Detected
[Kubernetes | Docker | Jenkins]
## Severity
[CRITICAL | HIGH | MEDIUM | LOW]
## Summary
[One-sentence description of the incident]
## What Went Wrong
[Plain-English explanation of the failure, suitable for engineers of all levels]
## Root Cause Analysis
[Detailed technical explanation of why this happened]
## Remediation Steps
[Numbered list of specific steps to fix the issue]
## Relevant Commands
[Exact commands the engineer should run to diagnose or fix the issue]
## Prevention
[How to prevent this from happening again]
Use the following DevOps runbook as reference for common failure patterns and remediation:
${devopsRunbook}
Be specific, actionable, and direct. Do not hedge or add unnecessary disclaimers.`;
const result = streamText({
model: groq('llama-3.3-70b-versatile'),
system: systemPrompt,
prompt: `Analyze these logs and provide a structured incident report:\n\n${logs}`,
});
return result.toDataStreamResponse();
}
What does this code do?
- groq('llama-3.3-70b-versatile') selects Groq's fastest large language model for the analysis.
- streamText() sends the prompt to the model and returns a streaming response. The browser receives tokens as they're generated instead of waiting for the full answer.
- systemPrompt instructs the model to produce a structured incident report with sections like Severity, Root Cause Analysis, and Remediation Steps. It also injects the full runbook as context.
- maxDuration = 30 tells the deployment platform (Vercel) to allow this function up to 30 seconds to complete, instead of the default timeout.
- toDataStreamResponse() converts the streaming result into a format the Vercel AI SDK's frontend hooks can consume.
The GROQ_API_KEY environment variable is read automatically by the @ai-sdk/groq package. You'll set that up in a later step.
- Save route.ts.
- In the Explorer sidebar, you should now see both runbook.ts and route.ts inside the src/app/api/analyze folder.
Seeing red squiggly lines in VS Code?
TypeScript may briefly show errors while it indexes the new packages. Wait a few seconds for VS Code to catch up. If errors persist, make sure you ran npm install ai @ai-sdk/groq successfully in the previous substep.
Also confirm that route.ts imports from './runbook' (with the ./ prefix). Without it, TypeScript cannot find the local file.
Help me fix TypeScript errors in my route file
✔️ Awesome, I've got everything!
Great. Make sure both files are saved before moving on.
ⓧ I'd like to double check the full code
import { groq } from '@ai-sdk/groq';
import { streamText } from 'ai';
import { devopsRunbook } from './runbook';
export const maxDuration = 30;
export async function POST(req: Request) {
const { logs } = await req.json();
const systemPrompt = `You are an expert DevOps incident response copilot. You analyze error logs from Kubernetes, Docker, and Jenkins environments.
When given logs, you must provide a structured incident analysis in the following format:
## Source Detected
[Kubernetes | Docker | Jenkins]
## Severity
[CRITICAL | HIGH | MEDIUM | LOW]
## Summary
[One-sentence description of the incident]
## What Went Wrong
[Plain-English explanation of the failure, suitable for engineers of all levels]
## Root Cause Analysis
[Detailed technical explanation of why this happened]
## Remediation Steps
[Numbered list of specific steps to fix the issue]
## Relevant Commands
[Exact commands the engineer should run to diagnose or fix the issue]
## Prevention
[How to prevent this from happening again]
Use the following DevOps runbook as reference for common failure patterns and remediation:
${devopsRunbook}
Be specific, actionable, and direct. Do not hedge or add unnecessary disclaimers.`;
const result = streamText({
model: groq('llama-3.3-70b-versatile'),
system: systemPrompt,
prompt: `Analyze these logs and provide a structured incident report:\n\n${logs}`,
});
return result.toDataStreamResponse();
}
Your API backend is built. Next up, you'll create the frontend interface where users paste their logs and watch the AI analysis stream in real time.
Build the Incident Copilot Interface
Your API route is ready to receive logs and stream back AI-powered analysis. Now you need a front-end where engineers can paste their error logs and watch the incident report stream in live.
You'll build a React interface using the Vercel AI SDK's useCompletion hook. This hook handles the streaming connection to your API route automatically, so you can focus on the UI.
In this step, get ready to:
- Build the log analysis form with a textarea, submit button, and streaming results panel.
- Add sample log buttons for Kubernetes, Docker, and Jenkins testing.
- Update the app layout with project metadata and a dark background.
Create the copilot page
The default Next.js page has placeholder content. You'll replace it entirely with your copilot interface containing sample log data, a form, and a results panel.
- In VS Code, open src/app/page.tsx from the Explorer sidebar.
- Select all the existing content and delete it.
- Add the imports and sample log data by pasting this code:
'use client';
import { useState } from 'react';
import { useCompletion } from 'ai/react';
const sampleLogs = {
kubernetes: `NAMESPACE NAME READY STATUS RESTARTS AGE
default api-gateway-7d4f8b6c95-xk2m9 0/1 CrashLoopBackOff 5 (32s ago) 8m
default api-gateway-7d4f8b6c95-p3n7z 0/1 CrashLoopBackOff 5 (28s ago) 8m
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8m default-scheduler Successfully assigned default/api-gateway-7d4f8b6c95-xk2m9
Normal Pulled 7m (x5 over 8m) kubelet Container image "myregistry/api-gateway:v2.1.0" already present
Normal Created 7m (x5 over 8m) kubelet Created container api-gateway
Normal Started 7m (x5 over 8m) kubelet Started container api-gateway
Warning BackOff 32s (x25 over 8m) kubelet Back-off restarting failed container
Container Logs (previous):
Error: Cannot find module '/app/config/database.yml'
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1075:15)
Process exited with code 1`,
docker: `2024-03-15T14:32:01.234Z ERROR [payment-service] Container unhealthy
docker inspect payment-service-1:
"State": {
"Status": "exited",
"ExitCode": 137,
"OOMKilled": true,
"Error": ""
}
docker stats (before crash):
CONTAINER CPU % MEM USAGE / LIMIT MEM %
payment-service-1 45.2% 1.98GiB / 2GiB 99.1%
payment-service-2 38.7% 1.85GiB / 2GiB 92.5%
docker logs payment-service-1 --tail 20:
[2024-03-15T14:31:45Z] Processing batch job: 15000 transactions
[2024-03-15T14:31:58Z] Memory allocation failed: Cannot allocate 256MB for transaction cache
[2024-03-15T14:32:00Z] FATAL: Out of memory. Heap size exceeded.
Killed`,
jenkins: `Started by timer
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on agent-linux-02 in /var/jenkins/workspace/deploy-prod
[Pipeline] stage (Build)
[Pipeline] sh
+ npm ci
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! Found: react@18.2.0
npm ERR! Could not resolve dependency: peer react@"^19.0.0" from @auth/nextjs@5.0.0
[Pipeline] stage (Test)
Stage "Test" skipped due to earlier failure
[Pipeline] stage (Deploy)
Stage "Deploy" skipped due to earlier failure
ERROR: script returned exit code 1
Finished: FAILURE
Build duration: 2 min 34 sec`,
};
What does this code do?
'use client' marks this as a client-side React component (required for hooks like useState). The useCompletion import comes from the Vercel AI SDK and handles streaming responses from your API route.
The sampleLogs object contains realistic error logs from three platforms. These let users test the copilot instantly without needing real production logs.
- Below the sampleLogs object, add the Home component by pasting this code:
export default function Home() {
const [logs, setLogs] = useState('');
const { completion, complete, isLoading } = useCompletion({
api: '/api/analyze',
body: { logs },
});
const handleAnalyze = async (e: React.FormEvent) => {
e.preventDefault();
if (!logs.trim()) return;
await complete(logs);
};
const loadSample = (type: keyof typeof sampleLogs) => {
setLogs(sampleLogs[type]);
};
const getSeverityColor = (text: string) => {
if (text.includes('CRITICAL')) return 'text-red-500';
if (text.includes('HIGH')) return 'text-orange-500';
if (text.includes('MEDIUM')) return 'text-yellow-500';
if (text.includes('LOW')) return 'text-green-500';
return '';
};
return (
<main className="min-h-screen bg-gray-950 text-gray-100 p-6">
<div className="max-w-4xl mx-auto">
<header className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">
DevOps Incident Copilot
</h1>
<p className="text-gray-400">
Paste your error logs and get instant AI-powered incident analysis with root cause and remediation steps.
</p>
</header>
<form onSubmit={handleAnalyze} className="mb-8">
<div className="mb-4">
<div className="flex gap-2 mb-3">
<span className="text-sm text-gray-500 self-center">Try a sample:</span>
<button
type="button"
onClick={() => loadSample('kubernetes')}
className="px-3 py-1 text-xs rounded bg-blue-900 text-blue-300 hover:bg-blue-800"
>
Kubernetes
</button>
<button
type="button"
onClick={() => loadSample('docker')}
className="px-3 py-1 text-xs rounded bg-purple-900 text-purple-300 hover:bg-purple-800"
>
Docker
</button>
<button
type="button"
onClick={() => loadSample('jenkins')}
className="px-3 py-1 text-xs rounded bg-amber-900 text-amber-300 hover:bg-amber-800"
>
Jenkins
</button>
</div>
<textarea
value={logs}
onChange={(e) => setLogs(e.target.value)}
placeholder="Paste your Kubernetes, Docker, or Jenkins error logs here..."
className="w-full h-48 p-4 bg-gray-900 border border-gray-700 rounded-lg text-sm font-mono text-gray-300 placeholder-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y"
/>
</div>
<button
type="submit"
disabled={isLoading || !logs.trim()}
className="px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
{isLoading ? 'Analyzing...' : 'Analyze Incident'}
</button>
</form>
{completion && (
<section className="bg-gray-900 border border-gray-700 rounded-lg p-6">
<h2 className="text-xl font-semibold text-white mb-4">
Incident Analysis
</h2>
<div
className={`prose prose-invert prose-sm max-w-none whitespace-pre-wrap font-mono text-sm leading-relaxed ${getSeverityColor(completion)}`}
>
{completion}
</div>
</section>
)}
</div>
</main>
);
}
What does this component do?
- useCompletion connects to your /api/analyze route and streams the AI response back. It gives you completion (the streamed text), complete (function to trigger a request), and isLoading (loading state).
- handleAnalyze prevents the form from submitting empty logs and calls complete() to trigger the API request.
- loadSample fills the textarea with a pre-built error log so users can test instantly.
- getSeverityColor applies Tailwind CSS color classes based on the severity level detected in the response (red for CRITICAL, orange for HIGH, etc.).
Why useCompletion instead of useChat?
Your copilot is a one-shot analysis tool, not a multi-turn conversation. The useCompletion hook is designed for exactly this pattern: send a single prompt, stream a single response. The useChat hook manages message history for back-and-forth conversations, which would add unnecessary complexity here.
- Save src/app/page.tsx.
You won't see a visual change yet because the dev server isn't running. The next chunk sets up the layout, and you'll test everything in the next step.
Seeing red squiggles in VS Code?
If VS Code shows TypeScript errors on the useCompletion import, make sure you installed the ai package in the previous step. Run npm install ai @ai-sdk/groq again if needed.
If errors appear on JSX elements, ensure the very first line of the file is 'use client'; with no blank lines above it.
Help me debug TypeScript errors in page.tsx
✔️ Awesome, I've got everything!
Great. Double check you've saved src/app/page.tsx before moving on.
ⓧ I'd like to double check the full code
'use client';
import { useState } from 'react';
import { useCompletion } from 'ai/react';
const sampleLogs = {
kubernetes: `NAMESPACE NAME READY STATUS RESTARTS AGE
default api-gateway-7d4f8b6c95-xk2m9 0/1 CrashLoopBackOff 5 (32s ago) 8m
default api-gateway-7d4f8b6c95-p3n7z 0/1 CrashLoopBackOff 5 (28s ago) 8m
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8m default-scheduler Successfully assigned default/api-gateway-7d4f8b6c95-xk2m9
Normal Pulled 7m (x5 over 8m) kubelet Container image "myregistry/api-gateway:v2.1.0" already present
Normal Created 7m (x5 over 8m) kubelet Created container api-gateway
Normal Started 7m (x5 over 8m) kubelet Started container api-gateway
Warning BackOff 32s (x25 over 8m) kubelet Back-off restarting failed container
Container Logs (previous):
Error: Cannot find module '/app/config/database.yml'
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1075:15)
Process exited with code 1`,
docker: `2024-03-15T14:32:01.234Z ERROR [payment-service] Container unhealthy
docker inspect payment-service-1:
"State": {
"Status": "exited",
"ExitCode": 137,
"OOMKilled": true,
"Error": ""
}
docker stats (before crash):
CONTAINER CPU % MEM USAGE / LIMIT MEM %
payment-service-1 45.2% 1.98GiB / 2GiB 99.1%
payment-service-2 38.7% 1.85GiB / 2GiB 92.5%
docker logs payment-service-1 --tail 20:
[2024-03-15T14:31:45Z] Processing batch job: 15000 transactions
[2024-03-15T14:31:58Z] Memory allocation failed: Cannot allocate 256MB for transaction cache
[2024-03-15T14:32:00Z] FATAL: Out of memory. Heap size exceeded.
Killed`,
jenkins: `Started by timer
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on agent-linux-02 in /var/jenkins/workspace/deploy-prod
[Pipeline] stage (Build)
[Pipeline] sh
+ npm ci
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! Found: react@18.2.0
npm ERR! Could not resolve dependency: peer react@"^19.0.0" from @auth/nextjs@5.0.0
[Pipeline] stage (Test)
Stage "Test" skipped due to earlier failure
[Pipeline] stage (Deploy)
Stage "Deploy" skipped due to earlier failure
ERROR: script returned exit code 1
Finished: FAILURE
Build duration: 2 min 34 sec`,
};
export default function Home() {
const [logs, setLogs] = useState('');
const { completion, complete, isLoading } = useCompletion({
api: '/api/analyze',
body: { logs },
});
const handleAnalyze = async (e: React.FormEvent) => {
e.preventDefault();
if (!logs.trim()) return;
await complete(logs);
};
const loadSample = (type: keyof typeof sampleLogs) => {
setLogs(sampleLogs[type]);
};
const getSeverityColor = (text: string) => {
if (text.includes('CRITICAL')) return 'text-red-500';
if (text.includes('HIGH')) return 'text-orange-500';
if (text.includes('MEDIUM')) return 'text-yellow-500';
if (text.includes('LOW')) return 'text-green-500';
return '';
};
return (
<main className="min-h-screen bg-gray-950 text-gray-100 p-6">
<div className="max-w-4xl mx-auto">
<header className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">
DevOps Incident Copilot
</h1>
<p className="text-gray-400">
Paste your error logs and get instant AI-powered incident analysis with root cause and remediation steps.
</p>
</header>
<form onSubmit={handleAnalyze} className="mb-8">
<div className="mb-4">
<div className="flex gap-2 mb-3">
<span className="text-sm text-gray-500 self-center">Try a sample:</span>
<button
type="button"
onClick={() => loadSample('kubernetes')}
className="px-3 py-1 text-xs rounded bg-blue-900 text-blue-300 hover:bg-blue-800"
>
Kubernetes
</button>
<button
type="button"
onClick={() => loadSample('docker')}
className="px-3 py-1 text-xs rounded bg-purple-900 text-purple-300 hover:bg-purple-800"
>
Docker
</button>
<button
type="button"
onClick={() => loadSample('jenkins')}
className="px-3 py-1 text-xs rounded bg-amber-900 text-amber-300 hover:bg-amber-800"
>
Jenkins
</button>
</div>
<textarea
value={logs}
onChange={(e) => setLogs(e.target.value)}
placeholder="Paste your Kubernetes, Docker, or Jenkins error logs here..."
className="w-full h-48 p-4 bg-gray-900 border border-gray-700 rounded-lg text-sm font-mono text-gray-300 placeholder-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y"
/>
</div>
<button
type="submit"
disabled={isLoading || !logs.trim()}
className="px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
{isLoading ? 'Analyzing...' : 'Analyze Incident'}
</button>
</form>
{completion && (
<section className="bg-gray-900 border border-gray-700 rounded-lg p-6">
<h2 className="text-xl font-semibold text-white mb-4">
Incident Analysis
</h2>
<div
className={`prose prose-invert prose-sm max-w-none whitespace-pre-wrap font-mono text-sm leading-relaxed ${getSeverityColor(completion)}`}
>
{completion}
</div>
</section>
)}
</div>
</main>
);
}
Update the app layout
The layout file wraps every page in your app. You'll update it with a proper title and a dark background color that matches the copilot theme.
- Open src/app/layout.tsx in VS Code.
- Replace the entire file contents with this code:
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'DevOps Incident Copilot',
description: 'AI-powered log analysis for Kubernetes, Docker, and Jenkins incidents',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="bg-gray-950">{children}</body>
</html>
);
}
What does this code do?
- The metadata export sets the page title and description that appear in the browser tab and search results.
- bg-gray-950 on the <body> ensures the entire page has a consistent dark background, even during page transitions.
- The globals.css import pulls in Tailwind's utility classes so they work across all pages.
- Save src/app/layout.tsx.
You won't see a visual change yet because the dev server isn't running. You'll start it and test the full interface in the next step.
✔️ Awesome, I've got everything!
Great. Double check you've saved src/app/layout.tsx before moving on.
ⓧ I'd like to double check the full code
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'DevOps Incident Copilot',
description: 'AI-powered log analysis for Kubernetes, Docker, and Jenkins incidents',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="bg-gray-950">{children}</body>
</html>
);
}
Your interface is ready. Next up, you'll connect everything with your API key, test locally, and deploy it live to Vercel.
Deploy to Vercel
You have a complete DevOps Incident Copilot with a working API route, a built-in runbook, and a professional interface. Now it is time to take your app live.
In this step, you will connect your Groq API key for local testing, push your code to GitHub, and deploy the finished application to Vercel so anyone can access it via a public URL.
In this step, get ready to:
- Test the copilot locally with your Groq API key.
- Push your project to a GitHub repository.
- Deploy to Vercel and get a live public URL.
Create your environment file and test locally
Your API route reads the Groq API key from an environment variable called GROQ_API_KEY. You need to create a local environment file so the app can access your key during development.
- In VS Code, create a new file called .env.local in the root of your incident-copilot folder.
- Add the following line, replacing your-api-key-here with the Groq API key you saved in Step 1:
GROQ_API_KEY=your-api-key-here
Why .env.local?
Next.js automatically loads variables from .env.local into your server-side code. This file is already listed in .gitignore by default, so your secret key never gets committed to GitHub.
- Save .env.local.
- Start the development server by running this command in your terminal:
npm run dev
- Open your browser and visit http://localhost:3000.
- Click the Kubernetes sample button to load a test log.
- Click Analyze Incident and watch the AI-generated analysis stream in.
You should see a structured incident report appear in the results panel with a detected source, severity level, root cause analysis, and remediation steps.
Not seeing any analysis?
- Make sure you replaced your-api-key-here with your actual Groq API key in .env.local.
- Restart the dev server after creating .env.local. Press Ctrl+C in the terminal, then run npm run dev again.
- Check that your .env.local file is in the project root (same level as package.json), not inside src/.
Why is my Groq API key not working locally?
Push your project to GitHub
Vercel deploys directly from a GitHub repository. Every time you push new code, Vercel automatically rebuilds and redeploys your app.
- Stop the dev server by pressing Ctrl+C in your terminal.
- Stage and commit all your project files by running these commands:
git add .
git commit -m "Add DevOps Incident Copilot"
What about .env.local?
Your .env.local file is already in .gitignore (added automatically when you scaffolded the project with create-next-app). It will not be committed or pushed to GitHub. Your API key stays private.
- Open your browser and go to github.com.
- Click the + icon in the top right and select New repository.
- Name the repository incident-copilot.
- Leave all other settings as defaults (do not initialize with a README, .gitignore, or license).
- Click Create repository.
- GitHub will show setup instructions. Copy the remote URL from the page, then connect and push your code by running these commands:
git remote add origin https://github.com/[[GITHUB_USERNAME="your GitHub username"]]/incident-copilot.git
git branch -M main
git push -u origin main
What do these commands do?
- git remote add origin connects your local project to the GitHub repository you just created.
- git branch -M main ensures your default branch is named main.
- git push -u origin main uploads all your committed code to GitHub.
- Refresh your GitHub repository page in the browser.
- You should see all your project files (src/, package.json, etc.) listed. Note that .env.local is NOT listed.
Push failed with authentication error?
GitHub requires a personal access token (not your password) for HTTPS pushes. If you see an authentication error, generate a token from GitHub Settings > Developer settings > Personal access tokens and use it as your password when prompted.
Help me fix a GitHub push authentication error.
Deploy to Vercel
Vercel is a hosting platform built by the Next.js team. It detects your framework automatically and deploys your app with zero configuration.
- Open your browser and go to vercel.com.
- Click Sign Up (or Log In if you already have an account) and choose Continue with GitHub.
- Authorize Vercel to access your GitHub account when prompted.
Once signed in, you will land on your Vercel dashboard.
- Click Add New... in the top right, then select Project.
- Find incident-copilot in the repository list and click Import.
Don't see your repository?
If your repository is not listed, click Configure GitHub App and grant Vercel access to the incident-copilot repository (or all repositories).
Help me connect my GitHub repository to Vercel.
You will see the project configuration page. Vercel auto-detects Next.js and fills in the build settings for you.
- Expand the Environment Variables section on the configuration page.
- In the Name field, enter GROQ_API_KEY.
- In the Value field, paste your Groq API key (the same one from your .env.local file).
- Click Deploy.
Why add the key here too?
Your .env.local file only works on your own machine. Vercel needs its own copy of the environment variable to use during production builds and when running your API route in the cloud.
Vercel will build and deploy your application. This takes about 30-60 seconds.
- Once the deployment finishes, Vercel shows a Congratulations screen with a preview of your live site.
- Click Visit to open your live app in a new tab.
- Copy the production URL from your browser's address bar (it will look like https://incident-copilot-xxxxx.vercel.app).
- Test the live app by clicking a sample log button and running an analysis, just like you did locally.
Deployment failed?
- Check the build logs on the Vercel deployment page for specific error messages.
- Make sure you typed GROQ_API_KEY exactly (all uppercase with underscores) in the environment variable name field.
- Verify your Groq API key value is correct by comparing it to the one in your local .env.local file.
Help me debug a failed Vercel deployment.
✔️ Awesome, I've got everything!
Great. Double check you have saved all your files and your live URL is working.
ⓧ I'd like to double check the full code
Here is the complete .env.local file:
GROQ_API_KEY=your-api-key-here
All other files (src/app/page.tsx, src/app/api/analyze/route.ts, src/app/api/analyze/runbook.ts, and src/app/layout.tsx) remain unchanged from the previous step.
Secret mission
Add Incident History
Your copilot analyzes one log at a time and forgets everything between sessions. What if it could remember past incidents so you can spot patterns across failures? Add persistent incident history using browser localStorage.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely on free tiers, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Vercel deployment (free Hobby tier, no charges).
- Groq API key (free tier, no charges).
- GitHub repository.
- Local incident-copilot project folder.
Keep everything running
No action needed. Choose this if you plan to keep using or sharing your DevOps Incident Copilot.
- Your Vercel deployment stays live indefinitely on the free Hobby tier.
- Your Groq API key remains active with no idle charges.
- Your GitHub repository and local files remain untouched.
Pause - I'll come back to this later
Both Vercel and Groq are on free tiers with no idle charges. There is nothing to pause.
- Your deployment continues to serve requests at no cost.
- Your Groq API key stays valid until you explicitly revoke it.
- If you want to free up disk space, you can delete the local incident-copilot folder and re-clone from GitHub later.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow each item below to fully clean up.
- Delete the Vercel project:
- Revoke the Groq API key:
- Delete the GitHub repository:
- Delete the local project folder:
macOS
rm -rf ~/Desktop/incident-copilot
Windows
Remove-Item -Recurse -Force ~\Desktop\incident-copilot
That's a Wrap!
That's a Wrap!
Nice work! You just built and deployed a full-stack AI-powered DevOps Incident Copilot from scratch.
You've learned how to:
- Built a full-stack Next.js application with an API route that streams AI-powered incident analysis using the Vercel AI SDK and Groq.
- Created a DevOps runbook knowledge base injected as context into the system prompt, teaching the AI to recognize common Kubernetes, Docker, and Jenkins failure patterns.
- Deployed a live application to Vercel with secure environment variable management and a shareable public URL.
- Secret Mission: Added persistent incident history using browser localStorage, letting users review past analyses with severity badges and timestamps.
Ready to quiz yourself?