GUIDE · RUBY · FREE

Monitor Ruby cron jobs and rake tasks: begin/rescue/else and one HTTP call

Wrap your entry point in Ruby's begin/rescue/else: the rescue block POSTs the exception to /fail and re-raises; the else block GETs the success URL. No gem required — Net::HTTP is in the stdlib. Absence detection handles what rescue can't: the OOM kill, the crashed interpreter, the cron entry that vanished. Free, no monitor cap.

The snippets

Create a monitor in the console (schedule = how often the script should run, plus a grace window), copy the ping URL, and wrap your entry point.

Stdlib Net::HTTP — zero dependencies

rescue reports the failure; else pings success. The raise in rescue keeps the exit code non-zero so your cron scheduler or CI system still sees the error:

require 'net/http' require 'uri' PING = ENV.fetch('LASTPING_URL', 'https://ping.lastping.dev/<your-monitor-id>') def main # your job end begin main rescue => e body = "#{e.class}: #{e.message}\n#{e.backtrace.first(10).join("\n")}" uri = URI("#{PING}/fail") req = Net::HTTP::Post.new(uri) req.body = body[0, 10_000] Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) } raise else Net::HTTP.get(URI(PING)) end

Faraday (if already in your Gemfile)

One line each for success and failure — Faraday handles connection pooling and automatic retries if you configure the retry middleware:

require 'faraday' PING = ENV.fetch('LASTPING_URL', 'https://ping.lastping.dev/<your-monitor-id>') begin main rescue => e Faraday.post("#{PING}/fail", "#{e.class}: #{e.message}"[0, 10_000]) raise else Faraday.get(PING) end

Inside a rake task

The pattern is identical inside a Rake task block. Rake propagates the re-raised exception and exits non-zero, so cron and CI systems see the failure:

require 'net/http' require 'uri' PING = ENV.fetch('LASTPING_URL', 'https://ping.lastping.dev/<your-monitor-id>') namespace :data do desc 'Nightly ETL — monitored' task :etl => :environment do begin # your ETL logic DataPipeline.run! rescue => e uri = URI("#{PING}/fail") req = Net::HTTP::Post.new(uri) req.body = "#{e.class}: #{e.message}"[0, 10_000] Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) } raise else Net::HTTP.get(URI(PING)) end end end

Optional: /start ping for overrun detection

A /start ping arms the hung-job alarm — if no success arrives within the grace window the incident opens even if no exception is raised. A shared rid pairs start with finish so each run's duration is recorded in the event trail:

require 'net/http' require 'uri' PING = ENV.fetch('LASTPING_URL', 'https://ping.lastping.dev/<your-monitor-id>') rid = Time.now.to_i.to_s Net::HTTP.get(URI("#{PING}/start?rid=#{rid}")) begin main rescue => e uri = URI("#{PING}/fail") req = Net::HTTP::Post.new(uri) req.body = "#{e.class}: #{e.message}"[0, 10_000] Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) } raise else Net::HTTP.get(URI("#{PING}?rid=#{rid}")) end

What this catches

The rescue reports what it can; the silence reports the rest.

  • A Ruby exception — the /fail ping opens an incident immediately, with the exception class, message, and backtrace stored on the ping event.
  • A crash your code never sees — OOM kill, segfault in a C extension, kill -9. No success ping arrives; the grace window raises the alert.
  • The script never ran — missing cron entry, broken bundler path, dead host. Absence → incident.
  • A hung run — with the /start ping armed, a job stuck on a database lock or a dead network socket opens an incident inside the grace window.
  • Recovery — the next successful run closes the incident and sends a recovery notice automatically.

Questions people ask

Front-loaded answers — the most important fact first.

  • Do I need a gem or SDK?

    No. Net::HTTP is in Ruby's stdlib — zero Gemfile changes. If Faraday or HTTParty is already loaded in your app, one line each works too. The ping is a plain HTTP GET (success) or POST with a text body (failure).

  • What if the Ruby process is OOM-killed or segfaults?

    A dead process sends no ping — and that missing ping is itself the alert. LastPing expects a signal on your schedule plus a grace window; silence opens an incident. The failure path requires no working code because the kernel, not your rescue block, handles the kill.

  • Does this work with Sidekiq cron jobs?

    Yes, but a middleware approach is cleaner for Sidekiq — see the Sidekiq monitoring guide. For scheduled rake tasks called from cron, this snippet is the direct path.

  • Can I use this with the LastPing MCP server?

    Yes. The LastPing MCP server lets AI agents create and inspect monitors. Your Ruby script pings the same URL regardless of how the monitor was provisioned.

  • Is this free?

    Yes. LastPing is free and fully hosted with no monitor cap and no paid tier. See how it compares to Healthchecks.io and Cronitor.

FREE · FULLY HOSTED · NO PAID TIER

Your rescue block can't catch a missing process.

Five lines now beat a quiet data gap later. First alert wired up in about a minute — and LastPing monitors itself the same way.