Automations Anonymous
Sheet
Display
Read
Type

Sheet 04 · /automations/tell-a-chat-channel-when-a-site-goes-down

Tell a chat channel when a site goes down, once

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.

Difficulty:
beginner
Tools:
cron, bash, curl, slack

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

  1. 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.

  2. 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.

  3. 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.

  4. 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/null

Failure 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"
}