Migrate from crontab

Convert a crontab file to a SteadyCron manifest — map each entry to a job, and gain retries, alerting, and an audit log.

A crontab fires and forgets. There are no retries, no timeouts, no alert if the script fails silently, and no record of what ran or what it returned. This guide maps a typical crontab to a SteadyCron manifest and explains what you gain.

A typical crontab

# /etc/cron.d/myapp
MAILTO=""

# Weekly digest — every Monday at 09:00 Berlin time
0 9 * * 1 www-data /var/www/myapp/bin/send-digest.sh

# Nightly DB backup — every day at 02:00
0 2 * * * www-data /var/www/myapp/bin/backup.sh >> /var/log/backup.log 2>&1

# 15-minute health sweep
*/15 * * * * www-data /var/www/myapp/bin/health-check.sh

The equivalent SteadyCron manifest

namespace: myapp

channels:
  - id: team-email
    kind: email
    email: oncall@myapp.com

jobs:
  # HTTP job: SteadyCron calls the endpoint directly.
  # Retries on failure; alerts via the channel if it keeps failing.
  - id: weekly-digest
    name: Weekly digest email
    kind: http
    method: POST
    url: https://api.myapp.com/jobs/send-digest
    schedule: "0 9 * * 1"
    timezone: Europe/Berlin
    timeout: 120
    retries: 3
    channel: team-email

  # Heartbeat: the backup script pings SteadyCron after it completes.
  # SteadyCron alerts if the ping goes missing or arrives late.
  - id: nightly-backup
    name: Nightly DB backup
    kind: heartbeat
    schedule: "0 2 * * *"
    grace: 1800
    channel: team-email

  # Heartbeat: the health check script pings after each run.
  - id: health-sweep
    name: 15-minute health sweep
    kind: heartbeat
    schedule: "*/15 * * * *"
    grace: 120
    channel: team-email

Mapping rules

Crontab conceptSteadyCron equivalent
Schedule expressionschedule — same 5-field cron syntax
MAILTO="" (suppress mail)Remove the channel field (no alerts)
Timezone via TZ= env vartimezone field per job
Script that hits an endpointkind: http job
Script you control (runs on your server)kind: heartbeat — script pings SteadyCron on success
Redirect to log fileSteadyCron stores full request/response logs; no redirect needed
2>&1 (capture stderr)SteadyCron captures both status and body from HTTP responses

HTTP job vs heartbeat: which to use?

Use kind: http when your cron logic is behind an HTTP endpoint you own. SteadyCron calls it on schedule, handles retries, records the response, and alerts on failure. Your crontab entry and the shell script become unnecessary — the endpoint is the job.

Use kind: heartbeat when:

  • The script runs directly on a server (shell, Python, PHP, etc.)
  • You can’t or don’t want to expose an HTTP endpoint
  • The script already exists and you just want monitoring

For heartbeats, add a single curl call to the SteadyCron ping URL at the end of your script:

#!/bin/bash
set -euo pipefail

# ... your backup logic ...

# Signal success to SteadyCron
curl -fsS https://ping.steadycron.com/{your-token}

See Ping from any language for snippets in Python, Ruby, PHP, Node.js, and more.

What you gain

crontabSteadyCron
Alerts on failureNoYes — email, Slack, Discord, Telegram, webhook
Alerts on missed runNoYes — with configurable grace period
RetriesNoYes — configurable per job
Timeout enforcementNoYes — per job
Execution logNo (log rotation)Yes — full request/response history
Version controlledNot easilyYes — manifest lives in your repo
Per-job timezoneVia TZ= workaroundsNative timezone field
PR review for changesNoYes — steadycron plan in CI

Next steps

  1. Apply the manifest: steadycron sync manifests/myapp.yaml
  2. For heartbeat jobs: add the ping URL to your scripts
  3. Set up CI to review changes: CI/CD setup
  4. For existing dashboard jobs: Migrate to IaC