cronkit.org
Cron expressions, explained and converted — in your browser

Cron tools › Build / Generate

Crontab line builder

flock · logging · timeout · nice · random delay · % escaped for you

Put in a schedule and the command you would type in a terminal. The builder writes the crontab line a careful admin would — no overlapping runs, output captured, a time limit — escapes the characters cron treats specially, and explains every piece.

Safety options

Environment

Your crontab lines

What each piece does

PieceWhat it does

When it runs

How to build a crontab line

  1. Enter the schedule, or click a preset. Anything the parser accepts works, including @daily and @reboot.
  2. Type the command exactly as you would run it in a terminal — do not escape anything yourself.
  3. Tick the safety options you want. Overlap protection and a log file are on by default because they prevent the two most common cron incidents: runs piling up, and failures nobody sees.
  4. Add environment lines if the job needs them — PATH for programs outside /usr/bin, MAILTO for where output goes.
  5. Copy the lines into crontab -e (leave User empty) or into a file in /etc/cron.d/ (fill it in). Keep a newline after the last line.

Why every % becomes \%

In a crontab, % in the command part is not a percent sign. Cron turns the first unescaped % into a newline and sends everything after it to the command's standard input. So tar czf /backup/$(date +%F).tgz /srv — fine in a terminal — reaches the shell as tar czf /backup/$(date +, a syntax error, and the job fails with a confusing message in cron's mail. Written as date +\%F it works. The builder escapes every % in the command, the log path and the $((RANDOM % 300)) of the random delay; a \% you already typed is left alone.

What the options protect against

OptionAddsProtects against
Overlap lockflock -n /tmp/job.lock …A slow run still going when the next one starts, then two, then ten.
Log file>> /var/log/job.log 2>&1Errors vanishing into a mailbox nobody reads.
Time limittimeout 30m …A hung job holding the lock forever, so no later run can start.
Directorycd /srv/app && …Relative paths resolving against the user's home directory.
nicenice -n 10 …A batch job slowing down the service on the same machine.
Random delaysleep $((RANDOM % 300)) && …Every server in a fleet hitting the same backend in the same second. For a fixed per-host offset instead, use the stagger generator.
PATHPATH=/usr/local/bin:…"command not found" because cron's default PATH is minimal.

Before installing a whole file, run it through the crontab checker: it catches unescaped %, a missing final newline and jobs scheduled in the hour that daylight saving time skips.

FAQ

Do I need the user field?

Only in the system crontab (/etc/crontab) and files in /etc/cron.d/, which have a sixth column naming the user to run as. A personal crontab edited with crontab -e has no user field — putting one there makes cron treat the name as the command.

Why did it add SHELL=/bin/bash?

The random delay uses $RANDOM, which is a bash feature; cron runs commands with /bin/sh unless the crontab sets SHELL. The builder also warns when your own command uses bash-only syntax such as [[ … ]], arrays or &> without bash selected.

Does CRON_TZ work on Ubuntu?

No. CRON_TZ is a cronie feature (Fedora, RHEL, Arch). Debian and Ubuntu cron have no per-user time zones; there, TZ only changes the time zone the command sees, not when it runs.

Why wrap the command in sh -c when a lock or time limit is on?

flock, timeout and nice run one program. If your command has a pipe or &&, only the part before it would be locked or timed. Wrapping it in sh -c '…' (or bash -c) makes the whole command one program.