Is Kavita Down? Real-Time Status & Outage Checker
Is Kavita Down? Real-Time Status & Outage Checker
Kavita is an open-source self-hosted digital library for manga, comics, and books with 6,000+ GitHub stars. It supports CBZ, CBR, PDF, EPUB, and many other formats, with features including reading progress sync, series tracking, reading lists, and a responsive web reader. Kavita is REST API-first with OPDS support, making it mobile-friendly and compatible with a wide range of e-reader apps. It serves as a self-hosted alternative to Kindle Unlimited and Comixology for readers who want full control of their digital collections. When Kavita goes down, readers lose access to their entire library, OPDS feeds stop serving e-reader apps, and reading progress sync halts.
Kavita exposes a REST API on port 5000. Monitoring the process, API health, and library path availability is the fastest way to catch failures before readers encounter blank shelves or stalled syncs.
Quick Status Check
#!/bin/bash
# Kavita health check
# Usage: KAVITA_API_KEY=your_key ./kavita-check.sh
KAVITA_HOST="${KAVITA_HOST:-http://localhost:5000}"
KAVITA_API_KEY="${KAVITA_API_KEY:-your_api_key_here}"
LIBRARY_PATH="${LIBRARY_PATH:-/mnt/media/books}"
PASS=0
FAIL=0
echo "=== Kavita Health Check ==="
echo "Host: $KAVITA_HOST"
echo ""
# 1. Check process is running
if pgrep -x "Kavita" > /dev/null 2>&1 || pgrep -f "Kavita.dll" > /dev/null 2>&1; then
echo "[OK] Kavita process is running"
PASS=$((PASS+1))
else
echo "[FAIL] Kavita process not found"
FAIL=$((FAIL+1))
fi
# 2. Check port 5000 is open
if nc -z localhost 5000 2>/dev/null; then
echo "[OK] Port 5000 is open"
PASS=$((PASS+1))
else
echo "[FAIL] Port 5000 is not reachable"
FAIL=$((FAIL+1))
fi
# 3. Check basic health endpoint (no auth required)
HEALTH=$(curl -sf --max-time 10 "$KAVITA_HOST/api/health")
if [ $? -eq 0 ]; then
echo "[OK] /api/health responded"
PASS=$((PASS+1))
else
echo "[FAIL] /api/health did not respond"
FAIL=$((FAIL+1))
fi
# 4. Check server info with API key
SERVER_INFO=$(curl -sf --max-time 10 \
-H "Authorization: Bearer $KAVITA_API_KEY" \
"$KAVITA_HOST/api/server/server-info")
if [ $? -eq 0 ]; then
VERSION=$(echo "$SERVER_INFO" | grep -o '"kavitaVersion":"[^"]*"' | cut -d'"' -f4)
echo "[OK] Server info API responding — Kavita v$VERSION"
PASS=$((PASS+1))
else
echo "[FAIL] /api/server/server-info did not respond (check API key)"
FAIL=$((FAIL+1))
fi
# 5. Check library path is mounted and accessible
if [ -d "$LIBRARY_PATH" ] && [ -r "$LIBRARY_PATH" ]; then
echo "[OK] Library path $LIBRARY_PATH is accessible"
PASS=$((PASS+1))
else
echo "[FAIL] Library path $LIBRARY_PATH is missing or unreadable"
FAIL=$((FAIL+1))
fi
echo ""
echo "=== Result: $PASS passed, $FAIL failed ==="
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
Python Health Check
#!/usr/bin/env python3
"""
Kavita health check
Checks basic health, authenticates for a JWT token, retrieves server info, library count,
series count, and storage usage.
"""
import os
import sys
import json
import urllib.request
import urllib.error
KAVITA_HOST = os.environ.get("KAVITA_HOST", "http://localhost:5000")
KAVITA_USERNAME = os.environ.get("KAVITA_USERNAME", "admin")
KAVITA_PASSWORD = os.environ.get("KAVITA_PASSWORD", "your_password_here")
results = []
failures = 0
jwt_token = None
def check(label, ok, detail=""):
global failures
status = "OK " if ok else "FAIL"
if not ok:
failures += 1
msg = f"[{status}] {label}"
if detail:
msg += f" — {detail}"
results.append(msg)
print(msg)
def api_get(path, token=None, timeout=10):
url = f"{KAVITA_HOST}{path}"
headers = {"Accept": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
req = urllib.request.Request(url, headers=headers)
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
raise RuntimeError(f"HTTP {e.code} from {path}")
except Exception as e:
raise RuntimeError(f"Request failed: {e}")
def api_post(path, data, timeout=10):
url = f"{KAVITA_HOST}{path}"
body = json.dumps(data).encode()
headers = {"Content-Type": "application/json", "Accept": "application/json"}
req = urllib.request.Request(url, data=body, headers=headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
raise RuntimeError(f"HTTP {e.code} from {path}")
except Exception as e:
raise RuntimeError(f"Request failed: {e}")
print("=== Kavita Python Health Check ===")
print(f"Host: {KAVITA_HOST}\n")
# 1. Basic health endpoint — no authentication required
try:
health = api_get("/api/health")
check("Basic health endpoint", True, "responded OK")
except RuntimeError as e:
check("Basic health endpoint", False, str(e))
# 2. Authenticate and retrieve JWT token
try:
login_resp = api_post("/api/account/login", {
"username": KAVITA_USERNAME,
"password": KAVITA_PASSWORD,
})
jwt_token = login_resp.get("token")
if jwt_token:
check("Authentication (JWT)", True, f"token obtained for user '{KAVITA_USERNAME}'")
else:
check("Authentication (JWT)", False, "login succeeded but no token in response")
except RuntimeError as e:
check("Authentication (JWT)", False, str(e))
# 3. Server info — version and uptime (requires JWT)
if jwt_token:
try:
info = api_get("/api/server/server-info", token=jwt_token)
version = info.get("kavitaVersion", "unknown")
check("Server info API", True, f"Kavita v{version}")
except RuntimeError as e:
check("Server info API", False, str(e))
else:
check("Server info API", False, "skipped — no JWT token available")
# 4. Library count — confirms libraries are configured and accessible
if jwt_token:
try:
libraries = api_get("/api/library", token=jwt_token)
count = len(libraries) if isinstance(libraries, list) else 0
ok = count > 0
check(
"Library count",
ok,
f"{count} library/libraries configured"
+ ("" if ok else " — no libraries found, check library setup"),
)
except RuntimeError as e:
check("Library count", False, str(e))
else:
check("Library count", False, "skipped — no JWT token available")
# 5. Series count — spot-check library content is accessible
if jwt_token:
try:
series_resp = api_get(
"/api/series/all?libraryId=0&pageNumber=0&pageSize=5",
token=jwt_token,
)
if isinstance(series_resp, list):
count = len(series_resp)
elif isinstance(series_resp, dict):
count = series_resp.get("totalCount", len(series_resp.get("result", [])))
else:
count = 0
check("Series spot-check", True, f"{count} series returned in sample query")
except RuntimeError as e:
check("Series spot-check", False, str(e))
else:
check("Series spot-check", False, "skipped — no JWT token available")
# 6. Storage — check disk usage on the library path
library_path = os.environ.get("KAVITA_LIBRARY_PATH", "/mnt/media/books")
try:
stat = os.statvfs(library_path)
total_gb = (stat.f_blocks * stat.f_frsize) / (1024 ** 3)
free_gb = (stat.f_bavail * stat.f_frsize) / (1024 ** 3)
used_pct = 100 * (1 - stat.f_bavail / stat.f_blocks) if stat.f_blocks else 0
ok = used_pct < 90
check(
f"Disk space ({library_path})",
ok,
f"{free_gb:.1f} GB free of {total_gb:.1f} GB ({used_pct:.0f}% used)"
+ ("" if ok else " — disk nearly full, scanner may fail"),
)
except Exception as e:
check(f"Disk space ({library_path})", False, str(e))
# Summary
print(f"\n=== Result: {len(results) - failures} passed, {failures} failed ===")
sys.exit(0 if failures == 0 else 1)
Common Kavita Outage Causes
| Symptom | Likely Cause | Resolution |
|---|---|---|
| API requests return 503 or hang indefinitely | SQLite database locked — concurrent scanner and API access conflict | Restart Kavita to release the lock; avoid triggering manual scans while reads are active |
| All series show "No files found" or library is empty | Library path unmounted — NFS/SMB share or external drive disconnected | Remount the share; check df -h and mount; verify fstab entry; restart Kavita after remount |
| New files added but don't appear in Kavita | Book scanner crashed or background job stalled | Trigger a manual library scan in Admin → Libraries; check Kavita logs for scanner errors |
| EPUB reader broken after update — pages not rendering | EPUB parser regression introduced in Kavita update | Check Kavita GitHub issues for the version; roll back to previous version if confirmed regression |
| E-reader app can't connect via OPDS | OPDS feed returning 401 Unauthorized — API key or credentials invalid | Regenerate the OPDS API key in User Settings; update credentials in the e-reader app |
| All series show blank cover images | Cover image cache corrupted or cover generation failed | Admin → Tasks → Clear Cache and regenerate covers; check disk space on the cache volume |
Architecture Overview
| Component | Function | Failure Impact |
|---|---|---|
| Kavita Core (C#/.NET) | Main process — API, web reader, scheduler, OPDS | Total failure; library, reader, and OPDS all unreachable |
| SQLite Database | Stores library metadata, reading progress, users, and settings | Crash on startup or API lock-up; all reading progress lost if corrupted |
| Library Scanner | Watches library paths and indexes new CBZ/CBR/EPUB/PDF files | New files not visible; existing library unaffected |
| Cover Image Cache | Generates and caches cover thumbnails for all series | Blank covers in UI; no functional impact on reading |
| OPDS Server | Exposes library to e-reader apps via OPDS catalog API | E-reader app sync fails; web reader unaffected |
| Library Storage (NFS/SMB/local) | Hosts the actual CBZ, CBR, EPUB, and PDF files | All content unavailable; API responds but series have no readable files |
Uptime History
| Date | Incident Type | Duration | Impact |
|---|---|---|---|
| 2026-02 | SQLite database lock under concurrent scanner load | ~1 hr | API returned 503 errors; web reader and OPDS inaccessible until restart |
| 2025-11 | NFS library path unmounted after NAS reboot | ~4 hrs undetected | All series showed empty file lists; no content accessible to readers |
| 2025-09 | Cover cache corruption after disk write error | ~2 hrs | All series thumbnails blank; required full cache clear and regeneration |
| 2025-07 | EPUB parser regression in minor update | ~6 hrs | EPUB files failed to render in web reader; CBZ/PDF unaffected |
Monitor Kavita Automatically
Kavita failures are easy to miss — the process stays running while the library path is unmounted, the SQLite database is locked, or OPDS feeds are returning 401 errors, all without any notification to the server owner. ezmon.com monitors your Kavita endpoints from multiple external probes and alerts your team via Slack, PagerDuty, or SMS the moment the API stops responding or health checks detect a problem.