Featured image of post Build an Automated Data Monitoring & Alert System with DuckDB + Cron

Build an Automated Data Monitoring & Alert System with DuckDB + Cron

Learn how to build a fully automated data health monitoring system using DuckDB, Python, and Telegram Bot API. Detect anomalies in real-time and get instant notifications—zero cost.

Build an Automated Data Monitoring & Alert System with DuckDB + Cron

Do you ever face this pain point—your business data grows every day, but you have no idea when something goes wrong. By the time your customers complain, the damage is already done.

Today I’ll show you how to build a fully automated Data Health Monitoring System using DuckDB. It runs queries on a schedule, detects anomalies, and sends instant Telegram alerts. No complex ETL, no expensive monitoring tools—just a Python script + DuckDB.

DuckDB Data Monitoring & Alert System Architecture

1. Why Do You Need Data Monitoring?

Let’s clarify the value first. Why build a monitoring system?

  • E-commerce: GMV drops 30% overnight—you need to catch it before customers place orders, not discover it the next day from reports
  • Finance: A stock breaks through a key level with high volume—push the signal to subscribers instantly
  • SaaS: User registrations drop for 3 consecutive days—something is broken in your product
  • Content: A video’s views spike unexpectedly—catch the trend while it’s hot

The common thread: fast detection, accurate alerts, minimal cost.

DuckDB’s role in this system: it’s not a database—it’s a query engine. It reads directly from CSV/Parquet/API files, runs one SQL query to find anomalies, and hands results to Telegram for delivery.

2. System Architecture

CSV/Parquet Data Sources
        ↓
   DuckDB (Query Engine)
        ↓
   Python Anomaly Detection
        ↓
   Telegram Bot API
        ↓
   Real-time Mobile Notifications

Everything runs in Python with zero external service dependencies. Data is stored in a single .duckdb file with incremental appends.

3. Core Monitoring Logic

Let’s say you’re an e-commerce seller tracking three core metrics:

  • Daily order volume anomalies
  • Sustained decline in average order value
  • Sudden spikes in refund rate

Your data source is daily order CSV exports. Here’s the DuckDB monitoring query:

import duckdb
import json
from datetime import datetime, timedelta

con = duckdb.connect("monitor.duckdb")

# Merge historical orders into a persistent table (one-time import)
con.execute("""
    CREATE TABLE IF NOT EXISTS orders AS
    SELECT * FROM read_csv_auto('orders_2024*.csv')
""")

# Monitor query: compute core metrics for the past 14 days
def check_health():
    today = datetime.now().date()
    
    result = con.execute("""
        WITH daily_stats AS (
            SELECT
                DATE(order_date) AS stat_date,
                COUNT(*) AS order_count,
                ROUND(AVG(amount), 2) AS avg_order_value,
                ROUND(SUM(CASE WHEN status = 'refunded' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS refund_rate
            FROM orders
            WHERE DATE(order_date) >= CURRENT_DATE - INTERVAL '14' DAY
            GROUP BY DATE(order_date)
        )
        SELECT
            MAX(CASE WHEN stat_date = CURRENT_DATE - INTERVAL '1' DAY THEN order_count END) AS yesterday_orders,
            AVG(order_count) OVER (ORDER BY stat_date ROWS BETWEEN 6 PRECEDING AND 1 PRECEDING) AS ma7_orders,
            MAX(CASE WHEN stat_date = CURRENT_DATE - INTERVAL '1' DAY THEN refund_rate END) AS yesterday_refund_rate,
            AVG(refund_rate) OVER (ORDER BY stat_date ROWS BETWEEN 6 PRECEDING AND 1 PRECEDING) AS ma7_refund_rate
        FROM daily_stats
        ORDER BY stat_date DESC
        LIMIT 1
    """).fetchone()
    
    return {
        'yesterday_orders': result[0],
        'ma7_orders': result[1],
        'yesterday_refund_rate': result[2],
        'ma7_refund_rate': result[3]
    }

Key points:

  1. read_csv_auto('orders_2024*.csv') — DuckDB supports wildcards to read multiple files at once, no manual concatenation needed
  2. Window function AVG(...) OVER (...) — Computes 7-day moving average without pre-aggregation
  3. Results persist in .duckdb file — Incremental append, not full rebuild each time

4. Anomaly Detection Algorithm

With baseline metrics in hand, the next step is determining whether something is actually abnormal.

We use a simple statistical approach: if the current value deviates from the 7-day average beyond a threshold, flag it as anomalous.

def detect_anomalies(stats):
    """Returns a list of anomalies, each with metric name, current value, expected range, and severity."""
    anomalies = []
    
    # Order volume anomaly
    if stats['ma7_orders'] > 0:
        order_drop = (stats['yesterday_orders'] - stats['ma7_orders']) / stats['ma7_orders']
        if order_drop < -0.20:  # Drop exceeds 20%
            anomalies.append({
                'metric': 'Orders',
                'value': stats['yesterday_orders'],
                'expected': f"~{stats['ma7_orders']:.0f}",
                'severity': '🔴 Critical',
                'message': f"Yesterday's orders: {stats['yesterday_orders']}, down {abs(order_drop)*100:.1f}% from 7-day avg"
            })
        elif order_drop > 0.50:  # Surge exceeds 50%
            anomalies.append({
                'metric': 'Orders',
                'value': stats['yesterday_orders'],
                'expected': f"~{stats['ma7_orders']:.0f}",
                'severity': '🟡 Attention',
                'message': f"Yesterday's orders: {stats['yesterday_orders']}, up {order_drop*100:.1f}% from 7-day avg—verify if this is a legitimate promotion"
            })
    
    # Refund rate anomaly
    if stats['ma7_refund_rate'] > 0:
        refund_spike = (stats['yesterday_refund_rate'] - stats['ma7_refund_rate']) / stats['ma7_refund_rate']
        if refund_spike > 0.50:  # Refund rate surges 50%
            anomalies.append({
                'metric': 'Refund Rate',
                'value': f"{stats['yesterday_refund_rate']:.2f}%",
                'expected': f"~{stats['ma7_refund_rate']:.2f}%",
                'severity': '🔴 Critical',
                'message': f"Yesterday's refund rate: {stats['yesterday_refund_rate']:.2f}%, up {refund_spike*100:.1f}% from 7-day avg"
            })
    
    return anomalies

Benefits of this approach:

  • Tunable thresholds (-0.20, 0.50)—adjust based on business sensitivity
  • Detects both drops and surges in a single pass
  • Structured output—easy to push via Telegram or log to file

5. Telegram Push Module

Use Python’s requests library to call the Telegram Bot API and push anomaly alerts to a channel or group.

import requests

TELEGRAM_BOT_TOKEN = "YOUR_BOT_TOKEN"
CHAT_ID = "YOUR_CHAT_ID"  # Group ID or channel ID

def send_telegram(message):
    url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
    payload = {
        'chat_id': CHAT_ID,
        'text': message,
        'parse_mode': 'HTML'
    }
    try:
        r = requests.post(url, json=payload, timeout=10)
        return r.json().get('ok', False)
    except Exception as e:
        print(f"Push failed: {e}")
        return False

def notify_anomalies(anomalies):
    if not anomalies:
        send_telegram(
            f"✅ <b>Daily Data巡检 Complete</b>\n\n"
            f"📅 {datetime.now().strftime('%Y-%m-%d')}\n"
            f"All metrics normal. No action needed.\n\n"
            f"— DuckDB Monitor Bot"
        )
        return
    
    lines = [
        f"🚨 <b>Data Anomaly Alert</b>",
        f"📅 {datetime.now().strftime('%Y-%m-%d %H:%M')}",
        ""
    ]
    
    for a in anomalies:
        lines.append(f"{a['severity']} {a['metric']}")
        lines.append(f"   Current: {a['value']}")
        lines.append(f"   Expected: {a['expected']}")
        lines.append(f"   {a['message']}")
        lines.append("")
    
    lines.append("--- DuckDB Monitor Bot")
    
    send_telegram("\n".join(lines))

6. Complete Script & Cron Scheduling

Now combine everything into a complete monitoring script:

import os
import glob
import json
from datetime import datetime

def incremental_update():
    """Incremental update: only import new CSV files"""
    today_str = datetime.now().strftime('%Y%m%d')
    new_files = glob.glob(f'orders_{today_str}*.csv')
    
    if not new_files:
        print("No new data files today")
        return
    
    for f in new_files:
        count = con.execute(f"SELECT COUNT(*) FROM read_csv_auto('{f}')").fetchone()[0]
        con.execute(f"""
            INSERT INTO orders
            SELECT * FROM read_csv_auto('{f}')
            WHERE order_id NOT IN (SELECT order_id FROM orders)
        """)
        print(f"Imported {f}: {count} records")

def main():
    # 1. Incremental data update
    incremental_update()
    
    # 2. Run health check
    stats = check_health()
    anomalies = detect_anomalies(stats)
    
    # 3. Push results
    notify_anomalies(anomalies)
    
    # 4. Log today's run
    log_entry = {
        'date': datetime.now().strftime('%Y-%m-%d'),
        'anomalies': len(anomalies),
        'stats': stats
    }
    with open('monitor_log.jsonl', 'a') as f:
        f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
    
    print(f"巡检 complete. Found {len(anomalies)} anomalies")

if __name__ == '__main__':
    main()

Schedule with cron (Linux):

# Run automatically at 9 AM every day
0 9 * * * cd /home/user/duckdb-monitor && python3 monitor.py >> monitor.log 2>&1

Or use macOS launchd or Windows Task Scheduler—the principle is the same.

7. Advanced: Multi-Source Cross-Database Monitoring

Real-world monitoring often requires crossing data sources. Your e-commerce data is in Shopify API, logistics data is in a JSON file, and financial data is in Excel.

DuckDB’s strength shines here—you don’t need to ETL everything into one database first. Just ATTACH different sources:

# Attach: Shopify orders (CSV)
con.execute("ATTACH 'shopify_orders.csv' AS shopify (READ_ONLY)")

# Attach: Logistics data (JSON)
con.execute("ATTACH 'logistics.json' AS log (READ_ONLY, TYPE JSON)")

# Attach: Financial data (Excel)
con.execute("ATTACH 'finance.xlsx' AS fin (READ_ONLY)")

# Cross-source query: find SKUs with late shipping AND high refund rates
con.execute("""
    SELECT 
        s.sku,
        s.product_name,
        s.order_count,
        AVG(l.transit_days) AS avg_transit,
        SUM(CASE WHEN f.status = 'refunded' THEN 1 ELSE 0 END) AS refund_count
    FROM shopify.orders s
    LEFT JOIN log.packages l ON s.tracking_number = l.tracking_id
    LEFT JOIN fin.refunds r ON s.order_id = r.order_id
    GROUP BY s.sku, s.product_name
    HAVING avg_transit > 7 AND refund_count > 5
    ORDER BY refund_count DESC
""").fetchdf()

ATTACH is one of DuckDB’s most underrated features. It lets you query CSV, JSON, Excel, Parquet, PostgreSQL, MySQL, and more—in a single query—without any ETL preprocessing.

8. Comparison with Traditional Monitoring Tools

FeatureDuckDB + PythonGrafana + PrometheusCommercial SaaS (Datadog)
Data source accessDirect CSV/JSON/Excel readRequires Exporter agentsAPI integration, complex config
Anomaly detectionCustom Python logicAlert rule configurationBuilt-in AI anomaly detection
Push notificationsTelegram Bot APISlack/PagerDutyMulti-channel
Deployment cost$0 (local/VPS)Self-hosted infrastructure$20-100/node/month
Learning curvePython + SQLHighMedium
CustomizationFully controllableMediumLow
Best forSMBs, indie developersLarge infra teamsEnterprise

9. Monetization Strategies

This system isn’t just a personal tool—it can become a revenue stream:

🟢 Low-Cost Approach ($0-5,000 startup)

  • Model: Deploy this system for 10 e-commerce sellers at $299/month, annual plan $2,999
  • Steps:
    1. Template the code so clients only replace data source paths
    2. Deploy on cheap cloud VPS (e.g., Alibaba Cloud ECS at ~$7/month)
    3. Separate Telegram group per client with independent config
  • Expected monthly revenue: $2,990-5,000
  • Best for: Individual developers with 1-2 client connections

🟡 Medium-Cost Approach ($5,000-50,000 startup)

  • Model: Data product subscription. Package monitoring results into daily briefing reports pushed via Telegram channel, priced at $99/month
  • Steps:
    1. Select a specific industry (e.g., cross-border e-commerce, stocks)
    2. Build a universal monitoring pipeline that auto-generates industry analysis reports
    3. Drive traffic via social media, monetize through Telegram paid channel
  • Expected monthly revenue: $5,000-20,000 (100-200 subscribers)
  • Best for: Content creators with existing industry audience

🔴 High-Cost Approach ($50,000+ startup)

  • Model: Embed as a value-added service in your consulting/agency offerings. Charge $500-2,000/month per client for “data health monitoring”
  • Steps:
    1. Add data monitoring module to your existing consulting services
    2. Monthly maintenance fee on top of core service
    3. Periodic threshold and metric optimization
  • Expected monthly revenue: $10,000-50,000 (depends on client count)
  • Best for: Freelancers or small studios with existing client base

Core principle: You’re not selling code—you’re selling “peace of mind, no more waking up to data disasters at midnight.”

10. Summary

ComponentTechnologyCost
Data queryingDuckDB (Python API)Free
Data storageSingle .duckdb fileFree
Anomaly detectionPython logic + SQL window functionsFree
Push notificationsTelegram Bot APIFree
SchedulingLinux cron / cloud VPSFree
Total cost$0

Compared to commercial monitoring tools (Grafana + PagerDuty, thousands to tens of thousands annually), this approach is functionally competitive at zero cost.

Remember: The essence of monitoring isn’t technology—it’s the awareness to catch problems before they cascade. DuckDB just makes it trivially simple.


📖 Full runnable code (including multi-source ATTACH examples, Telegram template, and cron configuration guide) is available at duckdblab.org, with complete step-by-step deployment instructions.

💡 Want to systematically learn DuckDB for monitoring, automation, and data products? duckdblab.org has a complete tutorial series.

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

Built with Hugo
Theme Stack designed by Jimmy

⚠️ This site is an independent community project, not affiliated with, endorsed by, or sponsored by the DuckDB Foundation or official DuckDB project.

"DuckDB" is a registered trademark of the DuckDB Foundation. This site uses the name solely for factual description purposes.

All content is for educational and community promotion purposes only and does not constitute any commercial service.