Skip to content
LastPing
GUIDE · FLY.IO · SCHEDULED MACHINES · FREE

Monitor Fly.io scheduled Machines: detect failed and missed cron runs

Fly.io Machines can run on a schedule using the --schedule flag (hourly, daily, weekly or monthly) or supercronic inside a long-running container. When a Machine exits non-zero, the run is logged as failed, but no alert is sent. When Fly doesn't start a Machine for a scheduled window (capacity, config change, region issue), nothing fires at all. Add one curl ping at the end of a successful run to detect every silent failure in seconds.

Your assistant can do this for you

Monitor my Fly.io scheduled machines with LastPing

On this page
  1. Setup: two approaches, both with one ping
  2. What this catches
  3. Questions people ask

Setup: two approaches, both with one ping

Use --schedule for simple single jobs that only need an approximate hourly, daily, weekly or monthly run; use supercronic for exact times or multiple schedules in one container.

Create a heartbeat monitor in LastPing

In the console, create a monitor that matches how the Machine runs. With --schedule, use a simple interval of the same length (for example 24 hours for daily) and a grace window wide enough for Fly's approximate start time. With supercronic, use the same cron expression as your crontab. Copy the ping URL.

https://ping.lastping.dev/<your-monitor-id>

Approach A: --schedule flag on fly machine run

Wrap your command in a shell script that pings on success and sends a /fail on error. Pass it as the Machine's command.

#!/bin/sh
# entrypoint.sh — stored in your Docker image
set -e
PING_URL="https://ping.lastping.dev/${LASTPING_MONITOR_ID}"
curl -fsS -m 10 --retry 3 -o /dev/null "${PING_URL}/start"  # arm overrun detection
if python /app/backup.py; then
  curl -fsS -m 10 --retry 3 -o /dev/null "$PING_URL"
else
  curl -fsS -m 10 --retry 3 -o /dev/null "${PING_URL}/fail"
  exit 1
fi
# Deploy and schedule via flyctl.
# --schedule daily runs about once a day; Fly picks the time.
fly machine run my-registry/my-job:latest \
  --schedule daily \
  --env LASTPING_MONITOR_ID=<your-monitor-id> \
  --region ord \
  --vm-size shared-cpu-1x

Approach B: supercronic inside a long-running Machine

supercronic is a container-native cron runner with stdout/stderr logging and proper signal handling. Install it in your Dockerfile, write a crontab, and run it as the container entrypoint.

# Dockerfile — install supercronic
FROM python:3.12-slim
RUN pip install -r /app/requirements.txt
ADD https://github.com/aptible/supercronic/releases/download/v0.2.33/supercronic-linux-amd64 \
    /usr/local/bin/supercronic
RUN chmod +x /usr/local/bin/supercronic
COPY crontab /etc/crontab
CMD ["supercronic", "/etc/crontab"]
# /etc/crontab — wrap each job with the ping pattern
0 3 * * * sh -c 'python /app/backup.py \
  && curl -fsS -m 10 -o /dev/null "https://ping.lastping.dev/$LASTPING_MONITOR_ID" \
  || curl -fsS -m 10 -o /dev/null "https://ping.lastping.dev/$LASTPING_MONITOR_ID/fail"'

Set the secret in your Fly app

fly secrets set LASTPING_MONITOR_ID=<your-monitor-id> --app my-job-app

Secrets are injected as environment variables at runtime and never appear in logs or image layers.

What this catches

  • Non-zero exit: /fail fires; incident opens immediately.
  • Machine not started: scheduled window passes, no ping → incident opens on silence.
  • Hung Machine: /start arrived, success never came → overrun incident.
  • Recovery: next successful run closes the incident automatically.

On Render? See Render cron jobs. On Railway? See Railway. For the general pattern, see monitor cron jobs. For AI agent heartbeat monitoring via the MCP protocol, see the LastPing MCP server.

Questions people ask

  • How do I run a cron job on Fly.io?

    There are two main approaches: (1) The --schedule flag on fly machine run (for example fly machine run --schedule daily) starts a Machine about once per hour, day, week or month (hourly, daily, weekly or monthly; Fly picks the exact time), runs its process once, and exits. Good for simple single-purpose jobs. (2) supercronic (a cron runner designed for containers) inside a long-running Machine: run supercronic as the container entrypoint with a crontab file to manage multiple schedules inside one container. Good for exact times, multiple schedules, complex jobs, or teams already familiar with crontab syntax. Only supercronic accepts standard 5-field unix cron expressions.

  • Does Fly.io alert when a scheduled Machine fails?

    No. Fly.io does not send notifications when a scheduled Machine exits non-zero or when a scheduled run is missed. You can view exit codes and logs via fly logs and fly machine status, but there is no built-in notification system for cron failures or missed runs. Dead-man's-switch monitoring fills this gap: if no ping arrives in the expected window, an incident opens and you get alerted via email, Slack, Discord, Telegram, ntfy, Pushover, Microsoft Teams, Google Chat, or a signed webhook.

  • What timezone does Fly.io use for scheduled Machines?

    None for --schedule: it takes no time of day, only hourly, daily, weekly or monthly, and Fly picks the approximate start time. If you're using supercronic with a crontab, add TZ=UTC or set the timezone via your Dockerfile to avoid surprises.

  • Is this free?

    Yes. LastPing is free for individuals with no monitor cap. See vs Healthchecks.io and vs Cronitor.

FREE FOR INDIVIDUALS · FULLY HOSTED

Fly.io scheduled Machines fail silently. One ping changes that.

One shell wrapper in your entrypoint. First alert in under a minute, and LastPing monitors itself the same way.