Documentation

Ping reliability

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.

Timeouts are mandatory

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 10hard cap on the whole request, retries included
--connect-timeout 5separate cap on establishing the connection
--retry 3retry on a network error and on 5xx, doubling the pause
--retry-connrefusedtreat connection refused as retriable (it is not by default)
-fnon-zero exit on 4xx/5xx — otherwise curl is happy with any response
-s -Sno 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.

Do not background a ping without a timeout

The temptation is obvious: append & and never wait for the network. Do not:

  • a backgrounded curl without -m lives indefinitely. A job that runs every minute leaves you thousands of stuck processes in a day;
  • cron considers a job finished when all of its processes are gone: a backgrounded ping can keep the entry alive;
  • the exit code of a background process is lost — you will never learn that the pings stopped arriving.

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" &

A ping must not break the job

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.

What if a signal never arrives

Nothing dramatic — and that is a deliberate design, not a concession. A lost ping does not need catching up:

  • the next run sends a fresh ping and brings the check back to up;
  • if the job genuinely did not run, the check goes down — so you never get a false "all good";
  • the only real risk is a false alert caused by a network incident on your side. That is what --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.

More than one ping domain

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.

Rate limits

Pings are rate limited, but in a way that can never break a heartbeat client:

  • ≤ 5 pings per second per check — anything above is dropped;
  • ≤ 100 requests per second per IP — same;
  • daily ping quotas come with the plan.

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.

See also