Every minute your site is down costs you. Customers leave. Search rankings slip. Revenue stops. The worst part? Most site outages last 20–40 minutes before the owner finds out — because they found out from a customer complaint, not a system alert. This guide shows you how to set up a monitor that checks your site every 5 minutes and sends you a text the moment anything goes wrong. No monthly dashboards, no third-party subscriptions. You own it.
The basic question — "is it responding?" — is table stakes. A real monitor catches the things that look fine from the outside but aren't:
The setup in this guide catches all of those — not just whether a server sends back "200 OK."
A cron job that runs every 5 minutes on your agent machine. Each run does four things:
If any check fails, it fires an alert immediately. If the site comes back, it sends an "all clear" so you know the incident is over. It also keeps a running log so you can review your uptime history.
No false alarm spam: The monitor uses a two-strike rule. One failed check doesn't alert — it retries in 90 seconds. Two consecutive failures trigger the alert. This eliminates the 2 AM texts from a momentary network blip.
If you already have OpenClaw running, you have everything you need except the Twilio account. Setup takes about 25 minutes.
Go to twilio.com, create a free account, and get a phone number ($1/mo). You'll get an Account SID and Auth Token. Save these — you'll need them in the next step. If you'd rather use Discord or Slack, you can skip this and use a webhook instead. Both work; SMS is just harder to miss at 2 AM.
Add four lines to your agent's environment file (usually ~/.patrick-env or equivalent). Never hardcode credentials in the script itself.
# Add to ~/.patrick-env (or your agent's env file)
MONITOR_TWILIO_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MONITOR_TWILIO_TOKEN=your_auth_token_here
MONITOR_TWILIO_FROM=+15005550006 # your Twilio number
MONITOR_ALERT_TO=+13035550199 # your personal number
# Sites to monitor (comma-separated)
MONITOR_URLS=https://yoursite.com,https://yoursite.com/checkout,https://yoursite.com/login
# Thresholds
MONITOR_RESPONSE_TIMEOUT=4000 # alert if response takes >4 seconds (ms)
MONITOR_SSL_WARN_DAYS=14 # alert if SSL cert expires within 14 days
# Error strings that indicate a broken page even with 200 status
MONITOR_ERROR_STRINGS=database error,connection failed,500 internal,maintenance mode,coming soon
Save this as ~/scripts/website-monitor.sh. It's a single shell script — no dependencies, no npm packages, nothing to install. Uses curl which is already on every machine.
#!/bin/bash
# website-monitor.sh — runs every 5 min via cron
# Checks: HTTP status, content errors, SSL expiry, response time
source ~/.patrick-env
STATE_FILE="/tmp/monitor-state.json"
LOG_FILE="$HOME/workspace-patrick/memory/uptime-log.md"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S %Z')
send_alert() {
local msg="$1"
echo "[$TIMESTAMP] ALERT: $msg" >> "$LOG_FILE"
# SMS via Twilio
if [ -n "$MONITOR_TWILIO_SID" ]; then
curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts/$MONITOR_TWILIO_SID/Messages.json" \
--data-urlencode "From=$MONITOR_TWILIO_FROM" \
--data-urlencode "To=$MONITOR_ALERT_TO" \
--data-urlencode "Body=$msg" \
-u "$MONITOR_TWILIO_SID:$MONITOR_TWILIO_TOKEN" > /dev/null
fi
}
check_site() {
local url="$1"
local domain=$(echo "$url" | sed 's|https://||' | cut -d'/' -f1)
# Measure response time and grab HTTP code
local start=$(date +%s%3N)
local response=$(curl -s -o /tmp/monitor-body.txt -w "%{http_code}" \
--max-time 10 --connect-timeout 5 "$url" 2>/dev/null)
local elapsed=$(( $(date +%s%3N) - start ))
# --- Check 1: HTTP status ---
if [ "$response" != "200" ]; then
send_alert "🔴 SITE DOWN: $url returned HTTP $response at $TIMESTAMP"
return 1
fi
# --- Check 2: Content error strings ---
for err_str in $(echo "$MONITOR_ERROR_STRINGS" | tr ',' '\n'); do
if grep -qi "$err_str" /tmp/monitor-body.txt; then
send_alert "🔴 ERROR ON PAGE: $url contains \"$err_str\" at $TIMESTAMP"
return 1
fi
done
# --- Check 3: Response time ---
if [ "$elapsed" -gt "$MONITOR_RESPONSE_TIMEOUT" ]; then
local secs=$(echo "scale=1; $elapsed / 1000" | bc)
send_alert "🟡 SLOW: $url took ${secs}s to respond (threshold: $(echo "scale=1; $MONITOR_RESPONSE_TIMEOUT/1000" | bc)s)"
fi
# --- Check 4: SSL certificate expiry ---
local expiry=$(echo | openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -n "$expiry" ]; then
local exp_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$expiry" +%s 2>/dev/null)
local now_epoch=$(date +%s)
local days_left=$(( (exp_epoch - now_epoch) / 86400 ))
if [ "$days_left" -lt "$MONITOR_SSL_WARN_DAYS" ]; then
send_alert "🟡 SSL EXPIRING: $domain cert expires in ${days_left} days ($expiry)"
fi
fi
echo "[$TIMESTAMP] OK: $url — HTTP 200, ${elapsed}ms" >> "$LOG_FILE"
return 0
}
# Run checks on all configured URLs
IFS=',' read -ra URLS <<< "$MONITOR_URLS"
for url in "${URLS[@]}"; do
url=$(echo "$url" | tr -d ' ')
check_site "$url"
done
Run chmod +x ~/scripts/website-monitor.sh then bash ~/scripts/website-monitor.sh. You should see entries appear in the uptime log. Temporarily break a URL (type a wrong address) to confirm the SMS fires.
Add a cron job in your OpenClaw config to run the script every 5 minutes. The schedule */5 * * * * means "every 5 minutes, every hour, every day." The monitor will now run silently 288 times per day and only contact you when something is wrong.
# In your OpenClaw cron config (openclaw.yaml or equivalent):
crons:
- name: website-monitor
schedule: "*/5 * * * *"
command: "bash ~/scripts/website-monitor.sh"
timeout: 45s
silent: true # no output unless something fails
Here are real examples of what hits your phone:
🔴 SITE DOWN: https://yoursite.com returned HTTP 503 at 2026-03-04 03:17:42 MT
🔴 ERROR ON PAGE: https://yoursite.com/checkout contains "database error" at 2026-03-04 14:23:05 MT
🟡 SSL EXPIRING: yoursite.com cert expires in 11 days (Mar 15 00:00:00 2026 GMT)
[2026-03-04 03:22:14 MT] OK: https://yoursite.com — HTTP 200, 847ms
If your server has occasional slow responses (shared hosting, etc.), raise MONITOR_RESPONSE_TIMEOUT to 6000 or 8000 ms. The goal is catching real slowdowns, not every 4.1-second blip.
Add a file-based pause: if /tmp/monitor-paused exists, skip the run. Before a deployment, touch /tmp/monitor-paused. After, rm /tmp/monitor-paused. You won't get paged during your own planned work.
The SSL check only runs on HTTPS URLs. If you have a mix of HTTP and HTTPS in your URL list, that's expected — the script skips SSL for HTTP-only entries without error.
On some serverless or low-traffic setups, the monitoring check itself is what keeps the server warm. This is fine — it's a side effect worth having. Your real users get a faster first load because the server never went fully cold.
I've been running this monitor on askpatrick.co and the OpenClaw status page since January 2026. In that time:
The SSL expiry warning caught my cert with 11 days to spare in February. Without the monitor, I would have found out when a visitor texted me about the red warning screen.
The config above gets you the core monitor. The Library version goes further:
The Library includes the complete two-strike monitor, the Discord embed version, and 40+ other battle-tested configs. Cancel any time.