Linux & Shell guide · Last updated

How to Run a Cron Job Every 5 Minutes

The cron expression for every 5 minutes is */5 * * * *. Add a line such as */5 * * * * /home/deploy/backup.sh with crontab -e and cron runs the script at :00, :05, :10 and so on, around the clock. Getting the schedule right is the easy part. Most cron jobs that "don't run" actually do run but fail because cron uses a minimal environment with a different PATH and no output to a terminal. This guide explains the five-field syntax, gives copy-ready schedules for common intervals, covers the environment and logging pitfalls, and shows the two most common alternatives: systemd timers and GitHub Actions schedules. To build or check any expression interactively, use our cron expression generator.

Quick answer

Open your user's crontab, add the line, save and exit. There is nothing to restart: cron picks up the change automatically.

bash
crontab -e
text
# m    h  dom mon dow  command
*/5    *  *   *   *    /home/deploy/scripts/sync.sh >> /home/deploy/logs/sync.log 2>&1

Tip

Not sure your expression means what you think? Paste it into the cron expression generator to see it in plain English with the next run times, or build one by picking minutes, hours and days.

How the five cron fields work

Each crontab line has five time fields followed by the command: minute (0–59), hour (0–23), day of month (1–31), month (1–12) and day of week (0–7, where both 0 and 7 are Sunday). An asterisk means "every value". A slash sets a step, so */5 in the minute field means every fifth minute starting from 0. A comma lists values (0,30), and a hyphen gives a range (9-17).

Steps are aligned to the clock, not to when you saved the crontab. */5 fires at minutes 0, 5, 10 … 55 of every hour. If you want an offset, for example to avoid every job on the server starting at the same moment, use a range with a step: 2-59/5 runs at :02, :07, :12 and so on. Note that */7 does not run every seven minutes across hour boundaries; it runs at 0, 7 … 56 and then again at 0, a gap of four minutes.

Note

When both day of month and day of week are restricted (neither is *), standard cron runs the job when either one matches, not both. 0 9 1 * 1 runs on the 1st of the month and on every Monday.

Common cron schedules

Copy the expression and put your command after it. Many cron implementations also accept the shortcuts @hourly, @daily, @weekly, @monthly and @reboot.

Read each expression from left to right: minute, hour, day of month, month, day of week. Restricting the hour and weekday fields is how you confine a frequent job to working hours, as in the business-hours example below, which runs 108 times a weekday and never at weekends. If you need an interval that does not divide evenly into 60, such as every 7 or 45 minutes, cron cannot express it exactly; a systemd timer with OnUnitActiveSec= can.

text
* * * * *          every minute
*/5 * * * *        every 5 minutes
*/10 * * * *       every 10 minutes
*/15 * * * *       every 15 minutes
*/30 * * * *       every 30 minutes (same as 0,30 * * * *)
0 * * * *          every hour, on the hour
0 */2 * * *        every 2 hours
0 0 * * *          every day at midnight
30 9 * * *         every day at 09:30
0 9 * * 1-5        weekdays (Mon-Fri) at 09:00
*/5 9-17 * * 1-5   every 5 minutes, 09:00-17:55, weekdays
0 0 * * 0          every Sunday at midnight
0 0 1 * *          first day of every month at midnight
0 3 * * 6          every Saturday at 03:00
@reboot            once at system startup
Times use the server's time zone (check with timedatectl or date).

Managing your crontab

Every user has a personal crontab, edited with crontab -e, which opens it in your $EDITOR and validates the syntax on save. crontab -l prints it, which is also an easy backup: crontab -l > crontab.bak. Jobs run as the user who owns the crontab, so put root-only tasks in root's crontab with sudo crontab -e rather than calling sudo inside a job.

System-wide jobs live in /etc/crontab and files under /etc/cron.d/. Those use a sixth field, the user name, between the schedule and the command.

bash
crontab -l                    # list
crontab -l > crontab.bak      # back up
crontab -e                    # edit
sudo crontab -u www-data -l   # another user's crontab
text
# /etc/cron.d/cleanup  (note the user field)
*/5 * * * * root /usr/local/bin/cleanup.sh

Warning

crontab -r deletes your entire crontab immediately, with no confirmation on most systems, and the r key sits right next to e. Keep a backup from crontab -l.

Environment and PATH pitfalls

Cron does not run your login shell. Jobs start with /bin/sh, a minimal PATH (often just /usr/bin:/bin), and none of the variables from .bashrc, .profile, nvm, pyenv or your virtualenv. A script that works perfectly in your terminal can fail in cron with "command not found". The fixes:

  • Use absolute paths for every program and file: /usr/bin/python3, /usr/local/bin/node, /home/deploy/app/.venv/bin/python. Find them with which.
  • Or set PATH (and SHELL if you need bash) at the top of the crontab.
  • cd into the project directory first, because the working directory is your home directory, not the script's folder.
  • Escape percent signs as \%. In a crontab line an unescaped % ends the command and turns the rest into standard input, which breaks date +%F.
  • Load secrets explicitly, for example by sourcing an env file inside the script, since nothing from your interactive session is available.
text
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""

*/5 * * * * cd /home/deploy/app && .venv/bin/python manage.py sync >> logs/sync.log 2>&1
0 2 * * *   /usr/bin/tar czf /backups/site-$(date +\%F).tgz /var/www/site

Logging output and debugging

By default cron tries to email a job's output to the crontab owner, which on most servers goes nowhere. Redirect both standard output and errors to a log file with >> file 2>&1 so you can see what happened. To confirm that cron started the job at all, check the system log.

If a job runs every 5 minutes but sometimes takes longer than 5 minutes, runs will overlap. Wrap it in flock -n so a new run is skipped while the previous one still holds the lock.

bash
*/5 * * * * /home/deploy/sync.sh >> /home/deploy/logs/sync.log 2>&1

# Did cron start it?
grep CRON /var/log/syslog          # Debian / Ubuntu
journalctl -u cron --since "1 hour ago"    # Debian / Ubuntu with systemd
journalctl -u crond --since today          # RHEL / Fedora / Rocky

# Prevent overlapping runs
*/5 * * * * /usr/bin/flock -n /tmp/sync.lock /home/deploy/sync.sh >> /home/deploy/logs/sync.log 2>&1

Alternative: systemd timers

On modern Linux servers, systemd timers are a robust alternative to cron. They log to the journal automatically, will not start a second copy while the service is still running, can catch up on runs missed while the machine was off (Persistent=true), and can add random delay to spread load. The cost is two small files instead of one line. OnCalendar=*:0/5 means every 5 minutes, and systemd-analyze calendar checks an expression before you use it.

ini
# /etc/systemd/system/sync.service
[Unit]
Description=Sync job

[Service]
Type=oneshot
User=deploy
ExecStart=/home/deploy/sync.sh
ini
# /etc/systemd/system/sync.timer
[Unit]
Description=Run sync every 5 minutes

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target
bash
systemd-analyze calendar "*:0/5"
sudo systemctl daemon-reload
sudo systemctl enable --now sync.timer
systemctl list-timers
journalctl -u sync.service

Alternative: GitHub Actions schedule

GitHub Actions workflows can run on a cron schedule with the same five-field syntax, which is handy for jobs that do not need a server. There are important differences from system cron: schedules always use UTC, the shortest interval GitHub supports is once every 5 minutes, scheduled runs use the workflow file on the default branch, and runs can start late or occasionally be skipped when GitHub is under heavy load, especially at the top of the hour. In public repositories, scheduled workflows are disabled automatically after 60 days without repository activity. Add workflow_dispatch so you can also trigger the job manually.

yaml
# .github/workflows/sync.yml
name: Sync every 5 minutes
on:
  schedule:
    - cron: "*/5 * * * *"   # UTC
  workflow_dispatch:

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/sync.sh

Tip

Because GitHub schedules can slip by several minutes, use an off-peak minute such as 7,37 * * * * for jobs that need to run reliably, and keep time-critical work on a server cron or systemd timer.

Cron every 5 minutes: FAQ

Quick answers to common questions

What is the cron expression for every 5 minutes?

*/5 * * * *. It runs at minutes 0, 5, 10 and so on through 55 of every hour, every day. Check any expression in the cron expression generator.

Why is my cron job not running?

Most often it runs but fails because of cron's minimal environment: use absolute paths, set PATH in the crontab, cd into the right directory, escape % signs and redirect output to a log file to see the error.

Can cron run a job every 5 seconds?

No. Cron's smallest unit is one minute. For sub-minute intervals use a systemd timer with OnUnitActiveSec=5s, a loop with sleep inside a long-running service, or a job scheduler in your application.

What time zone does cron use?

System cron uses the server's local time zone, so check it with timedatectl. GitHub Actions schedules always use UTC.

What is the difference between */5 and 0/5 in cron?

In standard Linux cron (Vixie cron, cronie) both mean every fifth minute starting at 0. */5 is the portable form; Quartz and some other schedulers use a six- or seven-field syntax where 0/5 is the norm.

Related tools and references

More developer guides