Skip to content

Health Monitoring

This page covers AutoMem’s health monitoring system, which continuously tracks the status of FalkorDB and Qdrant databases, detects data synchronization drift, and can automatically trigger recovery when data loss is detected. It also documents the monitoring and introspection endpoints that provide visibility into AutoMem’s operational status, database connectivity, queue health, and memory statistics.

For backup strategies and disaster recovery procedures, see Backup & Recovery. For deployment configuration, see Railway Deployment.

AutoMem’s health monitoring system operates at multiple levels to ensure data integrity and service availability.

graph TB
    subgraph "Health Monitoring System"
        HealthEndpoint["/health Endpoint<br/>automem/api/health.py"]
        HealthMonitor["health_monitor.py<br/>Continuous Monitoring"]
        GHAWorkflow[".github/workflows/backup.yml<br/>Scheduled Checks"]
    end

    subgraph "Data Stores"
        FalkorDB["FalkorDB<br/>Graph Database<br/>Port 6379"]
        Qdrant["Qdrant<br/>Vector Database<br/>Port 6333"]
    end

    subgraph "Monitoring Outputs"
        Dashboard["Railway Dashboard<br/>Service Metrics"]
        Webhooks["Slack/Discord<br/>Alert Webhooks"]
        AutoRecovery["recover_from_qdrant.py<br/>Auto-Recovery Script"]
    end

    subgraph "External Tools"
        UptimeRobot["UptimeRobot<br/>External Monitoring"]
        Grafana["Grafana Cloud<br/>Observability"]
    end

    HealthEndpoint -->|"Check Status"| FalkorDB
    HealthEndpoint -->|"Check Status"| Qdrant

    HealthMonitor -->|"Poll /health"| HealthEndpoint
    HealthMonitor -->|"Count Memories"| FalkorDB
    HealthMonitor -->|"Count Vectors"| Qdrant
    HealthMonitor -->|"Calculate Drift"| DriftCheck{"Drift > 5%?"}

    GHAWorkflow -->|"Scheduled Check"| HealthEndpoint

    DriftCheck -->|"Warning"| Webhooks
    DriftCheck -->|"Critical > 50%"| Webhooks
    DriftCheck -->|"Critical + Auto-Recover"| AutoRecovery

    HealthMonitor -->|"Metrics"| Dashboard

    UptimeRobot -->|"HTTP Checks"| HealthEndpoint
    Grafana -->|"Metrics Aggregation"| HealthEndpoint

    AutoRecovery -->|"Rebuild Graph"| FalkorDB
    AutoRecovery -->|"Source Data"| Qdrant

The health endpoint provides real-time service status, database connectivity checks, and enrichment pipeline metrics. This endpoint does not require authentication and is designed for automated health monitoring systems.

PropertyValue
Path/health
MethodGET
AuthenticationNone (public endpoint)
Response TypeJSON
Timeout100 seconds (Railway default)
{
"status": "healthy",
"falkordb": "connected",
"qdrant": "connected",
"memory_count": 884,
"vector_count": 884,
"sync_status": "synced",
"vector_dimensions": {
"configured": 1024,
"effective": 1024,
"collection": 1024,
"mismatch": false
},
"graph": "memories",
"timestamp": "2025-10-20T14:30:00Z",
"enrichment": {
"status": "running",
"queue_depth": 2,
"pending": 2,
"inflight": 0,
"processed": 1247,
"failed": 0
}
}
FieldTypeDescription
statusstringOverall health: "healthy" or "degraded"
falkordbstringFalkorDB status: "connected" or "disconnected"
qdrantstringQdrant status: "connected" or "disconnected"
memory_countinteger|nullTotal memories in FalkorDB (null if query fails)
vector_countinteger|nullTotal points in Qdrant collection (null if unavailable)
sync_statusstringFalkorDB/Qdrant count comparison: "synced", "drift_detected", "orphaned_vectors", or "unknown"
vector_dimensionsobjectConfigured, effective, and Qdrant collection vector dimensions (see below)
enrichmentobjectEnrichment queue metrics (see below)
graphstringFalkorDB graph name (FALKORDB_GRAPH env variable)
timestampstringISO 8601 timestamp of health check

The vector_dimensions object helps detect mismatches between the configured embedding size and the active Qdrant collection:

FieldTypeDescription
configuredintegerVECTOR_SIZE from configuration
effectiveinteger|nullRuntime effective vector size after provider/Qdrant initialization
collectioninteger|nullVector size reported by the Qdrant collection
mismatchbooleantrue when a Qdrant collection dimension differs from VECTOR_SIZE after provider initialization

The enrichment object provides visibility into the background enrichment pipeline:

FieldTypeDescription
statusstringWorker state: "running" or "stopped"
queue_depthintegerTotal jobs in queue (pending + inflight)
pendingintegerJobs waiting to be processed
inflightintegerJobs currently being processed
processedintegerTotal jobs completed since service start
failedintegerTotal jobs that failed permanently
FieldPossible ValuesMeaning
statushealthy, degradedOverall service status
falkordbconnected, disconnectedFalkorDB connection state
qdrantconnected, disconnectedQdrant connection state (optional service)
sync_statussynced, drift_detected, orphaned_vectors, unknownCount comparison between FalkorDB memories and Qdrant vectors
enrichment.statusrunning, stoppedBackground enrichment worker state
sequenceDiagram
    participant Client
    participant API as "app.py<br/>/health"
    participant Falkor as "FalkorDB<br/>redis_ping()"
    participant Qdrant as "Qdrant<br/>client.get_collection()"

    Client->>API: GET /health

    API->>Falkor: Test connection<br/>get_memory_graph()
    alt FalkorDB Available
        Falkor-->>API: graph handle
        API->>API: Set falkordb: connected
    else FalkorDB Unavailable
        Falkor-->>API: None
        API->>API: Set falkordb: disconnected
        API->>API: Set status: degraded
    end

    API->>Qdrant: Test connection<br/>get_collection()
    alt Qdrant Available
        Qdrant-->>API: Collections list
        API->>API: Set qdrant: connected
    else Qdrant Unavailable
        Qdrant-->>API: Exception
        API->>API: Set qdrant: disconnected<br/>and status: degraded
    end

    API->>Falkor: Count memories<br/>MATCH (m:Memory) RETURN count(m)
    Falkor-->>API: count

    API->>API: Check enrichment queue<br/>ServiceState.enrichment_queue

    API-->>Client: 200 OK<br/>{status, falkordb, qdrant,<br/>memory_count, sync_status,<br/>vector_dimensions, enrichment}
Terminal window
# Basic health check
curl https://your-project.up.railway.app/health
# With jq formatting
curl -s https://your-project.up.railway.app/health | jq .
# Check just the status field
curl -s https://your-project.up.railway.app/health | jq .status

AutoMem continues operating even when components are unavailable:

  • Qdrant unavailable: status becomes "degraded", qdrant shows "disconnected", vector fields are null/unknown
  • FalkorDB unavailable: status becomes "degraded", falkordb shows "disconnected"
  • Count drift: sync_status becomes "drift_detected" or "orphaned_vectors"; missing vectors degrade the overall status
  • Enrichment worker stopped: Service remains healthy but enrichment pipeline stops processing
Status CodeMeaningAction
200Health handler returned a JSON bodyInspect the body status, database fields, sync_status, and vector_dimensions
500Health handler failed before returning JSONInvestigate application logs and service startup state

Railway’s health check configuration monitors the /health endpoint to determine service availability. The current handler reports degraded database and sync states in the JSON body, so external monitors should inspect status, sync_status, and vector_dimensions rather than relying only on HTTP status.

Railway health check configuration in railway.json:

{
"healthcheckPath": "/health",
"healthcheckTimeout": 100
}

The analyze endpoint provides comprehensive statistics about the memory graph, including type distributions, entity frequencies, temporal patterns, and relationship counts.

PropertyValue
Path/analyze
MethodGET
AuthenticationRequired (API token via Bearer, X-API-Key, or api_key query parameter)
ResponseHTTP 200 with JSON analytics, or HTTP 401 if unauthorized

The /analyze endpoint runs 6 Cypher queries against FalkorDB and returns their results under a top-level analytics object:

  1. memory_types: Groups memories by m.type, with a per-type count and average confidence
  2. patterns: Memories of type Pattern
  3. preferences: Memories of type Preference
  4. temporal_insights: Extracts the hour from m.timestamp, keyed hour_00hour_23
  5. entity_frequency: Counts entities, keywords, and topics values from each memory’s metadata (top 50)
  6. confidence_distribution: Buckets m.confidence into low (< 0.4), medium (< 0.7), and high

There is no total-memory count, tag-frequency, or relationship-count component. All 6 queries share a single try-except: if any one of them raises, the endpoint returns HTTP 500 with {"error": "Analyze failed", "details": "..."} rather than a partial result. On success the response is {"status": "success", "analytics": {...}, "elapsed_ms": 0}elapsed_ms is currently hardcoded to 0.

sequenceDiagram
    participant Client
    participant API as "app.py<br/>/analyze"
    participant FalkorDB

    Client->>API: GET /analyze<br/>Authorization: Bearer TOKEN

    API->>API: Validate token

    API->>FalkorDB: Group by m.type
    FalkorDB-->>API: memory_types

    API->>FalkorDB: Match type = Pattern
    FalkorDB-->>API: patterns

    API->>FalkorDB: Match type = Preference
    FalkorDB-->>API: preferences

    API->>FalkorDB: Extract hour from timestamp
    FalkorDB-->>API: temporal_insights

    API->>FalkorDB: Read metadata entities/keywords/topics
    FalkorDB-->>API: entity_frequency (top 50)

    API->>FalkorDB: Bucket confidence scores
    FalkorDB-->>API: confidence_distribution

    API-->>Client: 200 OK {status, analytics, elapsed_ms}
Terminal window
# Get memory analytics
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-project.up.railway.app/analyze
# Check relationship distribution
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
https://your-project.up.railway.app/analyze | jq .relationships
Use CaseRelevant Fields
Identify memory class imbalanceanalytics.memory_types
Find frequently discussed projects/toolsanalytics.entity_frequency
Assess memory qualityanalytics.confidence_distribution
Understand activity patternsanalytics.temporal_insights
Review captured patterns and preferencesanalytics.patterns, analytics.preferences

For relationship counts by type, use GET /graph/stats (totals.edges, by_relationship) — /analyze does not report edges.

The startup recall endpoint returns a curated set of memories suitable for initializing AI agent context. It prioritizes high-importance memories and falls back to recent memories.

PropertyValue
Path/startup-recall
MethodGET
AuthenticationNone required
Query ParametersNone
ResponseHTTP 200 with JSON memory list, or HTTP 503 if FalkorDB unavailable

The startup recall endpoint runs two fixed tag queries and returns both result sets:

  • critical_lessons — memories tagged critical, lesson, or ai-assistant, ordered by importance descending, hard-limited to 10
  • system_rules — memories tagged system or memory-recall, hard-limited to 5

Neither limit is configurable, and there is no fallback to recent memories when the tag queries come back empty.

The response also carries lesson_count, has_critical (true when any returned lesson has importance >= 0.9), and a summary string.

The startup recall endpoint is designed for AI agent initialization. An agent can call this endpoint at the start of a session to load relevant context before processing user requests.

Terminal window
# Retrieve startup context
curl https://your-project.up.railway.app/startup-recall | jq '.critical_lessons | length'

The health_monitor.py script provides continuous monitoring with drift detection, alerting, and optional auto-recovery.

sequenceDiagram
    participant HM as health_monitor.py
    participant API as Flask API /health
    participant FK as FalkorDB
    participant QD as Qdrant
    participant WH as Webhook<br/>(Slack/Discord)
    participant REC as recover_from_qdrant.py

    Note over HM: Check Interval<br/>(300s default)

    HM->>API: GET /health
    API-->>HM: {"status": "healthy", ...}

    HM->>FK: MATCH (m:Memory) RETURN count(m)
    FK-->>HM: falkor_count: 884

    HM->>QD: GET /collections/memories
    QD-->>HM: qdrant_count: 884

    HM->>HM: Calculate drift %<br/>|FK - QD| / max(FK, QD)

    alt Drift < 5%
        Note over HM: Normal - No action
    else Drift 5-50%
        HM->>WH: POST warning alert
        Note over WH: "Warning: 12% drift detected"
    else Drift > 50%
        HM->>WH: POST critical alert
        Note over WH: "Critical: 52% drift detected"

        alt Auto-Recover Enabled
            HM->>REC: Execute recovery script
            REC->>QD: Read all vectors + payloads
            REC->>FK: Rebuild graph structure
            REC-->>HM: Recovery complete
            HM->>WH: POST success notification
        else Manual Mode
            Note over HM: Log critical event<br/>Wait for manual intervention
        end
    end

Drift percentage is calculated as:

drift_percent = |falkordb_count - qdrant_count| / max(falkordb_count, qdrant_count) * 100
graph LR
    subgraph "Monitoring Services"
        SyncWorker["Sync Worker Thread<br/>automem/sync/runtime_worker.py<br/>SYNC_CHECK_INTERVAL"]
        HealthMonitor["health_monitor.py<br/>scripts/<br/>External monitoring"]
        HealthEndpoint["/health endpoint<br/>automem/api/health.py<br/>Public access"]
    end

    subgraph "Data Stores"
        FalkorDB[("FalkorDB<br/>MATCH (m:Memory) RETURN count(m)")]
        Qdrant[("Qdrant<br/>client.count()")]
    end

    subgraph "Detection Thresholds"
        Normal["Normal Drift<br/>< 5%<br/>In-flight writes"]
        Warning["Warning Drift<br/>5% - 50%<br/>Failed writes"]
        Critical["Critical Drift<br/>> 50%<br/>Data loss event"]
    end

    subgraph "Response Actions"
        Log["Log Metrics<br/>sync_last_result"]
        Alert["Webhook Alert<br/>Slack/Discord"]
        AutoRecover["Auto Recovery<br/>recover_from_qdrant.py<br/>Optional"]
    end

    SyncWorker --> FalkorDB
    SyncWorker --> Qdrant
    HealthMonitor --> FalkorDB
    HealthMonitor --> Qdrant
    HealthEndpoint --> SyncWorker

    FalkorDB --> DriftCalc["Drift Calculation<br/>abs(falkor - qdrant) / max(falkor, qdrant)"]
    Qdrant --> DriftCalc

    DriftCalc --> Normal
    DriftCalc --> Warning
    DriftCalc --> Critical

    Normal --> Log
    Warning --> Log
    Warning --> Alert
    Critical --> Alert
    Critical --> AutoRecover
ThresholdPercentageActionMeaning
Normal< 5%NoneAcceptable in-flight writes
Warning5-50%Alert webhookPossible failed writes to one store
Critical> 50%Alert webhook + Optional auto-recoveryData loss event likely

Common Causes of Drift:

  • < 1% drift: Normal — in-flight writes during health check
  • 5-10% drift: Failed writes to one database during temporary outage
  • > 50% drift: Critical data loss — one database was cleared/corrupted
flowchart TD
    Monitor["Health Monitor<br/>health_monitor.py"]

    Monitor --> Count["Count memories:<br/>FalkorDB vs Qdrant"]

    Count --> Calc["Calculate drift %:<br/>abs(falkor - qdrant) / max(falkor, qdrant) * 100"]

    Calc --> Check{"Drift level?"}

    Check -->|"< 1%"| Normal["Normal<br/>In-flight writes<br/>No action needed"]
    Check -->|"1-5%"| Warning["Warning<br/>HEALTH_MONITOR_DRIFT_THRESHOLD<br/>Monitor but OK"]
    Check -->|"5-50%"| Alert["Alert<br/>Investigation needed<br/>Webhook notification"]
    Check -->|"> 50%"| Critical["Critical<br/>HEALTH_MONITOR_CRITICAL_THRESHOLD<br/>Data loss event<br/>Trigger recovery"]

    Alert --> Manual["Manual review:<br/>Check logs<br/>Verify writes"]
    Critical --> Auto["Auto-recovery (if enabled):<br/>recover_from_qdrant.py"]
Section titled “Option 1: Railway Service (Recommended for Production)”

Deploy as a dedicated Railway service for continuous monitoring.

Environment Variables for health-monitor service:

AUTOMEM_API_URL=http://memory-service.railway.internal:8001
HEALTH_MONITOR_WEBHOOK=https://hooks.slack.com/...

Auto-recovery and the check interval are command-line only — set them in the service start command, not the environment:

Terminal window
python scripts/health_monitor.py --interval 300 --auto-recover

Pros:

  • Continuous monitoring 24/7
  • Isolated from main service
  • Railway restart policies apply
  • Separate logging and metrics

Cons:

  • Additional Railway service cost (~$2/month)
  • Requires Pro plan resources
Section titled “Option 2: GitHub Actions (Recommended for Free Tier)”

Use GitHub Actions for scheduled health checks without consuming Railway resources.

Pros:

  • Free (2000 minutes/month on free tier)
  • No Railway resource consumption
  • GitHub Actions alerting built-in
  • Simple to set up

Cons:

  • No drift detection (only endpoint checks)
  • No auto-recovery
  • 5-minute minimum interval

Run periodic checks via Railway CLI or local cron.

Pros:

  • No additional deployment complexity
  • Direct access to recovery scripts
  • Easy to test and debug

Cons:

  • Requires active terminal/process
  • No automatic restart on failure
  • Not suitable for production

Health monitor sends JSON payloads to configured webhooks:

{
"level": "critical",
"title": "Data Loss Detected - Manual Recovery Required",
"message": "FalkorDB count dropped below Qdrant count. Drift: 52.3%",
"details": {
"drift_percent": 52.3,
"auto_recover_enabled": false,
"recovery_command": "python scripts/recover_from_qdrant.py"
},
"timestamp": "2025-10-20T14:30:00",
"system": "AutoMem Health Monitor"
}

level is one of info, warning, or critical. Alert-specific values (such as drift_percent) are nested under details, not at the top level, and the payload shape is the same for every alert type.

level is one of info, warning, or critical. Alert-specific values (such as drift_percent) are nested under details, not at the top level.

HEALTH_MONITOR_WEBHOOK=https://hooks.slack.com/services/T.../B.../...

Discord webhooks use the same format as Slack:

HEALTH_MONITOR_WEBHOOK=https://discord.com/api/webhooks/...

Implement your own webhook receiver to handle health alerts by accepting POST requests with the JSON payload format above.

When critical drift is detected (> 50% by default), AutoMem can automatically rebuild the FalkorDB graph from Qdrant vectors.

Auto-recovery is enabled by the --auto-recover command-line flag only. There is no environment-variable equivalent:

Terminal window
python scripts/health_monitor.py --auto-recover

The recovery script performs these steps:

  1. Read all vectors from Qdrant — Retrieves payloads containing memory data
  2. Clear FalkorDB graph — Removes corrupted/incomplete data
  3. Rebuild memory nodes — Creates Memory nodes with all properties
  4. Restore relationships — Rebuilds graph relationships from metadata
  5. Verify counts — Confirms successful recovery

For detailed recovery procedures, see Backup & Recovery.

VariableDefaultDescription
HEALTH_MONITOR_DRIFT_THRESHOLD5Warning threshold (percentage). Overridden by --drift-threshold
HEALTH_MONITOR_CRITICAL_THRESHOLD50Critical threshold (percentage). Overridden by --critical-threshold
HEALTH_MONITOR_WEBHOOKNoneSlack/Discord webhook URL. Overridden by --webhook
HEALTH_MONITOR_EMAILNoneAlert email address (logged only — email sending is not implemented)

Two settings have no environment variable and must be passed as flags: --auto-recover (default: alert only) and --interval (default: 300 seconds).

Personal Use:

  • Health checks: Every 5 minutes (alert-only)
  • Drift monitoring: Every 10 minutes
  • Auto-recovery: Disabled (manual trigger)

Team Use:

  • Health checks: Every 2 minutes
  • Drift monitoring: Every 5 minutes
  • Auto-recovery: Enabled (>50% drift)

Production Use:

  • Health checks: Every 30 seconds
  • Drift monitoring: Every 1 minute
  • Auto-recovery: Enabled (>50% drift) with immediate alerting

Railway + GitHub Actions (Free Tier):

  • GitHub Actions for scheduled /health endpoint checks
  • UptimeRobot for external HTTP monitoring (free tier, 5-minute checks)

Railway Pro (Production):

  • Dedicated health-monitor Railway service
  • Slack/Discord webhook alerts
  • Optional Grafana Cloud integration
ConfigurationMonthly CostServices
Basic~$15Memory service + FalkorDB (no monitoring)
Standard~$18+ Health monitor service (alert-only)
Production~$20+ Health monitor (auto-recovery) + Backup service
ConfigurationCostTrade-offs
Railway + GitHub Actions~$15/monthFree health checks, but no drift detection
Railway + UptimeRobot~$15/monthFree HTTP monitoring, but no database checks
Railway Pro + Grafana~$15/monthAdvanced metrics, but requires configuration

Symptoms:

⚠️ Warning: 12% drift detected
FalkorDB: 884 memories
Qdrant: 778 vectors

Solutions:

  1. < 5% drift: Normal — in-flight writes during check
  2. 5-10% drift: Possible failed writes — check logs for errors
  3. > 50% drift: Run recovery script manually:
    Terminal window
    python scripts/recover_from_qdrant.py

Causes:

  • FalkorDB connection failed
  • Qdrant connection failed (if configured)
  • FalkorDB and Qdrant counts drifted
  • Service still starting up

Solutions:

  • Check FalkorDB container is running: docker ps | grep falkordb
  • Verify FalkorDB environment variables: FALKORDB_HOST, FALKORDB_PORT, FALKORDB_PASSWORD
  • Inspect sync_status and vector_dimensions in /health
  • Wait 30-60 seconds if service was just deployed

Symptoms:

  • Critical drift detected
  • Alert sent to webhook
  • But recovery script not running

Solutions:

  • Verify the monitor was started with the --auto-recover flag (there is no environment-variable equivalent)
  • Check that scripts/recover_from_qdrant.py is accessible
  • Review health monitor logs for permission errors

All three monitoring endpoints emit structured logs for observability:

  • /health — logs connection check results and memory counts
  • /analyze — logs query execution times for each analytics query
  • /startup-recall — logs number of memories returned by each phase

The structured log format uses Python’s extra={} parameter to include machine-parseable fields alongside log messages, enabling automated performance analysis and metrics dashboard integration. See Performance Tuning for details on the logging fields and how to parse them.