Monitoring has to be more dependable than the thing it monitors. In practice that comes down to one rule: a ping must never delay your job, and never break it.
curl has no overall time limit by
default. Hung sockets are not exotic — dropped packets, a
black-hole firewall, DNS that never answers — and without a
timeout the cron job hangs until something kills it.
curl -fsS -m 10 --connect-timeout 5 --retry 3 -o /dev/null https://ping.cronalive.com/<uuid>
| Flag | What it does |
|---|---|
-m 10 | hard cap on the whole request, retries included |
--connect-timeout 5 | separate cap on establishing the connection |
--retry 3 | retry on a network error and on 5xx, doubling the pause |
--retry-connrefused | treat connection refused as retriable (it is not by default) |
-f | non-zero exit on 4xx/5xx — otherwise curl is happy with any response |
-s -S | no progress bar but keep the error text: otherwise cron emails you on every run |
For scale: a ping is a few hundred bytes. Ten seconds for the whole request is generous, while 30 or more is already comparable to the interval of a frequent job.
The temptation is obvious: append &
and never wait for the network. Do not:
curl without -m lives indefinitely. A job that runs every minute leaves you thousands of stuck processes in a day;If you do need to background it — say, a ping from an interactive script — the timeout is not optional:
# fine curl -fsS -m 10 -o /dev/null "$PING" & # not fine: this process may never exit curl "$PING" &
Monitoring being unreachable is no reason to abort a backup.
Swallow the error explicitly, especially under
set -e:
signal() { curl -fsS -m 10 --retry 3 -o /dev/null "$PING$1" || true; } The Python, Node and PHP snippets do the same with a caught exception. Our SDKs behave this way out of the box: the signal either goes out fast or is quietly dropped.
Nothing dramatic — and that is a deliberate design, not a concession. A lost ping does not need catching up:
--retry plus a sensible grace period are for.Retrying a signal in a loop inside the job is pointless: it doubles the delay and adds no information.
Ping intake lives on several domains and is deployed independently of the dashboard. The current list comes from an open endpoint:
curl -s https://app.cronalive.com/api/v1/ping-domains
The dashboard snippets already carry the fallback behind
|| — the second attempt only
happens if the first one failed:
curl -fsS -m 10 https://ping.cronalive.com/<uuid> || curl -fsS -m 10 https://ping2.cronalive.com/<uuid>
The SDKs read that list themselves. It may grow — do not hardcode it in your own scripts.
Pings are rate limited, but in a way that can never break a heartbeat client:
The important detail: a dropped ping is answered with
200 OK, not 429. That is on purpose. A client running
with -f must not read this as an
error and must not start retrying — otherwise the limit would
trigger a storm of retries exactly when the system is already
under load. Such a ping is simply not recorded and does not
change the check status.
Practical takeaway: if a job pings more than five times a second, that is almost certainly a loop in a script rather than a requirement. Monitor the run, not every iteration.
GET /api/v1/ping-domains.