productivity

Is Mealie Down? Real-Time Status & Outage Checker

Is Mealie Down? Real-Time Status & Outage Checker

Mealie is an open-source self-hosted recipe manager with 7,000+ GitHub stars. It features recipe importing from any URL, meal planning, shopping lists, and a beautiful responsive UI. Built on FastAPI (Python) + Vue.js, Mealie supports both SQLite and PostgreSQL backends and functions as a mobile-friendly PWA. Created by Hay Kranen and maintained by a growing community, it serves as a self-hosted alternative to Paprika and Plan to Eat — used by families and home cooks who want full ownership of their recipe collections without subscription fees or data lock-in.

When Mealie goes down, recipe imports stop working, meal plans become inaccessible, and shopping list generation fails. Because Mealie's recipe scraper depends on an active internet connection and frequently-changing site structures, even a partially degraded instance can silently break URL imports while appearing to function normally for existing recipes.

Quick Status Check

#!/bin/bash
# Mealie health check script
# Tests app/about API, process, port, database, and authenticated group endpoint

MEALIE_URL="${MEALIE_URL:-http://localhost:9000}"
MEALIE_TOKEN="${MEALIE_TOKEN:-your-api-token-here}"
DB_PATH="${DB_PATH:-/app/data/mealie.db}"

echo "=== Mealie Health Check ==="

# 1. Check Mealie process running
if pgrep -f "uvicorn\|mealie\|gunicorn" &>/dev/null; then
  echo "[OK] Mealie process is running"
else
  echo "[FAIL] Mealie process not detected"
fi

# 2. Check port 9000 is listening
if nc -z localhost 9000 2>/dev/null; then
  echo "[OK] Port 9000 is open"
else
  echo "[FAIL] Port 9000 is not responding"
fi

# 3. App about endpoint (no auth required)
ABOUT=$(curl -sf "${MEALIE_URL}/api/app/about" 2>/dev/null)
if echo "$ABOUT" | grep -q '"version"'; then
  VERSION=$(echo "$ABOUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('version','?'))" 2>/dev/null)
  echo "[OK] App about API responding — version ${VERSION}"
else
  echo "[FAIL] App about endpoint not responding: ${ABOUT:0:120}"
fi

# 4. Authenticated group endpoint
GROUP=$(curl -sf \
  -H "Authorization: Bearer ${MEALIE_TOKEN}" \
  "${MEALIE_URL}/api/groups/self" 2>/dev/null)
if echo "$GROUP" | grep -q '"id"'; then
  NAME=$(echo "$GROUP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('name','?'))" 2>/dev/null)
  echo "[OK] Authenticated API working — group: ${NAME}"
else
  echo "[FAIL] Authenticated API failed: ${GROUP:0:120}"
fi

# 5. Database file (SQLite)
if [ -f "$DB_PATH" ]; then
  DB_SIZE=$(du -sh "$DB_PATH" 2>/dev/null | awk '{print $1}')
  echo "[OK] SQLite database found (${DB_SIZE})"
else
  echo "[INFO] SQLite not found at ${DB_PATH} (may be using PostgreSQL)"
fi

echo "=== Check complete ==="

Python Health Check

#!/usr/bin/env python3
"""
Mealie health check
Checks app/about, JWT auth, recipe count, household info, and admin statistics.
"""

import os
import sys
import requests

MEALIE_URL = os.environ.get("MEALIE_URL", "http://localhost:9000")
MEALIE_USER = os.environ.get("MEALIE_USER", "changeme@example.com")
MEALIE_PASS = os.environ.get("MEALIE_PASS", "MyPassword")
MEALIE_ADMIN_TOKEN = os.environ.get("MEALIE_ADMIN_TOKEN", "")
TIMEOUT = 15

def check(label: str, ok: bool, detail: str = "") -> bool:
    status = "OK  " if ok else "FAIL"
    msg = f"[{status}] {label}"
    if detail:
        msg += f" — {detail}"
    print(msg)
    return ok

results = []
user_token = None

# 1. App about (no auth)
try:
    r = requests.get(f"{MEALIE_URL}/api/app/about", timeout=TIMEOUT)
    ok = r.status_code == 200 and "version" in r.json()
    data = r.json() if ok else {}
    version = data.get("version", "unknown")
    production = data.get("production", None)
    results.append(check(
        "App about endpoint",
        ok,
        f"version {version}, production={production}"
    ))
except Exception as e:
    results.append(check("App about endpoint", False, str(e)))

# 2. Authenticate via username/password to get JWT
try:
    r = requests.post(
        f"{MEALIE_URL}/api/auth/token",
        data={"username": MEALIE_USER, "password": MEALIE_PASS},
        timeout=TIMEOUT,
    )
    ok = r.status_code == 200 and "access_token" in r.json()
    if ok:
        user_token = r.json()["access_token"]
    token_type = r.json().get("token_type", "?") if ok else ""
    results.append(check("JWT authentication", ok, f"token_type={token_type}" if ok else r.text[:80]))
except Exception as e:
    results.append(check("JWT authentication", False, str(e)))

auth_headers = {"Authorization": f"Bearer {user_token}"} if user_token else {}

# 3. Recipe count
try:
    r = requests.get(
        f"{MEALIE_URL}/api/recipes",
        params={"page": 1, "perPage": 1},
        headers=auth_headers,
        timeout=TIMEOUT,
    )
    ok = r.status_code == 200
    data = r.json() if ok else {}
    total = data.get("total", 0)
    results.append(check("Recipes accessible", ok, f"{total} recipe(s) in collection"))
except Exception as e:
    results.append(check("Recipes accessible", False, str(e)))

# 4. Household / group info
try:
    r = requests.get(
        f"{MEALIE_URL}/api/households/self",
        headers=auth_headers,
        timeout=TIMEOUT,
    )
    ok = r.status_code == 200 and "id" in r.json()
    name = r.json().get("name", "unknown") if ok else ""
    results.append(check("Household info", ok, f"household: {name}"))
except Exception as e:
    results.append(check("Household info", False, str(e)))

# 5. Admin statistics (requires admin token)
admin_headers = (
    {"Authorization": f"Bearer {MEALIE_ADMIN_TOKEN}"}
    if MEALIE_ADMIN_TOKEN
    else auth_headers
)
try:
    r = requests.get(
        f"{MEALIE_URL}/api/admin/statistics",
        headers=admin_headers,
        timeout=TIMEOUT,
    )
    ok = r.status_code == 200
    data = r.json() if ok else {}
    recipe_count = data.get("totalRecipes", "?")
    user_count = data.get("totalUsers", "?")
    results.append(check(
        "Admin statistics",
        ok,
        f"{recipe_count} recipes, {user_count} users" if ok else f"HTTP {r.status_code}"
    ))
except Exception as e:
    results.append(check("Admin statistics", False, str(e)))

# Summary
passed = sum(results)
total = len(results)
print(f"\n{'='*40}")
print(f"Mealie health: {passed}/{total} checks passed")
if passed < total:
    print("Action required: review FAIL items above")
    sys.exit(1)
else:
    print("All systems operational")
    sys.exit(0)

Common Mealie Outage Causes

SymptomLikely CauseResolution
All pages return 502 Bad Gateway FastAPI/uvicorn process crashed (Python exception, OOM, or unhandled startup error) Check container logs (docker logs mealie); restart the container; check for OOM kills in system journal
App loads but data is missing or stale PostgreSQL connection lost (container restart, network issue, or credential change) Verify PostgreSQL is running and reachable; check Mealie env vars for DB host/port/credentials; check Mealie logs for connection errors
URL recipe imports fail for most sites Recipe scraper library broken (site structure changes break parser; upstream scraper package outdated) Check Mealie GitHub issues for affected sites; update to latest Mealie release; report broken scrapers upstream
Recipe photos missing or broken Image storage directory full or permissions changed Check disk space on image volume (df -h); fix directory permissions; free space and restart Mealie
All users suddenly logged out JWT secret rotated (new SECRET_KEY environment variable invalidates all existing tokens) Ensure SECRET_KEY is set to a stable value in your Docker Compose env; avoid regenerating it unless intentional
Meal plan notifications not sending Email/webhook integration misconfigured or SMTP credentials expired Verify SMTP settings in Mealie admin panel; test notification delivery manually; check email provider for app password requirements

Architecture Overview

ComponentFunctionFailure Impact
FastAPI + uvicorn Python backend — serves REST API, handles recipe imports, meal planning logic Complete API outage; web UI becomes non-functional (Vue.js shell only)
Vue.js frontend (PWA) Single-page application served as static files; mobile-installable PWA UI unreachable if Nginx/static serving fails; API can still function independently
SQLite / PostgreSQL Stores recipes, users, meal plans, shopping lists, household configuration App returns 500 errors; all data inaccessible until database restored
Recipe scraper Parses structured recipe data from any URL using multiple extraction strategies New URL imports fail; existing recipe data unaffected
Image/media storage volume Stores uploaded and scraped recipe photos Recipe images broken or missing; recipe data and functionality otherwise intact
Email / webhook notifications Sends meal plan reminders and shopping list notifications Notification delivery silently fails; core app functionality unaffected

Uptime History

DateIncident TypeDurationImpact
2026-02 FastAPI OOM crash during large recipe import ~30 min Complete service outage; all pages unavailable until container restart
2025-12 Recipe scraper breakage (major site changed structure) ~3 days URL imports failed for affected sites; fixed in next Mealie release
2025-09 Image volume filled (no disk space alert) ~2 hrs New recipe photo uploads failed; existing photos unaffected
2025-07 JWT secret changed after container rebuild without persistent env ~1 hr All users logged out; tokens invalid until users re-authenticated

Monitor Mealie Automatically

Mealie's most damaging failures — a crashed FastAPI process or a full image disk volume — are completely silent from the user's perspective until someone tries to add a recipe and nothing works. External monitoring catches these the moment they happen. ezmon.com monitors your Mealie endpoints from multiple external probes and alerts your team via Slack, PagerDuty, or SMS the moment the app API stops responding or authentication fails.

Set up Mealie monitoring free at ezmon.com →

mealierecipe-managerself-hostedmeal-planningcookingstatus-checker