Cron Jobs in Jenkins: Scheduling Builds with Cron Syntax and the H Symbol

Scheduling work in Jenkins looks familiar at first glance. You open a job, tick Build periodically, and type a cron expression. But Jenkins cron is not the same as the `crontab` you run on a Linux server. It borrows the five-field layout, then adds one critical extension—the H (hash) symbol—that changes how you should write every periodic schedule. Misunderstand it, and dozens of your jobs will fire at the exact same second. Understand it, and your CI server stays smooth under load.

This guide explains how cron jobs in Jenkins actually work: the syntax fields, the H symbol and why it exists, the difference between Build periodically and Poll SCM, aliases like `@daily`, timezones, and pipeline `triggers` blocks.

Key Takeaways
• Jenkins Build periodically uses a 5-field cron syntax: minute, hour, day-of-month, month, day-of-week.
• Jenkins extends standard cron with H (hash), which spreads jobs across a time window by hashing the job name—use it instead of fixed minutes.
Build periodically triggers a build on a schedule; Poll SCM checks your repository on a schedule and only builds when there are changes.
• Aliases such as `@daily`, `@hourly`, and `@midnight` are supported shorthand.
• In Declarative Pipelines, schedules live in a `triggers { cron(‘H * * * *’) }` block.

What cron syntax does Jenkins use?

Jenkins Build periodically accepts a cron expression with five space-separated fields. Reading left to right, they are:

“` MINUTE HOUR DOM MONTH DOW │ │ │ │ │ │ │ │ │ └─ Day of week (0-7, Sunday is 0 or 7) │ │ │ └─────── Month (1-12) │ │ └─────────── Day of month (1-31) │ └──────────────── Hour (0-23) └─────────────────────── Minute (0-59) “`

Each field supports the same operators you know from Unix cron:

Operator Meaning Example
`*` Every value in the field `* * * * *` (every minute)
`,` List of values `0 9,17 * * *` (09:00 and 17:00)
`-` Range of values `0 9-17 * * *` (every hour 09:00–17:00)
`/` Step values `H/15 * * * *` (every 15 minutes)
`H` Hash (Jenkins-specific) `H * * * *` (hourly, distributed minute)
`@daily` Alias Once a day

A few quick examples:

“` H/15 * * * * # Roughly every 15 minutes H 2 * * * # Once a day, around 02:00 H 9 * * 1-5 # Once each weekday, around 09:00 H H(0-7) * * * # Once a day, sometime between midnight and 08:00 “`

Note the `H` appearing where a normal crontab would have a number. That is the part most people get wrong, so let’s look at it closely.

Why does Jenkins use the H (hash) symbol?

This is the single most important difference between cron jobs in Jenkins and a system crontab.

In a plain crontab, `0 * * * *` means “run at minute zero of every hour.” If you have fifty jobs all set to `0 * * * *`, all fifty fire at exactly :00 past the hour. That is a thundering herd: a sudden load spike where the CI controller, agents, network, and any shared services get hammered at the top of every hour while sitting idle the rest of the time.

The H symbol solves this. Instead of you picking a fixed minute, Jenkins picks one for you by hashing the job name to a stable value within the allowed range. So `H * * * *` still runs once per hour, but the actual minute is derived from the job’s name—job A might land on minute 17, job B on minute 43. The schedule is:

  • Consistent — the same job always runs at the same hashed minute, not a random one each time.
  • Distributed — different jobs spread across the window instead of stacking on one value.

Always prefer `H` over a fixed minute in periodic triggers. Writing `H * * * *` instead of `0 * * * *` is not a stylistic preference—it is load engineering. The hash spreads your jobs evenly across the hour by deriving each one’s minute from its name, so a server running dozens of scheduled jobs never sees them all detonate at `:00`. The only times you should hard-code a minute are when an external constraint genuinely requires a precise moment (for example, a downstream system that ingests at exactly 00:00). For everything else, let the hash do the smoothing.

Using H with ranges

You can constrain where the hash lands by giving `H` a range in parentheses:

“` H(0-29) * * * * # A hashed minute within the first half of every hour H H(9-16) * * 1-5 # Once on weekdays, at a hashed hour between 09:00 and 16:00 H(0-5) 0 * * * # Just after midnight, within the first 5 minutes “`

`H(0-29)` tells Jenkins: pick a stable minute for this job, but keep it between 0 and 29. This is useful when you want jobs to run only in a maintenance window yet still be distributed across it rather than all firing at its first minute.

What are the schedule aliases?

Jenkins supports convenient shorthand aliases that map to common schedules. They are equivalent to using `H` for the unspecified fields, so they are already load-friendly:

Alias Approximate meaning
`@yearly` / `@annually` Once a year (hashed)
`@monthly` Once a month (hashed)
`@weekly` Once a week (hashed)
`@daily` / `@midnight` Once a day (hashed, `@midnight` near midnight)
`@hourly` Once an hour, equivalent to `H * * * *`

For example, `@daily` is the readable way to say “run this once a day and let Jenkins choose a sensible, distributed time.”

How do timezones work in Jenkins cron?

By default, Jenkins evaluates cron schedules in the controller’s system timezone. If your server runs UTC but your team thinks in local time, schedules can land at surprising moments.

You can override the timezone on a per-trigger basis by adding a `TZ` line at the top of the schedule field:

“` TZ=Asia/Karachi H 9 * * 1-5 “`

This runs the job at a hashed time near 09:00 Karachi time, regardless of the server’s clock. Use a valid IANA timezone identifier (such as `Europe/London` or `America/New_York`). Setting the timezone explicitly removes ambiguity when teams, servers, and audiences span regions.

Build periodically vs Poll SCM: which trigger should you use?

Both triggers use the same cron syntax, but they do very different things.

Build periodically

Build periodically starts a build every time the schedule fires, unconditionally. It does not care whether anything changed. This is the right choice for time-driven work:

  • Nightly full builds or regression suites.
  • Scheduled deployments to a staging environment.
  • Periodic housekeeping jobs (cleanup, report generation, cache warming).

Poll SCM

Poll SCM uses the schedule to decide how often to check the source repository for new commits. A build only starts if Jenkins detects a change. The cron expression here controls polling frequency, not build frequency:

“` H/5 * * * * # Check the repo for changes about every 5 minutes “`

Polling is heavier than it looks—each check contacts your version control system. Where possible, prefer webhooks (push notifications from your Git host) over frequent polling, and reserve Poll SCM for environments where webhooks are not available. When you do poll, apply `H` so the checks from many jobs do not all hit your repository server at once.

How do you schedule jobs in a Jenkins pipeline?

In Declarative Pipeline, cron schedules live inside a `triggers` block. The syntax inside `cron()` is identical to Build periodically, including the H symbol:

“`groovy pipeline { agent any triggers { cron(‘H 2 * * *’) } stages { stage(‘Build’) { steps { sh ‘make build’ } } } } “`

For Poll SCM behavior in a pipeline, use `pollSCM` instead:

“`groovy triggers { pollSCM(‘H/15 * * * *’) } “`

In Scripted Pipeline, you declare the trigger with the `properties` step and `pipelineTriggers`:

“`groovy properties([ pipelineTriggers([cron(‘H H(0-7) * * *’)]) ]) “`

A common gotcha: the `triggers` block only takes effect after the pipeline has run at least once, because Jenkins needs to read the `Jenkinsfile` to learn about the schedule. Run the job manually the first time, then the cron trigger activates.

How is Jenkins cron different from the system crontab?

It is worth being explicit, because the two are easy to conflate:

  • Field count — Both use five fields, but the system `crontab` has no equivalent of `H`. A crontab minute is always a literal number, list, range, or `*`.
  • Load distribution — System cron does exactly what you write; if you put `0` for the minute, it runs at minute zero. Jenkins encourages `H` so the platform balances load across many jobs.
  • Scope — System cron triggers OS-level commands as a user on the host. Jenkins cron triggers builds and jobs inside Jenkins, with all of Jenkins’ logging, agents, and pipeline machinery around them.
  • Aliases and TZ — Jenkins’ `@daily` and inline `TZ=` syntax are Jenkins conveniences, not standard crontab features.

In practice, many teams use both: Jenkins cron for CI/CD builds and scheduled jobs, and system cron on the same server for OS-level maintenance such as log rotation or backup scripts. They live side by side without conflict.

Self-hosting Jenkins on infrastructure you control

Jenkins is a long-running server. To run it reliably—handling concurrent builds, scheduled jobs, and agents—you need root access, predictable CPU and memory, and persistent storage that you control.

DarazHost VPS and dedicated servers give you exactly that: a root-enabled environment with the resources to self-host Jenkins as your CI/CD server, run periodic builds and scheduled jobs without fighting a noisy shared host, and use system cron alongside Jenkins for OS-level tasks like backups and log rotation. With reliable infrastructure and 24/7 support, your build server stays available when your nightly jobs are supposed to fire—and the H symbol keeps them from stampeding when they do.

Common scheduling patterns

A quick reference of patterns you’ll reach for often:

“` H/15 * * * * # Roughly every 15 minutes (distributed) H * * * * # Once an hour, hashed minute (instead of 0 * * * *) H 2 * * * # Nightly around 02:00 H 9 * * 1-5 # Weekday mornings around 09:00 H 0 * * 0 # Weekly, around midnight on Sunday @daily # Once a day, Jenkins picks the time @weekly # Once a week, distributed TZ=Europe/London H 6 * * * # Daily ~06:00 London time “`

Frequently asked questions

What does H mean in a Jenkins cron expression? H is the hash symbol. Jenkins replaces it with a stable value computed from the job’s name, so the job runs at a consistent but distributed time. `H * * * *` runs once an hour at a hashed minute, preventing all jobs from firing at minute zero.

Can I use a normal crontab expression in Jenkins? Yes—standard five-field cron expressions work. But you should replace fixed minutes with `H` wherever the exact moment doesn’t matter, so Jenkins can spread load. Note that Jenkins cron has no seconds field; the smallest unit is one minute.

What is the difference between Build periodically and Poll SCM? Build periodically starts a build every time the schedule fires, regardless of changes. Poll SCM uses the schedule to check the repository and only builds when it detects new commits. Both use the same cron syntax.

Why isn’t my pipeline cron trigger running? A `triggers { cron(…) }` block in a Jenkinsfile only activates after the pipeline has run at least once, because Jenkins must read the file to learn the schedule. Run the job manually first. Also confirm the controller’s timezone or add an explicit `TZ=` line.

How do I schedule a build at an exact minute? Use a literal number, for example `0 0 * * *` for midnight exactly. Reserve this for cases with a real external constraint; otherwise prefer `H` to avoid load spikes.

About the Author

Leave a Reply