tutorial

Monitoring Kapowarr (Comic Book Download Manager) with Vigilmon

Kapowarr automates comic book volume downloading and library management — but Flask web UI outages, download queue stalls, metadata fetch failures, and storage backend problems silently break your library pipeline. Here's how to monitor Kapowarr availability, download health, metadata connectivity, and storage with Vigilmon.

Kapowarr is a Python/Flask-based server (port 5656) that automates downloading comic book volumes from configured sources, manages your local comic library, and keeps metadata synchronized with upstream databases like Comic Vine. It runs as a persistent daemon with a web UI for management, a background download queue, and scheduled library scans. When the Flask process crashes, download sources become unreachable, or the storage backend fills up, Kapowarr stops downloading silently — your "Wanted" list grows without any notification, and new issues for running series are never fetched. Without external monitoring, you may not notice for weeks that your comic collection stopped updating. Vigilmon lets you monitor Kapowarr's web UI availability, download queue health, metadata API connectivity, library scan completion, and storage backend so your comic library pipeline stays healthy.

What You'll Set Up

  • Kapowarr web UI availability (port 5656)
  • Download queue health and completion rate
  • Comic Vine metadata API connectivity
  • Library scan completion monitoring
  • Storage backend health checks

Prerequisites

  • Kapowarr running (Python/Flask, port 5656)
  • Access to Kapowarr's API (available at /api/ on port 5656)
  • A free Vigilmon account

Step 1: Monitor Kapowarr Web UI Availability

The Kapowarr Flask application serves both the web UI and its API from the same process on port 5656. If the process crashes or the port becomes unreachable, downloads stop and you lose management access entirely. Set up an HTTP monitor as the primary health check:

Direct HTTP monitor in Vigilmon:

  1. Click Add MonitorHTTP(S).
  2. Set URL to http://your-kapowarr-host:5656/.
  3. Set check interval to 2 minutes.
  4. Set expected response to HTTP 200.

For script-based monitoring (useful when Kapowarr is on an internal network):

#!/bin/bash
# /usr/local/bin/kapowarr-health-check.sh

HEARTBEAT_URL="https://vigilmon.online/heartbeat/abc123"
KAPOWARR_HOST="localhost"
KAPOWARR_PORT="5656"
TIMEOUT=10

# Check web UI
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
  --max-time "$TIMEOUT" \
  "http://${KAPOWARR_HOST}:${KAPOWARR_PORT}/")

if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "301" ]; then
  echo "Kapowarr web UI OK (HTTP $HTTP_CODE)"

  # Also verify API endpoint responds
  API_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    --max-time "$TIMEOUT" \
    "http://${KAPOWARR_HOST}:${KAPOWARR_PORT}/api/system/status")

  if [ "$API_CODE" = "200" ] || [ "$API_CODE" = "401" ]; then
    echo "Kapowarr API reachable (HTTP $API_CODE)"
    curl -s "$HEARTBEAT_URL"
  else
    echo "Kapowarr API unreachable (HTTP $API_CODE)"
    exit 1
  fi
else
  echo "Kapowarr web UI down (HTTP $HTTP_CODE)"
  exit 1
fi

Install as a cron job:

chmod +x /usr/local/bin/kapowarr-health-check.sh
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/local/bin/kapowarr-health-check.sh >> /var/log/kapowarr-health.log 2>&1") | crontab -

Step 2: Monitor Download Queue Health

Kapowarr maintains a download queue for volumes and issues in your "Wanted" list. When all download sources fail, a volume has no valid release candidates, or network connectivity breaks mid-download, items stay in the queue indefinitely with no completion progress. Monitor queue health through the Kapowarr API:

#!/bin/bash
# /usr/local/bin/kapowarr-queue-check.sh

HEARTBEAT_URL="https://vigilmon.online/heartbeat/def456"
KAPOWARR_API="http://localhost:5656/api"
API_KEY="your-kapowarr-api-key"     # From Settings → API in the web UI
MAX_STUCK_MINUTES=120               # Alert if a download hasn't progressed in 2 hours
MAX_QUEUE_DEPTH=100                 # Alert if more than 100 items are queued
TIMEOUT=15

# Get queue status
QUEUE_RESP=$(curl -s \
  --max-time "$TIMEOUT" \
  -H "api-key: ${API_KEY}" \
  "${KAPOWARR_API}/download/queue")

if [ -z "$QUEUE_RESP" ]; then
  echo "No response from Kapowarr download API"
  exit 1
fi

QUEUE_RESULT=$(echo "$QUEUE_RESP" | python3 -c "
import sys, json
from datetime import datetime, timezone

try:
    data = json.load(sys.stdin)
except Exception as e:
    print(f'error:JSON parse failed: {e}')
    sys.exit(1)

# Extract queue items
items = data if isinstance(data, list) else data.get('result', [])
total = len(items)
downloading = sum(1 for i in items if i.get('status') == 'downloading')
failed = sum(1 for i in items if i.get('status') in ('failed', 'error'))
waiting = sum(1 for i in items if i.get('status') in ('queued', 'waiting'))

print(f'total:{total}')
print(f'downloading:{downloading}')
print(f'failed:{failed}')
print(f'waiting:{waiting}')
" 2>/dev/null)

if [ -z "$QUEUE_RESULT" ]; then
  echo "Failed to parse queue response"
  exit 1
fi

TOTAL=$(echo "$QUEUE_RESULT" | grep "^total:" | cut -d: -f2)
FAILED=$(echo "$QUEUE_RESULT" | grep "^failed:" | cut -d: -f2)
WAITING=$(echo "$QUEUE_RESULT" | grep "^waiting:" | cut -d: -f2)

TOTAL="${TOTAL:-0}"
FAILED="${FAILED:-0}"
WAITING="${WAITING:-0}"

ALERT=0

if [ "$TOTAL" -gt "$MAX_QUEUE_DEPTH" ]; then
  echo "Queue backed up: ${TOTAL} items (max: ${MAX_QUEUE_DEPTH})"
  ALERT=1
fi

if [ "$FAILED" -gt 10 ]; then
  echo "Failed downloads: ${FAILED} items in failed state"
  ALERT=1
fi

if [ "$ALERT" -eq 0 ]; then
  echo "Queue OK: ${TOTAL} total, ${WAITING} waiting, ${FAILED} failed"
  curl -s "$HEARTBEAT_URL"
else
  exit 1
fi

Run this every 10 minutes. A queue that never empties (total stays constant across multiple checks) indicates download sources are all failing or the download directory is unreachable.


Step 3: Monitor Comic Vine Metadata Connectivity

Kapowarr uses Comic Vine (and optionally other sources) to resolve series metadata, issue numbers, and cover art. When the Comic Vine API key is rate-limited, the API is down, or your key expires, Kapowarr cannot add new volumes to your library, match issues to series, or fetch cover art. Monitor Comic Vine connectivity directly:

#!/bin/bash
# /usr/local/bin/kapowarr-comicvine-check.sh

HEARTBEAT_URL="https://vigilmon.online/heartbeat/ghi789"
COMIC_VINE_API_KEY="your-comic-vine-api-key"
TIMEOUT=20

# Check Comic Vine API connectivity and key validity
CV_RESP=$(curl -s \
  --max-time "$TIMEOUT" \
  "https://comicvine.gamespot.com/api/volumes/?api_key=${COMIC_VINE_API_KEY}&format=json&limit=1&field_list=id")

CV_STATUS=$(echo "$CV_RESP" | python3 -c "
import sys, json
try:
    data = json.load(sys.stdin)
    status = data.get('status_code', 0)
    error = data.get('error', 'Unknown')
    print(f'{status}:{error}')
except:
    print('0:parse_error')
" 2>/dev/null)

CV_CODE=$(echo "$CV_STATUS" | cut -d: -f1)
CV_MSG=$(echo "$CV_STATUS" | cut -d: -f2-)

case "$CV_CODE" in
  1)
    echo "Comic Vine API OK (status: OK)"
    curl -s "$HEARTBEAT_URL"
    ;;
  100)
    echo "Comic Vine API key invalid (status: $CV_CODE $CV_MSG)"
    exit 1
    ;;
  104)
    echo "Comic Vine API rate limited (status: $CV_CODE $CV_MSG)"
    exit 1
    ;;
  0)
    echo "Comic Vine API unreachable or parse error ($CV_MSG)"
    exit 1
    ;;
  *)
    echo "Comic Vine API error (status: $CV_CODE $CV_MSG)"
    exit 1
    ;;
esac

Run this every 30 minutes. Comic Vine rate limits (100 requests/hour on free keys) can be exhausted by library scans, leaving Kapowarr unable to resolve new series. Detecting rate-limit hits early lets you pause automatic scans before they exhaust the daily limit.


Step 4: Monitor Library Scan Completion

Kapowarr periodically scans your library directory to detect locally present files and match them against your "Wanted" list. When a library scan stalls (due to a filesystem error, an NFS mount going stale, or a very large directory taking too long), new files are never recognized and the "Downloaded" status for volumes doesn't update. Monitor scan completion via Kapowarr's task log:

#!/bin/bash
# /usr/local/bin/kapowarr-scan-check.sh

HEARTBEAT_URL="https://vigilmon.online/heartbeat/jkl012"
KAPOWARR_API="http://localhost:5656/api"
API_KEY="your-kapowarr-api-key"
MAX_HOURS_SINCE_SCAN=25    # Alert if no library scan in 25 hours
TIMEOUT=15

# Get task history from Kapowarr API
TASKS_RESP=$(curl -s \
  --max-time "$TIMEOUT" \
  -H "api-key: ${API_KEY}" \
  "${KAPOWARR_API}/tasks/history?limit=50")

if [ -z "$TASKS_RESP" ]; then
  echo "No response from Kapowarr tasks API"
  exit 1
fi

SCAN_RESULT=$(echo "$TASKS_RESP" | python3 -c "
import sys, json
from datetime import datetime, timezone

try:
    data = json.load(sys.stdin)
except:
    sys.exit(1)

tasks = data if isinstance(data, list) else data.get('result', [])

# Find last completed library scan
scan_tasks = [t for t in tasks if 'scan' in t.get('name', '').lower() or 'library' in t.get('name', '').lower()]
completed = [t for t in scan_tasks if t.get('status') in ('success', 'completed', 'done')]

if not completed:
    print('no_scan_found')
    sys.exit(0)

latest = completed[0]
completed_at = latest.get('finished_at') or latest.get('updated_at') or latest.get('created_at', '')

print(f'last_scan:{completed_at}')
" 2>/dev/null)

if [ "$SCAN_RESULT" = "no_scan_found" ] || [ -z "$SCAN_RESULT" ]; then
  echo "No completed library scan found in recent task history"
  exit 1
fi

LAST_SCAN_TIME=$(echo "$SCAN_RESULT" | grep "^last_scan:" | cut -d: -f2-)

if [ -z "$LAST_SCAN_TIME" ]; then
  echo "Could not determine last scan time"
  exit 1
fi

SCAN_EPOCH=$(date -d "$LAST_SCAN_TIME" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
HOURS_AGO=$(( (NOW_EPOCH - SCAN_EPOCH) / 3600 ))

if [ "$HOURS_AGO" -le "$MAX_HOURS_SINCE_SCAN" ]; then
  echo "Library scan OK: last completed ${HOURS_AGO}h ago"
  curl -s "$HEARTBEAT_URL"
else
  echo "Library scan stale: last completed ${HOURS_AGO}h ago (threshold: ${MAX_HOURS_SINCE_SCAN}h)"
  exit 1
fi

Set the Vigilmon heartbeat to 26 hours. A missing library scan means locally downloaded files aren't being matched to their Kapowarr entries and the "downloaded" count never updates.


Step 5: Monitor Storage Backend

Kapowarr downloads comics to a configured library path (local disk, NFS, SMB, or a Docker volume). When the storage backend fills up or becomes unmounted, Kapowarr's download jobs fail at the write step — the download appears to start but files are never written. Monitor storage separately:

#!/bin/bash
# /usr/local/bin/kapowarr-storage-check.sh

HEARTBEAT_URL="https://vigilmon.online/heartbeat/mno345"
LIBRARY_PATH="/mnt/comics"     # Your Kapowarr library root — adjust as needed
DOWNLOAD_TEMP="/mnt/comics/temp_downloads"   # Temp download dir if separate
DISK_THRESHOLD=85              # percent
TIMEOUT=10

FAILED=0

# Verify library path is mounted and accessible
if ! mountpoint -q "$LIBRARY_PATH" 2>/dev/null && [ ! -d "$LIBRARY_PATH" ]; then
  echo "Library path not accessible: $LIBRARY_PATH"
  exit 1
fi

# Test write access (Kapowarr needs to write to this path)
TEST_FILE="${LIBRARY_PATH}/.vigilmon_write_test_$$"
if ! touch "$TEST_FILE" 2>/dev/null; then
  echo "Library path is read-only or write-protected: $LIBRARY_PATH"
  FAILED=1
else
  rm -f "$TEST_FILE"
  echo "Write access OK: $LIBRARY_PATH"
fi

# Check disk space
DISK_PCT=$(df "$LIBRARY_PATH" | awk 'NR==2 {gsub(/%/,""); print $5}')
DISK_AVAIL=$(df -h "$LIBRARY_PATH" | awk 'NR==2 {print $4}')

if [ "${DISK_PCT:-0}" -ge "$DISK_THRESHOLD" ]; then
  echo "Library disk usage high: ${DISK_PCT}% used (${DISK_AVAIL} free) — threshold: ${DISK_THRESHOLD}%"
  FAILED=1
else
  echo "Disk OK: ${DISK_PCT}% used (${DISK_AVAIL} free)"
fi

# Check temp download directory if separate
if [ -n "$DOWNLOAD_TEMP" ] && [ "$DOWNLOAD_TEMP" != "$LIBRARY_PATH" ] && [ -d "$DOWNLOAD_TEMP" ]; then
  TEMP_PCT=$(df "$DOWNLOAD_TEMP" | awk 'NR==2 {gsub(/%/,""); print $5}')
  if [ "${TEMP_PCT:-0}" -ge "$DISK_THRESHOLD" ]; then
    echo "Temp download disk usage high: ${TEMP_PCT}% — stalled partial downloads may be filling disk"
    FAILED=1
  else
    echo "Temp disk OK: ${TEMP_PCT}%"
  fi
fi

# Count stale temp files (partial downloads older than 24 hours)
if [ -d "$DOWNLOAD_TEMP" ]; then
  STALE_COUNT=$(find "$DOWNLOAD_TEMP" -name "*.part" -o -name "*.tmp" 2>/dev/null | \
    xargs ls -lt 2>/dev/null | awk -v age=86400 '{
      if (NR>1 && $0 ~ /^-/) {
        # simplified: count all temp files as potential stale
        count++
      }
    } END {print count+0}')
  if [ "${STALE_COUNT:-0}" -gt 20 ]; then
    echo "Stale partial downloads found: ${STALE_COUNT} temp files in $DOWNLOAD_TEMP"
    FAILED=1
  fi
fi

if [ "$FAILED" -eq 0 ]; then
  curl -s "$HEARTBEAT_URL"
else
  exit 1
fi

Run this every 15 minutes. Stale .part files accumulating in the temp directory usually means downloads are starting but being aborted — investigate Kapowarr logs for source errors or source URL changes.


Step 6: Set Up Alert Channels

Configure Vigilmon alert routing for Kapowarr:

  1. Go to Alert Channels in Vigilmon.
  2. Add email notification for all monitors — Kapowarr failures are not usually urgent enough for pager-style alerts.
  3. Add a Slack webhook to #media-ops for web UI downtime and storage alerts (these are actionable immediately).
  4. Set Consecutive failures before alert to 2 for the web UI monitor — Flask startup takes several seconds and brief restarts are normal.
  5. Set Consecutive failures before alert to 3 for the Comic Vine monitor — brief rate-limit hits during library scans are expected.

Add a maintenance window before Kapowarr updates:

curl -X POST https://vigilmon.online/api/maintenance \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "monitor_id": "KAPOWARR_WEB_MONITOR_ID",
    "duration_minutes": 5,
    "reason": "Kapowarr version update"
  }'

# Update Kapowarr (Docker example)
docker compose pull kapowarr && docker compose up -d kapowarr

Summary

| Monitor | Target | What It Catches | |---|---|---| | HTTP monitor (web UI) | Port 5656 | Flask crash, process exit, port binding failure | | Cron heartbeat (queue) | /api/download/queue | Stalled downloads, high failure rate, backed-up queue | | Cron heartbeat (Comic Vine) | Comic Vine API status | Rate limiting, key expiry, API outage | | Cron heartbeat (library scan) | Task history API | Stalled scan, filesystem error, NFS mount loss | | Cron heartbeat (storage) | Disk space + write access | Full disk, read-only mount, stale partial downloads |

Kapowarr's failure mode is an invisible backlog — new comic issues are released, your "Wanted" list grows, but nothing downloads because the source failed, the API is rate-limited, or the disk is full. Without external monitoring, the only way to notice is to open the web UI and manually check the queue. Vigilmon's continuous checks make failures visible within minutes so your comic library stays current automatically.

Start monitoring for free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →