This article explains how to set up a cron expression to run every 5 minutes (*/5 * * * *), with best practices for logging, preventing overlaps, and monitoring. Using Instatus adds alerts and status page updates to catch failures before users notice. For more guides on monitoring, uptime, and incident management, visit the Instatus blog.
A B2B SaaS company once discovered its MySQL backup job had been silently failing for 89 days. The backup partition filled to capacity, the job exited with error code 1, and nothing sent an alert. The team found out when a customer noticed missing data.
That is the real problem with cron. It does not fail loudly. It just stops running, and silence is the default.
In this Instatus guide, we'll walk through the exact cron expression for every 5 minutes, when to use it, how to set it up correctly, and how to make sure you actually know when it breaks.
At Instatus, we build status pages and monitoring tools for over thousands of SaaS and DevOps teams. Working closely with teams on incident communication, we have seen firsthand how undetected scheduled task failures compound into user-facing outages with no explanation ready.
That pattern shapes everything we know about cron jobs, monitoring, and the gap between a task being scheduled and a task reliably running.
Cron is a built-in Unix job scheduler. It checks a schedule file called the crontab every minute and runs any command whose scheduled time matches the current moment. If you want to understand the broader context of what cron jobs are and the kinds of tasks they power, our beginner's guide to cron jobs covers that in detail.
A cron expression has five time fields followed by the command:
* * * * * command
│ │ │ │ │
│ │ │ │ └─ Day of week (0–7, where 0 and 7 are Sunday)
│ │ │ └─── Month (1–12)
│ │ └───── Day of month (1–31)
│ └─────── Hour (0–23)
└───────── Minute (0–59)
Cron evaluates these fields left to right. A command runs only when all fields match the current time. For example, setting minute to 15 and all other fields to * fires the job at 15 minutes past every hour.
Four special characters define schedules:
* matches any value, separates multiple values (1,15)- defines a range (1-5)/ sets a step (*/5 = every 5 minutes)The expression */5 * * * * runs every 5 minutes. Alternatives like 0-59/5 or listing all minutes manually are equivalent but less concise.
The expression itself is one line. The part that actually protects you in production is knowing when it stops running. Here's how to set up the cron job and add monitoring that sends alerts when it fails.
Create your script and make it executable before touching crontab. This example health check pings an endpoint and logs the response:
nano ~/health-check.sh
Add your command:
#!/bin/bashcurl -fsS https://yourapp.com/health >> /var/log/health.log 2>&1
The -f flag is critical, without it, curl exits successfully even on a failed HTTP response, meaning your monitoring would never catch a down endpoint.
Make it executable:
chmod +x ~/health-check.sh
The most common mistake that catches beginners is relative paths. Always use absolute paths. Cron runs with a minimal environment and does not load your shell's PATH. If your script calls php or python without a full path like /usr/bin/php, cron will not find it and will fail with no warning.
Beyond PATH, cron also strips any environment variables set in your shell profile, things like DATABASE_URL, API_KEY, or custom config values your script depends on. A script that works in the terminal but produces wrong results in cron often fails for this reason, not an exit-code failure, so nothing alerts you. Either export the variables explicitly at the top of your script, or define them directly in your crontab above the job line.
Before adding the job to crontab, set up your monitor so you have the ping URL ready to drop straight into the cron line.

In your Instatus dashboard, go to your project, then Monitors, then Add Monitor. Select Cron/Heartbeat as the monitor type. Instatus generates a unique URL for this job:
https://cron.instatus.com/your-unique-url

While you are here, configure two settings:
Calculate your grace period as normal execution time plus variance. If your job typically runs in 10 seconds but occasionally takes 30, set grace to 1 minute. Too tight creates alert fatigue. Too loose delays detection of real failures.
The degraded and down states are doing different work. Degraded means the job has not pinged on schedule yet but is still within the grace window, a signal to watch, not necessarily to act. Down means the grace window has passed with no ping, and something has genuinely stopped running.
crontab -e
Add your job with the Instatus ping appended directly to it:
*/5 * * * * /home/yourusername/health-check.sh && curl -fsS https://cron.instatus.com/your-unique-url
If you want execution time tracking as well, use the start and success signals. Send a start ping when the job begins and a success ping when it finishes. Instatus records the duration of each individual run, which is useful for spotting jobs that are taking longer than expected:
*/5 * * * * curl -fsS https://cron.instatus.com/your-unique-url/start \&& /home/yourusername/health-check.sh \&& curl -fsS https://cron.instatus.com/your-unique-url
The && chain only catches exit-code failures. If your script exits 0 but produces a bad result (a sync that ran but wrote nothing, a report that generated an empty file), the success ping still fires and Instatus has no way to know something went wrong. For those cases, call the /fail endpoint directly from inside your script. Instatus marks the monitor as down immediately and alerts fire without waiting for the next missed window:
https://cron.instatus.com/your-unique-url/fail
Instatus supports multiple alert channels. Which ones are available depends on your plan. Starter includes email, Pro adds SMS, and Business adds phone calls. Slack, Microsoft Teams, Discord, and Google Chat are available across all plans.
In your Instatus monitor's alert settings, configure at least two channels: a team-wide channel like Slack for visibility, and a direct channel (SMS or a call) for whoever is on-call. For a job running every 5 minutes, the gap between failure and alert should be as short as possible, so pick channels your team actually monitors outside business hours.

While you are in alert settings, also enable recovery alerts. Instatus can notify you when a monitor returns to health after being down, these are not on by default and need to be enabled alongside your failure alerts. A recovery alert confirms your fix worked without anyone having to check the dashboard manually.
crontab -l
Confirm the line is there, then wait 5 minutes. Check your monitor history to see whether the job ran. A successful ping shows up within seconds of execution. When the period passes with no ping, the monitor moves to degraded and your first alert fires before anything downstream breaks. That is Instatus doing the work cron cannot do on its own: turning silence into a signal.

Monitor your alert patterns for the first week. If alerts fire for transient issues that self-resolve, increase your grace period. Reliable monitoring depends on alerts you trust enough to act on immediately.
If you want to cross-check at the system level, the cron logs will show whether the job was invoked at all:
Ubuntu/Debian:
grep CRON /var/log/syslog | tail -20
CentOS/RHEL:
tail -20 /var/log/cron
systemd (Ubuntu/Debian):
journalctl -u cron -f
systemd (CentOS/RHEL):
journalctl -u crond -f
When a cron-powered service goes down, your users notice before you do. They refresh the dashboard, see nothing, and file a support ticket, or worse, quietly churn.
Connecting your Instatus monitor to a status page component closes that gap. When the monitor goes down, update the component status directly from the incident view. Users land on one authoritative page that tells them the issue is known and being addressed, which can meaningfully reduce inbound support volume on its own.
Your team gets the alert. Your users get the status page. Nobody is left guessing, and your support queue stays manageable.
Five-minute schedules work well for tasks that need to stay current without running constantly: health checks, queue length monitors, cache warmers, log rotation, and API sync jobs. They are a poor fit for heavy database queries, full backups, or any job that routinely takes more than 5 minutes to complete. If a job runs for 6 minutes and fires every 5, you end up with overlapping instances piling up.
The 5-minute interval is commonly used for monitoring and maintenance tasks. It is frequent enough that users notice issues within minutes rather than hours, but sparse enough to avoid overwhelming your server or API rate limits. For context, 288 executions per day means 8,640 runs per month. If your job writes to a database or calls an external API, verify that this volume stays well within your quotas before deploying.
Use flock to ensure only one instance runs at a time:
*/5 * * * * /usr/bin/flock -n /tmp/job.lock /path/to/script.sh
If a previous instance is still running when the next fires, flock exits the new one immediately. Note that flock is part of util-linux and is standard on Linux. macOS users will need to install it via Homebrew or use a different locking mechanism.
Do not redirect everything to /dev/null while a new job is getting established. Log output while validating behavior:
*/5 * * * * /path/to/script.sh >> /var/log/myjob.log 2>&1
You can also add MAILTO="you@example.com" at the top of your crontab to receive job output by email, which is useful for catching intermittent issues that do not surface on every run.
Cron uses the server's local timezone, not yours. Run date to check what your server considers the current time. If your server is set to UTC and you expect local time behavior, your */5 expression is firing at UTC minutes, which may not align with what you intended. On CentOS/RHEL systems, you can set CRON_TZ=America/New_York at the top of your crontab to override the schedule timezone without touching the server clock. On Ubuntu/Debian, support depends on the cron implementation installed. If CRON_TZ has no effect, manually calculate the UTC offset or switch to systemd timers for reliable timezone-aware scheduling.
Most teams miss this step. When a 5-minute cron job powers a user-facing feature such as data syncs, scheduled reports, or notification queues, its failure directly affects users. They do not know what cron is. They know their dashboard stopped updating.
When a cron-powered service degrades, a status page gives users one authoritative place to check instead of filing tickets or assuming the product is broken. It also signals you are already aware of the issue, which on its own reduces inbound support volume during an incident.
When something breaks, update the component status, post a brief note on what is happening, and let your monitoring alerts handle notifications, all from one place. If you want a structured approach to handling the communication side of these incidents, our guide on outage communication plans walks through it step by step.
# systemd systemssudo systemctl status cron# older init systemssudo service cron status
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
No output or errors to debug. You are likely discarding output. Add >> /var/log/myjob.log 2>&1 to your cron line to capture everything.
Multiple instances running at once. Your job takes longer than 5 minutes. Use flock as shown in the best practices section above.
Writing the cron expression is the straightforward part. The harder problem is knowing when a job that ran perfectly yesterday quietly stopped working overnight. Add output logging while you are getting a new job established, use flock to prevent overlapping runs, and set up a heartbeat monitor so failures surface before users notice.
And if a failing job affects your users, tell them before they have to ask. Instatus gives you a status page with uptime monitoring and multi-channel alerting built in, and you can get started for free.
*/5 work on all Linux systems?Yes. The step syntax is supported across all modern distributions including Ubuntu, Debian, CentOS, RHEL, and Alpine. It also works natively on macOS.
One minute. Cron checks its schedule every 60 seconds, so sub-minute scheduling is not natively possible. For intervals under a minute, use a loop inside a script that sleeps between iterations.
Not natively. Use a while loop with sleep 5 inside a script, then schedule that script with a standard cron expression.
Cron runs in a stripped-down shell without your user profile. Use absolute paths for every command and binary in your cron commands.
It uses the server's local timezone. Run date on your server to confirm what timezone it is operating in.
Monitor your services
Fix incidents with your team
Share your status with customers