- Difficulty:
- beginner
Problem
Uptime checks that post on every failed poll turn a two hour outage into twenty four identical messages, and people mute the channel. The useful signal is the transition, not the poll.
Trigger
Cron, every five minutes.
Prerequisites
- 01A webhook URL from the chat tool.
- 02curl, present by default on macOS and most Linux images.
- 03A writable /tmp for the state file.
Steps
- 01
Create an incoming webhook in the chat tool and copy the URL slack
Any service that accepts a JSON body with a text field works the same way.
- 02
Put the webhook URL in the environment, not in the script
The script reads SLACK_WEBHOOK_URL and exits with a message if it is missing.
- 03
Set URL at the top and run it twice by hand curl
The first run records the state and stays quiet. Point it at a URL that fails to watch the alert fire.
- 04
Add the cron line cron
*/5 * * * * SLACK_WEBHOOK_URL=https://... $HOME/bin/uptime.sh
Payload · sh
#!/usr/bin/env bash
# Check one URL. Post to chat only when the state changes, so a long
# outage does not repeat the same message every five minutes.
set -euo pipefail
URL="https://example.com"
WEBHOOK="${SLACK_WEBHOOK_URL:?set SLACK_WEBHOOK_URL in the environment}"
STATE_FILE="/tmp/uptime-$(printf '%s' "$URL" | cksum | cut -d' ' -f1).state"
code="$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$URL" || echo 000)"
if [ "$code" = "200" ]; then now="up"; else now="down"; fi
was="$(cat "$STATE_FILE" 2>/dev/null || echo up)"
printf '%s' "$now" > "$STATE_FILE"
if [ "$now" = "$was" ]; then
exit 0
fi
if [ "$now" = "down" ]; then
text="$URL is down. HTTP $code."
else
text="$URL is back. HTTP $code."
fi
curl -s -X POST -H 'Content-Type: application/json' \
--data "$(printf '{"text":"%s"}' "$text")" "$WEBHOOK" > /dev/nullFailure modes
- !The checking machine loses its own network. Everything looks down and the alert cannot be sent anyway. Run this somewhere other than the machine you are watching.
- !/tmp is cleared on reboot, the state resets to up, and a still down site alerts a second time. Move STATE_FILE somewhere persistent if that matters.
- !The site returns 200 with an error page. This checks the status code only.
- !A redirect returns 301 and reads as down. Add -L to the curl call if redirects are expected.
JSON-LD · HowTo
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "Tell a chat channel when a site goes down, once",
"description": "A five minute cron check posts to a chat webhook only when a site changes state, so an outage announces itself once and the recovery announces itself once.",
"url": "https://automationsanonymous.com/automations/tell-a-chat-channel-when-a-site-goes-down",
"tool": [
{
"@type": "HowToTool",
"name": "cron",
"url": "https://automationsanonymous.com/tools/cron"
},
{
"@type": "HowToTool",
"name": "bash",
"url": "https://automationsanonymous.com/tools/bash"
},
{
"@type": "HowToTool",
"name": "curl",
"url": "https://automationsanonymous.com/tools/curl"
},
{
"@type": "HowToTool",
"name": "slack",
"url": "https://automationsanonymous.com/tools/slack"
}
],
"step": [
{
"@type": "HowToStep",
"position": 1,
"name": "Create an incoming webhook in the chat tool and copy the URL",
"text": "Any service that accepts a JSON body with a text field works the same way.",
"url": "https://automationsanonymous.com/tools/slack"
},
{
"@type": "HowToStep",
"position": 2,
"name": "Put the webhook URL in the environment, not in the script",
"text": "The script reads SLACK_WEBHOOK_URL and exits with a message if it is missing."
},
{
"@type": "HowToStep",
"position": 3,
"name": "Set URL at the top and run it twice by hand",
"text": "The first run records the state and stays quiet. Point it at a URL that fails to watch the alert fire.",
"url": "https://automationsanonymous.com/tools/curl"
},
{
"@type": "HowToStep",
"position": 4,
"name": "Add the cron line",
"text": "*/5 * * * * SLACK_WEBHOOK_URL=https://... $HOME/bin/uptime.sh",
"url": "https://automationsanonymous.com/tools/cron"
}
],
"datePublished": "2026-09-05T16:53:17.760Z"
}