Cron can run a Zsh script on a schedule after the script already works from the terminal. The important handoff is the executable script path, not an interactive Zsh session, because cron does not load ~/.zshrc or keep terminal-only variables before each job.

The script's shebang selects Zsh for the script body, while the crontab command line is still parsed by /bin/sh unless the crontab sets SHELL differently. Keeping the crontab command to one absolute script path plus redirection avoids quoting problems, and a literal PATH line in the crontab keeps the job from depending on interactive shell startup files.

Use the per-user crontab for jobs that should run as the current account. System cron files such as /etc/crontab and files under /etc/cron.d have an extra username field, so do not copy that format into crontab -e. The schedule follows the cron daemon's local time, and any percent signs typed directly in the crontab command field must be escaped.

Steps to schedule a Zsh script with cron:

  1. Print the account home path for the crontab entry.
    $ printf '%s\n' "$HOME"
    /home/admin

    Replace /home/admin in the examples with the home path printed on the target account.

  2. Confirm the Zsh command path.
    $ command -v zsh
    /usr/bin/zsh

    The shebang below uses /usr/bin/env so the crontab PATH must include the directory that contains zsh.

  3. Create directories for the script and report output.
    $ mkdir -p ~/bin ~/reports
  4. Create the scheduled Zsh script.
    ~/bin/report.zsh
    #!/usr/bin/env zsh
    emulate -L zsh
    setopt err_exit no_unset
     
    mkdir -p "$HOME/reports"
    print -r -- "daily report $(/usr/bin/date -u +%FT%TZ)" >> "$HOME/reports/daily-report.txt"

    emulate -L zsh starts the script with standard Zsh options for the current process, which makes the script less dependent on user startup files.

  5. Make the script executable for the file owner.
    $ chmod u+x ~/bin/report.zsh
  6. Check the script syntax.
    $ zsh -n ~/bin/report.zsh

    No output from zsh -n means Zsh parsed the script without finding a syntax error.

  7. Run the script once from the terminal.
    $ ~/bin/report.zsh
  8. Confirm the script wrote the report file.
    $ cat ~/reports/daily-report.txt
    daily report 2026-06-05T05:58:12Z
  9. Open the current user's crontab.
    $ crontab -e

    crontab -e edits the per-user crontab for the current account. Use sudo crontab -u username -e only when the job must run as a different account.

  10. Add the cron environment lines and schedule entry.
    SHELL=/bin/sh
    PATH=/usr/local/bin:/usr/bin:/bin
    MAILTO=""
    0 6 * * * /home/admin/bin/report.zsh >> /home/admin/report.log 2>&1

    Do not add a username field to a per-user crontab. The username field belongs in /etc/crontab and files under /etc/cron.d.

    If the crontab command itself contains a % character, escape it as \%. Cron treats unescaped percent signs in the command field as line breaks passed to the command.

  11. Save the file and exit the editor so crontab installs the updated schedule.

    A normal Linux cron daemon loads the saved user crontab automatically. A service restart is not usually required after crontab -e saves successfully.

  12. List the installed crontab.
    $ crontab -l
    SHELL=/bin/sh
    PATH=/usr/local/bin:/usr/bin:/bin
    MAILTO=""
    0 6 * * * /home/admin/bin/report.zsh >> /home/admin/report.log 2>&1

    This confirms that the user crontab contains the exact Zsh script path and output redirection that cron will evaluate.

  13. Run the scheduled command with a minimal cron-like environment.
    $ env -i HOME=/home/admin LOGNAME=admin PATH=/usr/local/bin:/usr/bin:/bin SHELL=/bin/sh /home/admin/bin/report.zsh

    This catches missing paths, missing directories, and startup-file assumptions before the cron daemon runs the job.

  14. After the next scheduled run time passes, check the report output again.
    $ cat /home/admin/reports/daily-report.txt
    daily report 2026-06-05T05:58:12Z
    daily report 2026-06-05T05:59:01Z
    daily report 2026-06-05T06:00:01Z

    The new timestamp confirms that cron matched the schedule, launched the script as the crontab owner, and appended another report line.