Motia Icon

Real-Time Uptime Monitoring: Building a Resilient Website Monitor with Motia

In today's modern era, website uptime is critical for business success. Whether you're monitoring a personal blog or enterprise applications, you need a reliable system that can detect outages, send alerts, and provide visibility into your site's health. Traditional monitoring solutions often involve complex infrastructure and vendor lock-in, but there's a better way.

This comprehensive guide explores how to build a production-ready uptime monitoring system using Motia's event-driven architecture. We'll cover:

  1. Event-Driven Monitoring: How Motia's steps create a scalable, maintainable monitoring pipeline.
  2. Building the Architecture: A detailed walkthrough of our five-component monitoring system.
  3. Smart Alerting: Implementing rate limiting and status change detection to prevent spam.

Let's build a monitoring system that actually works for you.


The Power of Event-Driven Monitoring

Uptime Monitor Architecture

At its core, our uptime monitoring system solves a fundamental challenge: how do you continuously monitor multiple websites without creating a brittle, monolithic application? Traditional monitoring tools often suffer from tight coupling, making them difficult to scale and customize. Our Motia-powered solution breaks this down into discrete, event-driven components that each handle a specific aspect of monitoring.

The magic happens through the integration of proven technologies and patterns:

Instead of a monolithic monitoring daemon, we get a resilient architecture where each component can be scaled, modified, or replaced independently.


The Anatomy of Our Monitoring System

Our application consists of five specialized steps, each handling a specific part of the monitoring workflow. Let's explore the complete architecture.

cron.step.js
checker.step.js
alerter.step.js
health.step.js
env.js
rate-limiter.js
streams.js

The heartbeat of our monitoring system. This cron-triggered step periodically emits check requests for all configured websites, acting as the central scheduler.

import { config as envConfig } from '../lib/env.js';
 
export const config = {
  type: 'cron',
  name: 'UptimeCronTrigger',
  cron: envConfig.cron,
  emits: ['check.requested'],
  flows: ['uptime-monitoring']
};
 
export async function handler(context) {
  context.logger.info(`Starting uptime checks for ${envConfig.sites.length} sites`);
  context.logger.info(`Sites configured: ${JSON.stringify(envConfig.sites)}`);
 
  try {
    // Emit one check.requested event per configured site URL
    for (const url of envConfig.sites) {
      context.logger.info(`Scheduling check for: ${url}`);
      
      await context.emit({ 
        topic: 'check.requested', 
        data: { url: url } 
      });
      
      context.logger.info(`Successfully emitted for: ${url}`);
    }
 
    context.logger.info(`Successfully scheduled checks for all ${envConfig.sites.length} sites`);
  } catch (error) {
    context.logger.error('Error during cron execution:', error);
    throw error;
  }
}

Explore the Workbench

The Motia Workbench provides a visual representation of your monitoring pipeline, making it easy to understand the event flow and debug issues in real-time.

Uptime Monitor in Motia Workbench

You can monitor real-time status checks, view Discord alert logs, and trace the execution of each step directly in the Workbench interface. This makes development and debugging significantly easier compared to traditional monitoring solutions.


Key Features & Benefits

Event-Driven Architecture

Each component is independent and communicates through events, making the system highly scalable and maintainable.

🎯 Smart Status Detection

Only triggers alerts when status actually changes (UP ↔ DOWN), eliminating noise from temporary fluctuations.

🚨 Intelligent Rate Limiting

Token bucket algorithm prevents alert spam during site flapping while ensuring critical alerts get through.

📊 Built-in Observability

Comprehensive logging, health checks, and real-time status tracking with persistent storage.

🔧 Production-Ready

Robust error handling, timeout management, and configurable check intervals ensure reliability.

🎨 Rich Discord Alerts

Beautiful embedded messages with response times, error details, and status transitions.


Data Flow & Event Architecture

Uptime Monitor Event Flow

Event Flow

  1. Cron Trigger → Emits check.requested events for each configured site
  2. Website Checker → Receives check.requested, performs HTTP check
  3. Status Update → Checker emits check.result with result
  4. Alert Processing → Alerter receives check.result, detects status changes
  5. Discord Notification → Alerter sends webhook if status changed and rate limit allows
  6. Status Storage → Status is persisted for health endpoint and future comparisons

Trying It Out

Ready to build your own production-ready monitoring system? Let's get it running.

Install Dependencies

Install the necessary npm packages and set up the development environment.

npm install

Configure Environment Variables

Create a .env file with your monitoring configuration. You'll need a Discord webhook URL and the sites you want to monitor.

# Required: Discord webhook for alerts
DISCORD_WEBHOOK="https://discord.com/api/webhooks/123456789/your-webhook-token"
 
# Required: JSON array of URLs to monitor
SITES='["https://example.com", "https://api.yourdomain.com", "https://blog.yoursite.org"]'
 
# Optional: Check frequency (default: every minute)
CHECK_INTERVAL_CRON="*/1 * * * *"
 
# Optional: Rate limiting (default: 3 alerts per 5 minutes)
ALERT_BURST="3"
ALERT_WINDOW_SEC="300"

Set Up Discord Webhook

Create a Discord webhook to receive alerts:

  1. Go to your Discord server settings
  2. Navigate to IntegrationsWebhooks
  3. Click New Webhook
  4. Copy the webhook URL and add it to your .env file

Run the Monitoring System

Start the Motia development server to begin monitoring your websites.

npm run dev

Check System Health

Verify your monitoring system is working correctly:

curl http://localhost:3000/healthz

You should see a response with your configured sites and their current status:

{
  "status": "ok",
  "sitesConfigured": 3,
  "lastKnown": {
    "https://example.com": {
      "url": "https://example.com",
      "status": "UP",
      "code": 200,
      "responseTime": 245,
      "checkedAt": "2024-01-15T10:30:00.000Z",
      "error": null
    }
  },
  "now": "2024-01-15T10:35:00.000Z"
}

Monitor the Logs

Watch the logs to see your monitoring system in action:

  • Cron triggers: Check scheduling for all configured sites
  • Website checks: HTTP requests and response analysis
  • Status changes: UP/DOWN transitions and Discord alerts
  • Rate limiting: Alert suppression during site flapping

Advanced Configuration

Custom Check Intervals

Modify the cron expression to change monitoring frequency:

# Every 5 minutes
CHECK_INTERVAL_CRON="*/5 * * * *"
 
# Every hour
CHECK_INTERVAL_CRON="0 * * * *"
 
# Every 6 hours
CHECK_INTERVAL_CRON="0 */6 * * *"
 
# Business hours only (9 AM - 5 PM, Mon-Fri)
CHECK_INTERVAL_CRON="* 9-17 * * 1-5"

Alert Rate Limiting

Fine-tune the rate limiting to match your needs:

# Very strict: 1 alert per 10 minutes
ALERT_BURST="1"
ALERT_WINDOW_SEC="600"
 
# More permissive: 5 alerts per 2 minutes
ALERT_BURST="5"  
ALERT_WINDOW_SEC="120"

Multi-Environment Monitoring

Set up different monitoring configurations for different environments:

# Production sites
SITES='["https://app.production.com", "https://api.production.com"]'
 
# Staging sites  
SITES='["https://app.staging.com", "https://api.staging.com"]'
 
# Development sites
SITES='["https://app.dev.com", "http://localhost:8080"]'

Custom Discord Alert Formatting

Modify the createDiscordMessage function in alerter.step.js to customize alert appearance:

function createDiscordMessage(result, previousStatus) {
  const { url, status, code, responseTime } = result
  
  // Custom colors for your brand
  const color = status === 'UP' ? 0x2ecc71 : 0xe74c3c
  
  // Custom emoji and formatting
  const emoji = status === 'UP' ? '✅' : '❌'
  const urgency = responseTime > 5000 ? '🐌 SLOW' : '⚡ FAST'
  
  return {
    content: `${emoji} **${url}** is ${status}`,
    embeds: [{
      title: `${urgency} Website ${status}`,
      description: `**${url}** changed from ${previousStatus} to ${status}`,
      color,
      timestamp: result.checkedAt,
      fields: [
        {
          name: '⏱️ Response Time',
          value: `${responseTime}ms`,
          inline: true
        },
        {
          name: '📊 Status Code', 
          value: code?.toString() || 'N/A',
          inline: true
        }
      ]
    }]
  }
}

Troubleshooting

Common Issues

Sites not being checked:

  • Verify SITES environment variable is valid JSON
  • Check cron expression syntax using crontab.guru
  • Review logs for parsing errors

Discord alerts not working:

  • Verify DISCORD_WEBHOOK URL is correct
  • Test webhook manually: curl -X POST $DISCORD_WEBHOOK -H "Content-Type: application/json" -d '{"content":"Test message"}'
  • Check Discord webhook permissions

High memory usage:

  • Monitor status store size with health endpoint
  • Consider implementing status history cleanup
  • Reduce check frequency for many sites

False positive alerts:

  • Increase timeout values in checker step
  • Implement retry logic before marking as DOWN
  • Adjust rate limiting to reduce noise

Performance Tips

  • Large Site Lists: Consider sharding across multiple instances
  • Slow Sites: Implement custom timeout values per site
  • High Frequency: Use Redis for status storage instead of file system
  • Alert Fatigue: Implement escalation policies and alert grouping

Monitoring the Monitor

Set up monitoring for your monitoring system:

# Monitor the health endpoint itself
curl -f http://localhost:3000/healthz || echo "Monitor is down!"
 
# Check for recent status updates
curl http://localhost:3000/healthz | jq '.lastKnown | to_entries | map(select(.value.checkedAt > (now - 300)))'
 
# Verify all sites are being checked
curl http://localhost:3000/healthz | jq '.sitesConfigured == (.lastKnown | length)'

💻 Dive into the Code

Want to explore the complete monitoring implementation? Check out the full source code, including all steps, utilities, and configuration examples:

Complete Uptime Monitor

Access the full implementation with event steps, utility libraries, Discord integration, and production-ready configuration.


Conclusion: Monitoring That Actually Works

This uptime monitoring system demonstrates the power of event-driven architecture for infrastructure monitoring. By breaking down monitoring into discrete, specialized components, we've created a system that's not only reliable but also extensible and maintainable.

The event-driven approach means you can easily:

  • Add new notification channels (Slack, PagerDuty, email) by creating new steps
  • Implement custom health checks (database connectivity, API endpoints, SSL certificates)
  • Scale monitoring across multiple regions or environments
  • Integrate with existing systems without disrupting the core monitoring loop

Key architectural benefits:

  • Resilience: Component failures don't bring down the entire system
  • Observability: Built-in logging and tracing at every step
  • Flexibility: Easy to modify check intervals, alert logic, or add new features
  • Testing: Each component can be tested in isolation

From here, you can extend the system by:

  • Adding support for different check types (TCP, database, custom health endpoints)
  • Implementing escalation policies and on-call rotations
  • Building a web dashboard for historical data and trends
  • Adding integration with incident management systems
  • Implementing multi-region monitoring with failover

The event-driven architecture makes all of these extensions straightforward to implement without disrupting the existing monitoring pipeline.

Ready to build monitoring infrastructure that scales with your business? Start building with Motia today!

Need help? See our Community Resources for questions, examples, and discussions.