Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Cron Jobs Explained: Schedule Tasks on Linux

By Kokil Thapa | Last reviewed: September 2026

Your Laravel app sends nightly backups. Your WooCommerce store clears expired carts. Your law-firm portal emails appointment reminders. None of that should depend on someone clicking a button at 2 a.m. That is what cron jobs explained: schedule tasks on Linux is really about — turning repeatable server work into reliable, clock-driven automation. On the Ubuntu boxes I maintain for client sites, cron remains the default scheduler even as systemd timers grow in popularity. This guide walks through crontab syntax, real commands, logging, and the production mistakes that break schedules silently. If you run a Linux server administration stack for PHP apps, cron is not optional knowledge.

What Are Cron Jobs and How Does the Linux Scheduler Work?

Cron is a time-based job scheduler built into Unix-like systems. The cron daemon wakes every minute, reads system and user crontabs, and executes commands whose schedule matches the current time. You do not restart cron after each edit — it picks up changes automatically.

Two crontab sources matter in practice:

  • User crontabs — stored per account, edited with crontab -e, ideal for app-specific tasks run as www-data or a deploy user.
  • System crontabs — files under /etc/cron.d/, plus /etc/crontab and hourly/daily folders managed by run-parts.

On production servers hosting Laravel 12 or WordPress 7.1 sites, I almost always put application schedules in the deploy user's crontab. System paths stay for OS maintenance like log rotation and package updates. That separation keeps application logic out of root's table and makes rollbacks easier during ongoing server maintenance.

Linux Cron Scheduler OverviewUser Crontabcrontab -eCron Daemonchecks each minute/etc/cron.dsystem jobsMatch Time Fields Against Clockminute hour day month weekdayFork Shellrun command as userLog Outputsyslog or mail
Cron jobs explained: how the Linux scheduler reads crontabs and executes matching commands every minute

The official reference for field definitions lives in the crontab(5) manual page. Bookmark it. Guessing field order causes jobs that never fire or fire at the wrong hour.

How Do You Read and Write Crontab Syntax on Linux?

Every cron line has five time fields followed by a command. Order is fixed: minute, hour, day-of-month, month, day-of-week. Each field accepts a number, a range, a list, or an asterisk meaning "every".

FieldAllowed valuesExampleMeaning
Minute0–59*/15Every 15 minutes
Hour0–232At 02:00 server time
Day of month1–311First day of month
Month1–12*Every month
Day of week0–7 (0 and 7 = Sunday)1-5Monday through Friday

Special strings shorten common patterns. They appear at the start of a line instead of five fields:

  • @reboot — once after boot
  • @daily or @midnight — 0 0 * * *
  • @hourly — 0 * * * *
  • @weekly — 0 0 * * 0
  • @monthly — 0 0 1 * *
Crontab Line AnatomyMinute0-59Hour0-23Day1-31Month1-12Weekday0-7Shell Command/usr/bin/php /var/www/app/artisan schedule:runExample: Daily 2 AM0 2 * * * backup.shExample: Every 15 min*/15 * * * * sync.sh
Crontab syntax for cron jobs explained: five time fields followed by the command Linux executes

A common mistake is putting day-of-month and day-of-week in an OR relationship when you wanted AND. If both fields are restricted, cron fires when either matches — not when both match. For "first Monday of the month" you need a script, not a single line.

Essential crontab commands

# Edit current user's crontab
crontab -e

# List current user's crontab
crontab -l

# Remove all entries (careful in production)
crontab -r

# Edit another user's crontab (requires sudo)
sudo crontab -u www-data -e

# Verify cron service on Ubuntu
sudo systemctl status cron

For a deeper Ubuntu-focused walkthrough, see the companion post on Ubuntu cron jobs setup. The commands are the same on Debian-derived servers I use for Laravel booking applications and legal-tech portals.

How Do You Create and Test Cron Jobs on Ubuntu Step by Step?

Follow this sequence whenever you add a new scheduled task. Skipping a step is how backups silently stop for months.

  1. Write the script first. Put logic in a shell or PHP script with a shebang, execute permission, and absolute paths. Cron runs with a minimal environment — no $PATH guesswork.
  2. Run it manually as the cron user. Switch to www-data or your deploy user and execute the exact command string you plan to schedule.
  3. Add a near-term test schedule. Use */2 * * * * for a two-minute test window before switching to production timing.
  4. Capture output. Redirect stdout and stderr to a log file until you trust the job.
  5. Lock the production schedule. Replace the test line, document it in your runbook, and confirm with crontab -l.

Example: nightly MySQL dump at 01:30 server time, logging to a dedicated file:

30 1 * * * /usr/bin/mysqldump -u backup_user -p'STRONG_PASS' myapp_db \
  > /var/backups/myapp_$(date +\%Y-\%m-\%d).sql 2>> /var/log/myapp-backup.log

Note the escaped percent signs. Cron treats % specially unless you escape it or wrap the command in a shell. I hit this on almost every new project until the first failed run reminds me. For structured backup strategies, read automate database backups on Linux and rsync plus cron backup automation.

Another production-ready pattern wraps PHP artisan scheduling — one cron line replaces a dozen raw tasks inside Laravel 12 or 13:

* * * * * cd /var/www/myapp/current && /usr/bin/php artisan schedule:run >> /dev/null 2>&1

Laravel's scheduler then dispatches individual tasks defined in routes/console.php or app/Console/Kernel.php on Laravel 12. That indirection is cleaner than scattering ten crontab lines across servers. See Laravel scheduled tasks in production and replacing messy crontabs with Laravel scheduling for the full pattern.

Cron Jobs vs Systemd Timers vs Laravel Queues — Which Should You Use?

Not every recurring task belongs in cron. The right tool depends on runtime, failure handling, and overlap tolerance.

ToolBest forOverlap handlingFailure retryComplexity
CronShell scripts, backups, cache clears, single artisan callNone by default — jobs can stackManualLow
Systemd timerBoot-dependent tasks, service-aware schedulingOnUnitActiveSec optionsUnit restart policiesMedium
Laravel queue workerEmail, imports, API sync, long PHP workwithoutOverlapping() in schedulerBuilt-in retriesMedium
Supervisor + queueContinuous background processingWorker count limitsJob-level backoffHigher

On sister sites sharing a Deployer 7 pipeline — including Notary Kathmandu and related legal portals — I run one cron line for schedule:run and Supervisor for queue workers. Cron kicks the scheduler; queues handle the heavy lifting. That split is documented further in Laravel cron vs queue worker and background jobs vs cron design choices.

Pick the Right SchedulerCronshell scriptssimple schedulesSystemd Timerboot-aware tasksservice couplingLaravel Queuelong PHP jobsretries built inProduction pattern: cron triggers schedule:runQueue workers process jobs asynchronously
Cron jobs explained alongside systemd timers and Laravel queues for scheduling tasks on Linux servers

Systemd timers integrate cleanly with service units. They shine when a task must run only after networking is up or when you want journald logging by default. The Ubuntu guide on managing services with systemd covers timer units in more detail. For most PHP hosting stacks I still reach for cron first because every operator already knows it.

What Production Mistakes Break Cron Jobs on Linux Servers?

Cron fails quietly. No popup, no Slack message, no failed deploy badge. These are the failures I troubleshoot most often on live servers.

Wrong PATH and missing environment variables

Cron runs commands through /bin/sh with a stripped environment. Relative paths fail. Node, Composer, and custom binaries vanish unless you set PATH explicitly:

PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * /usr/local/bin/backup.sh

Or invoke through bash login:

0 3 * * * /bin/bash -lc '/usr/local/bin/backup.sh'

Overlapping long-running jobs

If a backup takes 90 minutes and cron fires hourly, you get two concurrent dumps competing for disk I/O. Use flock to prevent overlap:

0 * * * * flock -n /tmp/backup.lock /usr/local/bin/backup.sh

flock -n exits immediately if another instance holds the lock. That one line has saved client databases more than once.

Zero-downtime deploys change the current symlink. Crontabs hard-coded to an old release path break after rollback or deploy. Always point artisan and PHP scripts at the symlinked current directory, not a dated release folder. I've fixed broken cron paths on shared EC2 hosts where schedule:run still targeted a removed release — a classic post-deploy incident.

No logging or monitoring

Redirect output until stable, then monitor the log or use a health check. Tools like Netdata can alert when expected log lines stop appearing. See Linux server monitoring with Netdata and diagnosing high CPU on Linux when cron jobs spike resources unexpectedly.

Production Cron GotchasMissing PATHcommand not found errorsJob Overlapuse flock lockfileStale Deploy Pathpoint at current symlinkSilent Failureslog stdout and stderrFix: test as cron user + log + monitorcrontab -l audit after every deploy
Cron jobs explained: production failure modes when you schedule tasks on Linux without logging or path checks

Security matters too. Never store database passwords in world-readable scripts. Restrict log directories. Run app crons as www-data, not root, unless the task truly requires elevated privileges. For hosted stacks, pair cron with proper domain and hosting setup so timezone and mail delivery for cron output are configured correctly.

Timezone surprises

Cron uses the system timezone unless you set CRON_TZ in the crontab (Vixie cron and cronie support this). A server set to UTC while your team thinks in Nepal Time (NPT, UTC+5:45) shifts every job by five hours and forty-five minutes. Confirm with timedatectl before scheduling business-hour tasks. For BS-calendar display elsewhere in the app, keep scheduling in server time and convert in the UI — a pattern I use on portals that also expose a Nepali date converter for end users.

How Do You Secure and Audit Cron Jobs on a Shared Linux Server?

Cron access is controlled through allow and deny files. On many distributions, only root may use /etc/cron.allow and /etc/cron.deny to restrict who schedules jobs. Audit regularly:

# List all user crontabs
sudo ls -la /var/spool/cron/crontabs/

# Inspect system cron drop-ins
ls -la /etc/cron.d/

# Review recent cron executions in syslog (Ubuntu)
grep CRON /var/log/syslog | tail -20

The Debian cron package documentation at wiki.debian.org/Cron explains spool locations and permissions. Treat unexpected crontab entries as seriously as unexpected SSH keys — they are persistence mechanisms attackers use.

For WordPress 7.1 and WooCommerce 11.1 sites, wp-cron pseudo-cron triggered by page views is unreliable under low traffic. Disabling wp-cron in wp-config.php and hitting wp-cron.php from system cron is standard practice on production hosts I manage through WordPress development and maintenance. Same idea applies to Magento 2.4.x — use OS cron for the message queue consumer rather than relying on web requests.

When automation grows beyond a handful of lines, consider whether AI-assisted automation or a proper job orchestrator belongs in the roadmap. Cron stays the foundation either way. Jenkins and CI pipelines also use cron triggers — see Jenkins build triggers including cron if your deploy pipeline needs timed builds.

Key Takeaways

  • Edit user crontabs with crontab -e; use five time fields plus a command with absolute paths and escaped % characters.
  • Test every new job manually as the cron user before locking in production timing.
  • Prefer one Laravel schedule:run line over scattered raw crontab entries for PHP 8.3+ applications.
  • Prevent overlapping runs with flock and log stdout/stderr until the job proves stable.
  • Point commands at symlinked deploy paths, not dated release directories, after Deployer-style deployments.
  • Audit crontabs after server migrations and treat unknown entries as a security review item.

People Also Ask

What is the difference between cron and crontab?

Cron is the background daemon that executes scheduled commands. Crontab is the file format and command (crontab -e) used to define those schedules per user. You edit crontab; cron runs it.

How do I run a cron job every 5 minutes on Linux?

Add */5 * * * * /path/to/script.sh to your crontab. The */5 in the minute field fires at minutes 0, 5, 10, and so on. Always use the full script path.

Why is my cron job not running on Ubuntu?

Check that the cron service is active, the command works when run as the same user, paths are absolute, and output is not failing silently. Inspect /var/log/syslog for CRON entries.

Can cron run a PHP or Laravel script?

Yes. Call the PHP binary with the full path to your script or artisan command. For Laravel, schedule * * * * * php artisan schedule:run and define individual tasks inside the framework scheduler.

Schedule Tasks on Linux With Confidence

Cron jobs explained: schedule tasks on Linux is foundational DevOps knowledge, not legacy trivia. A single well-tested crontab line keeps backups running, queues draining, and legal-tech portals sending reminders while your team sleeps. Start with manual tests, add logging, use flock where jobs can overlap, and wire Laravel apps through schedule:run instead of duplicating logic in shell. When cron configuration is part of a larger hosting or application rollout, contact us for help auditing schedules on your production servers — or explore web development services if you need the application and its automation built together from the start.

Frequently Asked Questions

A command the cron daemon runs on a fixed schedule. Cron reads crontabs every minute and executes matching entries on Ubuntu without restarting after each edit.

Cron is the background daemon that executes scheduled commands. Crontab is the file format and the crontab command you use with crontab -e to define schedules per user. You edit crontab; cron runs it.

Add /5 followed by the full path to your script in crontab -e. The /5 in the minute field runs at minutes 0, 5, 10, and so on.

Order is fixed: minute, hour, day-of-month, month, day-of-week, then the command. Minutes are 0–59, hours 0–23, day-of-month 1–31, month 1–12, day-of-week 0–7 where 0 and 7 mean Sunday. Use asterisks for every value, or ranges and lists. Special strings like @daily, @hourly, and @reboot replace the five fields for common patterns. The crontab(5) manual page is the authoritative reference — guessing field order is a common reason jobs never fire or fire at the wrong hour.

Write the script first with a shebang, execute permission, and absolute paths because cron runs with a minimal environment. Run the exact command manually as the cron user, such as www-data or your deploy user. Add a near-term test schedule like /2 * before switching to production timing. Redirect stdout and stderr to a log file until the job is stable. Lock the production schedule, document it in your runbook, and confirm with crontab -l. Skipping manual verification is how backups silently stop for months.

Confirm the cron service is active with sudo systemctl status cron. Run the command as the same user cron uses — relative paths and missing PATH entries fail because cron runs through /bin/sh with a stripped environment. Use absolute paths or set PATH explicitly in the crontab. Check /var/log/syslog for CRON entries. Redirect output to a log file so failures are not silent, and verify percent signs in date commands are escaped with backslashes.

Yes. Call the PHP binary with the full path to your script or artisan command. For Laravel 12 or 13 on PHP 8.3 or higher, the cleaner production pattern is one line running php artisan schedule:run every minute from the symlinked current directory. Define individual tasks in routes/console.php or app/Console/Kernel.php on Laravel 12 instead of scattering many raw crontab lines across servers. That indirection is easier to maintain than duplicating task logic in shell.

User crontabs are stored per account and edited with crontab -e, ideal for app-specific tasks run as www-data or a deploy user. System crontabs live under /etc/cron.d/, plus /etc/crontab and hourly or daily folders managed by run-parts, typically for OS maintenance like log rotation and package updates. On production servers hosting Laravel 12 or WordPress 7.1 sites, keep application schedules in the deploy user crontab and reserve system paths for server maintenance. That separation keeps application logic out of root's table and simplifies rollbacks.

Use cron for shell scripts, backups, cache clears, and a single artisan schedule:run call — overlap handling is none by default and retries are manual. Systemd timers suit boot-dependent or service-aware scheduling with journald logging and unit restart policies. Laravel queue workers handle email, imports, API sync, and long PHP work with withoutOverlapping() in the scheduler and built-in retries. On Deployer 7 hosts I maintain, one cron line kicks schedule:run while Supervisor runs queue workers for the heavy lifting.

Cron has no built-in overlap protection — if a backup takes ninety minutes and cron fires hourly, two instances compete for disk I/O. Wrap the command with flock -n and a lock file so a second run exits immediately when another instance holds the lock. That one-line pattern has prevented competing database dumps on production servers more than once. For Laravel tasks, withoutOverlapping() in the scheduler addresses the same problem inside the framework rather than at the shell level.

Deployer-style zero-downtime deploys swap a current symlink to a new release directory. Crontabs hard-coded to a dated release folder break after rollback or deploy because that path no longer exists. Always point artisan and PHP scripts at the symlinked current directory, not a specific release folder. schedule:run still targeting a removed release after deploy is a classic post-deploy incident on shared EC2 hosts where the crontab was never updated to follow the symlink.

Cron uses the system timezone unless you set CRON_TZ in the crontab on Vixie cron or cronie. A server set to UTC while your team plans schedules in Nepal Time shifts every job by five hours and forty-five minutes. Confirm the active timezone with timedatectl before scheduling business-hour tasks. Keep scheduling in server time and convert for display in the application UI where needed — a pattern I use on portals that also expose Nepali date display for end users.

Access is controlled through /etc/cron.allow and /etc/cron.deny on many distributions, where only root may restrict who schedules jobs. Audit with sudo ls -la /var/spool/cron/crontabs/, inspect /etc/cron.d/, and grep CRON /var/log/syslog for recent executions. Treat unexpected crontab entries as seriously as unexpected SSH keys — attackers use them for persistence. Never store database passwords in world-readable scripts, restrict log directories, and run app crons as www-data, not root, unless elevated privileges are truly required.

wp-cron triggered by page views is unreliable under low traffic on WordPress 7.1 and WooCommerce 11.1 sites. Disabling wp-cron in wp-config.php and hitting wp-cron.php from system cron is standard practice on production hosts I manage. The same idea applies to Magento 2.4.x — use OS cron for the message queue consumer rather than relying on web requests. Real scheduled work should not depend on someone visiting the site at the right moment.

When both day-of-month and day-of-week fields are restricted, cron fires when either matches — not when both match. That OR relationship catches teams who expect AND logic, such as wanting the first Monday of the month. Complex calendar rules like that need a script, not a single crontab line. Bookmark the crontab(5) manual page for field definitions rather than guessing, because misread field order also produces jobs that never fire or fire at the wrong hour.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: