← Back to blog

Why Markdown Wins for Runbooks (Over Notebooks)

· 5 min read · Stew Team
markdownrunbooknotebooksdocumentation

Jupyter and VS Code notebooks are powerful, but they weren’t designed for operational runbooks. Markdown was—and here’s why it matters.

For runbook tool comparisons, see our interactive runbook tools comparison.

The Format Problem

Open a Jupyter notebook in a text editor:

{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": ["# Check Pod Status\n", "\n", "Run this to see current pods."]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [{"name": "stdout", "output_type": "stream", "text": ["NAME..."]}],
      "source": ["!kubectl get pods"]
    }
  ]
}

Now open the same runbook in markdown:

# Check Pod Status

Run this to see current pods.

​```bash
kubectl get pods
​```

Which would you rather read at 3am during an incident?

Why Plain Text Matters for Ops

Reason 1: Incidents Don’t Wait for Tools

During an incident:

  • Your IDE might not be available
  • You might be SSH’d into a bastion host
  • You might be on a borrowed laptop
  • Your tool might be down (ironic, right?)

Markdown works everywhere:

# Read a runbook with any tool
cat runbook.md
less runbook.md
vim runbook.md

Reason 2: Git Actually Works

JSON notebook diffs are unreadable:

- "source": ["kubectl get pods -n production"]
+ "source": ["kubectl get pods -n production -o wide"]

Markdown diffs are clear:

- kubectl get pods -n production
+ kubectl get pods -n production -o wide

This means:

  • Code review works for runbooks
  • History is meaningful
  • Merge conflicts are resolvable
  • Blame shows who changed what

Reason 3: No Lock-In

Notebooks require specific software:

  • Jupyter needs Python runtime
  • VS Code notebooks need VS Code
  • Each has its own kernel requirements

Markdown is universal:

  • Any text editor
  • Any git host renders it
  • Any documentation system accepts it
  • Any static site generator builds it

Reason 4: Copy-Paste Friendly

From a notebook, copying a command means:

  1. Open the notebook
  2. Find the cell
  3. Click into the cell
  4. Select the code
  5. Copy

From markdown:

  1. View the file (raw or rendered)
  2. Copy the command

When you’re in a hurry, every step matters.

What Notebooks Do Better

Notebooks aren’t all bad. They excel at:

Use CaseWhy Notebooks Win
Data analysisRich output (tables, charts)
Machine learningVisualizations, state management
Interactive explorationCell-by-cell execution with context
TeachingStep-by-step with visible output

But these aren’t typical runbook use cases.

What Runbooks Need

Runbooks have different requirements:

RequirementMarkdownNotebooks
Emergency readability⚠️
Version control⚠️
Low tooling requirements
Shell-first workflows⚠️
Team adoption⚠️
Works over SSH

Making Markdown Executable

The knock against markdown: it’s static. Commands are just text.

But this is a tooling problem, not a format problem.

The Traditional Approach

# Restart Service

## Stop the service
​```bash
systemctl stop myservice
​```

## Wait for cleanup
​```bash
sleep 10
​```

## Start the service
​```bash
systemctl start myservice
​```

To execute: copy each block, paste into terminal, run.

The Executable Approach

Same markdown, but with tooling that adds execution:

# Restart Service

## Stop the service
​```bash
systemctl stop myservice
​```
[Run] ← Click to execute, output appears below

## Wait for cleanup
​```bash
sleep 10
​```
[Run]

## Start the service
​```bash
systemctl start myservice
​```
[Run]

The format stays readable. The tooling adds capability.

Markdown Runbook Best Practices

Structure for Clarity

# [Service] [Procedure] Runbook

## Overview
Brief description of when to use this runbook.

## Prerequisites
- Access to production cluster
- kubectl configured

## Steps

### Step 1: Verify Current State
​```bash
kubectl get pods -l app=service
​```

### Step 2: Apply Change
​```bash
kubectl apply -f config.yaml
​```

### Step 3: Verify Success
​```bash
kubectl rollout status deployment/service
​```

## Troubleshooting
Common issues and solutions.

## Rollback
How to undo if something goes wrong.

Use Fenced Code Blocks with Language

​```bash
# This gets syntax highlighting
kubectl get pods
​```

​```sql
-- This does too
SELECT * FROM users;
​```

Add Context Around Commands

## Check pod logs

This shows the last 50 lines of logs. Look for ERROR or WARN patterns.

​```bash
kubectl logs -l app=api --tail=50
​```

**Expected output**: Normal startup messages
**If you see**: Connection refused → Check database connectivity

Make Commands Copy-Paste Ready

# Don't add prompts
​```bash
kubectl get pods   # Good
​```

​```bash
$ kubectl get pods # Bad - $ gets copied
​```

Stew: Markdown Execution Done Right

Stew keeps your runbooks as plain markdown while adding execution:

  • Write in any editor
  • Store in any git repo
  • Read with any tool
  • Execute with Stew

The format you trust + the execution you need.

# Your markdown stays clean
​```bash
kubectl get pods
​```

Stew adds the run button. Your runbook stays portable.

Join the waitlist and make your markdown executable.