I'd separate "did it start?", "did it finish?", and "how did it finish?". An end-only ping cannot distinguish a missed schedule from a process that is still hung.
On Linux my wrapper is roughly:
#!/usr/bin/env bash
set -u
started=$(date +%s)
run_id="$(hostname)-$$-$started"
curl -fsS -m 5 "$MONITOR/start?run=$run_id" >/dev/null || true
if flock -n /run/lock/sync.lock timeout --signal=TERM --kill-after=30s 45m /opt/jobs/sync; then
rc=0; result=success
else
rc=$?; result=failure
fi
elapsed=$(($(date +%s)-started))
curl -fsS -m 10 --retry 3 --data-urlencode "run=$run_id" \
--data-urlencode "exit=$rc" --data-urlencode "seconds=$elapsed" \
"$MONITOR/$result" >/dev/null || true
exit "$rc"
The monitoring side can treat a start with no terminal event before the grace period as "hung", and no start as "scheduler or host failed". flock prevents an overrun from overlapping the next run. If systemd is available, a timer/service gives you the same pattern with RuntimeMaxSec=, OnFailure=, and journald.
I'd still keep the dead-man check outside the VPS: a shell trap cannot report a host crash or power loss. Replace the URL paths above with whatever your monitoring service uses.