Skip to main content
DeployStack Satellite sends periodic heartbeats to the Backend every 30 seconds to maintain connectivity status, report system health, and provide real-time visibility into running MCP server processes.

Overview

Purpose

The heartbeat system enables:
  • Health Monitoring: Track satellite connectivity and availability
  • System Metrics: Monitor CPU, memory, disk usage, and uptime
  • MCP Process Tracking: Real-time visibility into running MCP server instances
  • Tool Discovery Status: Track discovered tools and server availability
  • Automatic Activation: Satellites become active on first heartbeat

Communication Pattern

Characteristics:
  • Interval: 30 seconds (fixed)
  • Transport: HTTPS POST with Bearer token authentication
  • Direction: Satellite → Backend (outbound-only, firewall-friendly)
  • Timeout: 10 seconds

Heartbeat Payload Structure

TypeScript Interface

System Metrics

Collection:
  • Memory: From process.memoryUsage().heapUsed
  • Uptime: From process.uptime()
  • CPU/Disk: Placeholder (future implementation)

MCP Status Snapshot

The mcp_status field provides lightweight runtime visibility into MCP servers:
Key Characteristics:
  • Lightweight: Excludes tool descriptions/schemas (~8-15KB vs ~200-500KB)
  • Per-User Instances: Includes instance_id for granular tracking
  • Fresh Data: Replaced on each heartbeat (no accumulation)
  • Optional: Missing if satellite has no running MCP servers

Backend Processing

Endpoint

Route: POST /api/satellites/{satelliteId}/heartbeat Authentication: Satellite API key (Bearer token)

Database Operations

1. Update Satellite Status and MCP Status
2. Insert Heartbeat Record
3. Update Process Statuses (if included)
  • Upserts records in satelliteProcesses table

Data Retention

Cleanup Policy:
  • Global satellites: Keep 1000 most recent heartbeats (~8.3 hours at 30s intervals)
  • Team satellites: Keep 500 most recent heartbeats (~4.2 hours)
  • Cron job: Runs every 3 minutes

Database Schema

satellites Table

The satellites table stores the latest MCP status from the most recent heartbeat:
Usage:
  • Latest Status: Query this table to get the current MCP status of a satellite
  • Real-Time View: Always reflects the most recent heartbeat data
  • Lightweight Query: No need to scan heartbeat history

satelliteHeartbeats Table

Indexes:
  • (satellite_id, timestamp) - Query heartbeats for specific satellite
  • (status) - Filter by satellite status
  • (timestamp) - Time-series queries
Purpose:
  • Historical record of all heartbeats
  • Time-series analysis of system metrics
  • Audit trail for satellite connectivity

Querying Heartbeat Data

Query the satellites table for the latest MCP status:
Why use this?
  • Fastest query (single row lookup)
  • Always current (updated on each heartbeat)
  • No need to scan heartbeat history

Extract MCP Status Summary

Get Per-Instance Details

Get Historical System Metrics

Query the satelliteHeartbeats table for system metrics over time:

Monitor Heartbeat Lag

Testing & Debugging

Enable Debug Logging

Watch for Heartbeat Logs

Success:
Failure:

Verify Payload Size

Expected payload size: ~13-18 KB per heartbeat
  • System metrics: ~500 bytes
  • MCP status: ~8-15 KB (lightweight snapshot)
  • Processes by team: ~5-10 KB
Check logs for large payloads (may indicate issue with data collection).

Manual Heartbeat Trigger

Satellites send heartbeats automatically every 30 seconds. For testing:
  1. Start satellite
  2. Wait 30 seconds for first heartbeat
  3. Check backend logs for POST /api/satellites/{id}/heartbeat
  4. Query database for new record

Implementation Details

Satellite Side

File: services/satellite/src/services/heartbeat-service.ts Key methods:
  • start() - Initializes 30s interval
  • sendHeartbeat() - Collects data and sends POST request
  • collectSystemMetrics() - Gathers CPU, memory, uptime
  • collectStdioProcessesByTeam() - Groups stdio processes by team
  • collectMcpStatusSnapshot() - Builds lightweight MCP status
Dependencies:
  • RuntimeState - Process tracking
  • UnifiedToolDiscoveryManager - Tool and server status
  • BackendClient - HTTP communication

Backend Side

File: services/backend/src/routes/satellites/heartbeat.ts Processing steps:
  1. Validate satellite exists
  2. Update satellites.last_heartbeat and status
  3. Insert satelliteHeartbeats record
  4. Parse and store mcp_status JSON
  5. Update satelliteProcesses if included

Troubleshooting

Heartbeat Not Being Sent

Check:
  • Is satellite registered? (runtimeState.satelliteId set?)
  • Backend URL correct? (DEPLOYSTACK_BACKEND_URL)
  • Network connectivity? (firewall, DNS)
  • API key valid? (not revoked)

Backend Rejecting Heartbeat

Check:
  • JSON schema validation errors in backend logs
  • Satellite ID exists in database
  • API key authentication header correct

Old Heartbeats Not Cleaned Up

Check:
  • Cleanup cron job running (cleanupSatelliteHeartbeats)
  • Worker logs for errors
  • Database retention limits configured

Large Payload Size

Expected: ~13-18 KB If larger: Check mcp_status collection - may be including descriptions/schemas