{"post":{"slug":"how-to-automate-with-cron-2026","title":"How to automate with cron in 2026: schedules, syntax, and best practices","excerpt":"Cron is still the simplest reliable scheduler on any Linux or macOS machine. This guide covers syntax, modern gotchas, and exactly when to move beyond it.","category":"code","date":"February 21, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">C</span>ron has been scheduling Unix jobs since 1975, and in 2026 it remains the lowest-friction way to run a script on a schedule. Cloud functions cost money per invocation, container orchestrators need infrastructure — cron is free and already installed. Knowing it well saves you from overengineering simple automation.\n\n## What changed in 2026\n\n- **Systemd timers are mainstream on modern Linux** — they log to journald and support dependency ordering, so they are preferred over cron on systemd systems for anything beyond a quick one-liner.\n- **macOS Sequoia kept launchd** as the native scheduler; cron works but Apple deprecated it in favour of `launchctl` for persistent daemons.\n- **Cloud cron is everywhere** — GitHub Actions scheduled workflows, Cloud Scheduler (GCP), EventBridge Scheduler (AWS), and Fly.io Machines all speak cron syntax in YAML. Understanding the syntax still matters.\n- **Container cron gotchas** are well-documented; running cron inside Docker requires explicit signal handling and PID 1 concerns.\n\n## The five-field syntax\n\n```\n# ┌── minute (0-59)\n# │  ┌── hour (0-23)\n# │  │  ┌── day of month (1-31)\n# │  │  │  ┌── month (1-12)\n# │  │  │  │  ┌── day of week (0-7, 0 and 7 = Sunday)\n# │  │  │  │  │\n# *  *  *  *  *  command\n```\n\nCommon examples:\n\n```\n# Every day at 2:30 AM\n30 2 * * * /usr/local/bin/backup.sh\n\n# Every 15 minutes\n*/15 * * * * /opt/scripts/healthcheck.sh\n\n# Monday–Friday at 9 AM\n0 9 * * 1-5 /home/deploy/reports.py\n\n# First day of every month at midnight\n0 0 1 * * /usr/local/bin/monthly_cleanup.sh\n```\n\nUse [crontab.guru](https://crontab.guru) to verify expressions before deploying.\n\n## What changed in 2026\n\n### Environment — the biggest gotcha\n\nCron runs with a minimal environment: `HOME`, `USER`, and a stripped `PATH` (`/usr/bin:/bin`). Nothing else. A command that works in your shell may silently fail in cron.\n\n```bash\n# BAD — depends on user PATH\nbackup.sh\n\n# GOOD — absolute path\n/usr/local/bin/backup.sh\n\n# GOOD — set PATH at the top of crontab\nPATH=/usr/local/bin:/usr/bin:/bin\n```\n\nAlways use absolute paths for the command **and** any files it references.\n\n## Editing the crontab\n\n```bash\ncrontab -e          # edit your crontab (opens $EDITOR)\ncrontab -l          # list current jobs\ncrontab -r          # remove all jobs (dangerous — no confirm!)\nsudo crontab -e -u deploy   # edit another user's crontab\n```\n\nSystem-wide jobs live in `/etc/cron.d/`, `/etc/cron.daily/`, etc. — files placed there run as root.\n\n## Logging and visibility\n\nCron mails output to the local user by default — which no one reads. Redirect explicitly:\n\n```bash\n# Discard all output (use only if you're sure the script is working)\n0 3 * * * /opt/bin/cleanup.sh > /dev/null 2>&1\n\n# Log to a file (append)\n0 3 * * * /opt/bin/cleanup.sh >> /var/log/cleanup.log 2>&1\n\n# Log with timestamps via systemd-cat (systemd systems)\n0 3 * * * systemd-cat -t cleanup /opt/bin/cleanup.sh\n```\n\nThe `>> logfile 2>&1` pattern is the minimum acceptable logging for any production job.\n\n## How to pick the right scheduler\n\n| Need | Tool |\n| --- | --- |\n| Simple repeating script | cron |\n| Log to journald, dependencies | systemd timer |\n| Retry on failure | systemd timer or job queue |\n| Cloud-native, no server | Cloud Scheduler / EventBridge |\n| CI pipeline schedule | GitHub Actions `schedule:` |\n| Complex DAG of jobs | Airflow, Prefect, or similar |\n| macOS background task | launchd (`launchctl`) |\n\n## Cron vs systemd timers\n\n| Feature | cron | systemd timer |\n| --- | --- | --- |\n| Syntax | five-field | calendar expressions |\n| Logging | mail / redirect | journald (automatic) |\n| Missed-run catch-up | no | yes (`Persistent=true`) |\n| Dependency ordering | no | yes (`After=`, `Requires=`) |\n| Ease of setup | one line | two files (.timer + .service) |\n| Requires systemd | no | yes |\n\nFor a new job on a modern Ubuntu/Debian server, prefer a systemd timer. For portability or a quick script, cron wins.\n\n## Common mistakes\n\n**Forgetting to redirect output.** The job fails silently and you find out days later when nothing happened.\n\n**Relative paths in scripts called by cron.** The working directory is usually `$HOME`, not where your script lives. Use `cd /path/to/project && ./script.sh` or absolute paths everywhere.\n\n**No lock file for long-running jobs.** If a job takes longer than its interval, two instances run in parallel. Guard with `flock`:\n\n```bash\n0 * * * * flock -n /tmp/myjob.lock /opt/bin/myjob.sh\n```\n\n**Editing `/etc/crontab` directly.** Use `/etc/cron.d/` instead; files there are named and easier to manage.\n\n**Not testing outside cron first.** Run your command manually in a clean shell (`env -i HOME=$HOME /usr/local/bin/script.sh`) before adding it to crontab.\n\n## What to skip\n\n- **Cron for retry logic** — if failure means re-run, use a job queue (Sidekiq, BullMQ, Celery) or systemd's `OnFailure=`.\n- **Cron inside Docker containers** — the complexity of signal handling, PID 1, and log forwarding usually means you're better off with a sidecar or an external scheduler.\n- **`@reboot` for critical daemons** — use systemd/init for proper service management with restart policies.\n\n## FAQ\n\n**How do I debug a failing cron job?**\nRedirect stderr to a log file, run the exact command manually with a stripped environment (`env -i HOME=$HOME PATH=/usr/bin:/bin the-command`), and check `/var/log/syslog` or `journalctl` for cron execution records.\n\n**Can cron run every second?**\nNo — the minimum interval is one minute. For sub-minute scheduling use a while-loop script, systemd timers with `OnUnitActiveSec`, or a proper scheduler.\n\n**What user does cron run as?**\nUser crontabs run as that user. `/etc/cron.d/` jobs specify the user in the crontab line. System directories like `cron.daily` run as root unless otherwise configured.\n\n**Does cron run if the machine is off?**\nNo. Missed jobs are skipped by default. Use `anacron` for machines that are not always on, or `Persistent=true` in a systemd timer.\n\n## Where to go next\n\n- [How to write a bash script in 2026](/blog/how-to-write-a-bash-script-2026)\n- [How to write a GitHub Action in 2026](/blog/how-to-write-a-github-action-2026)\n- [How to set up a dev environment in 2026](/blog/how-to-set-up-a-dev-environment-2026)","hue":307,"metaTitle":"How to Automate with Cron in 2026: Syntax and Best Practices","metaDescription":"Master cron in 2026 — five-field syntax, environment pitfalls, logging, and when to upgrade to a modern scheduler. Practical examples included.","keywords":["how to automate with cron 2026","cron job syntax 2026","crontab tutorial","linux cron examples","cron environment variables","cron logging best practices","schedule tasks linux 2026","cron vs systemd timer","automate scripts cron","cron best practices 2026"],"tldr":["**Cron syntax is five fields** — minute, hour, day-of-month, month, day-of-week — then the command.","**Use absolute paths everywhere** — cron's environment has no PATH, and relative paths silently fail.","**Log stdout and stderr** — redirect both or you'll never know why a job stopped running.","**Test the command manually first** — if it fails outside cron, it will fail inside cron.","**Skip:** cron for jobs that need retry logic, concurrency control, or dependency chaining — use a proper job queue instead."],"readTime":"5 min read","wordCount":1020},"related":[{"slug":"how-to-write-sql-joins-2026","title":"How to write SQL joins in 2026: INNER, LEFT, and beyond with real examples","excerpt":"SQL joins are the most important query concept for working with relational data. This guide covers every join type with practical Postgres examples and performance tips.","category":"code","date":"April 20, 2026","author":"ByteLedger Team","readTime":"6 min read","hue":314},{"slug":"how-to-write-a-bash-script-2026","title":"How to write a Bash script in 2026: the safe and practical guide","excerpt":"Bash scripting is still the fastest way to automate repetitive shell work, but most scripts are written unsafely. Here is the 2026-correct approach.","category":"code","date":"April 6, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":306},{"slug":"how-to-write-pseudocode-2026","title":"How to write pseudocode in 2026","excerpt":"Pseudocode is the fastest way to design an algorithm before you commit to a language. In 2026 it is also the clearest way to communicate intent with both humans and AI coding tools.","category":"code","date":"April 6, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":310}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"homelab-setup-2026","title":"Homelab setup in 2026: a beginner path that does not bankrupt you","excerpt":"A homelab teaches you more than any course and can replace a pile of subscriptions. Here is a sane 2026 starting path — hardware, software, and traps.","category":"guides","date":"February 21, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":145},"older":{"slug":"how-to-avoid-lifestyle-creep-2026","title":"How to avoid lifestyle creep in 2026: keep more of every raise","excerpt":"Lifestyle creep silently eats every raise before it reaches your savings. Here is how to spot it early and build a system that lets you enjoy income growth without losing ground.","category":"finance","date":"February 21, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":278}}