Jenkins build periodically: cron syntax and the H symbol

Jenkins' Build periodically takes 5 cron fields plus H, a hash of the job name that spreads start times: H 20 * * * runs once in the 8 p.m. hour.

5 min readSume
All posts

Jenkins' Build periodically trigger, and triggers { cron('…') } in a Declarative Pipeline, take cron's five fields, MINUTE HOUR DOM MONTH DOW, plus the symbol H: a hash of the job name that picks a stable value inside a range, so scheduled jobs don't all start at once. H 20 * * * builds once a day at a fixed minute in the 8 p.m. hour; 0 20 * * * builds at exactly 8:00 p.m.

Jenkins facts come from its Pipeline Syntax and Using a Jenkinsfile docs and from the Build periodically help text in Jenkins' source; Sume facts come from Create a run, Runs and results and Authentication. All were read on 2026-09-28. Sume has no Jenkins plugin: the build makes a plain HTTPS call with curl.

What is the Jenkins cron syntax?

Five fields separated by spaces or tabs: minute (0–59), hour (0–23), day of month (1–31), month (1–12) and day of week (0–7, where 0 and 7 are Sunday). Each field takes *, ranges, steps and lists. The Build periodically field takes one schedule per line, and lines starting with # are comments. Other schedulers count fields differently; Cron expression with 6 fields compares them.

From Jenkins' Pipeline Syntax and Build periodically help text, read 2026-09-28.
GoalScheduleWhat Jenkins does
Every day in the 8 p.m. hourH 20 * * *One build at a minute hashed from the job name
Every day at exactly 8:00 p.m.0 20 * * *Every job with this line starts in the same minute
Every fifteen minutesH/15 * * * *Perhaps at :07, :22, :37 and :52
Once a day between midnight and 7:59 a.m.H H(0-7) * * *H limited to a range
Every two hours, 9:45 a.m. to 3:45 p.m., weekdays45 9-16/2 * * 1-5Fixed minutes, no hash
Once an hour@hourlyThe same as H * * * *
Once a night@midnightSome time between 12:00 a.m. and 2:59 a.m.

What does H mean in a Jenkins cron expression?

H stands for hash. Jenkins asks you to use it wherever possible so periodic jobs produce an even load: 0 0 * * * on a dozen daily jobs causes a large spike at midnight, while H H * * * still runs each job once a day, just not all at the same time. It isn't random: the value is a hash of the job name, so it stays stable for a given project.

H also takes ranges and steps. In the day-of-month field, hashes are chosen in the 1–28 range, so H/3 can leave a gap of 3 to 6 days at the end of a month. The @yearly, @monthly, @weekly, @daily, @midnight and @hourly aliases use the same hashing.

Which time zone does Build periodically use?

By default, the time zone of the Jenkins controller's JVM. To change it, start the schedule with a TZ= line and a time zone ID, such as TZ=Europe/London above H 8 * * *. The IDs you can use depend on the Java runtime Jenkins runs on.

How do I call a paid API from a scheduled build?

Sume's docs list CI secret stores among the places a key may live, so store it as a Secret text credential and bind it in the environment block with credentials('sume-api-key'). Jenkins prints **** if the value is echoed, which it says only reduces the risk of accidental exposure. Keep the sh script in single quotes so the shell expands $SUME_API_KEY: Jenkins warns that Groovy interpolation copies the secret into process arguments that tools such as ps can show. Key the request to the build's date, and keep the body the same all day.

pipeline {
  agent any
  triggers { cron('H 20 * * *') }
  environment { SUME_API_KEY = credentials('sume-api-key') }
  stages {
    stage('Start the nightly video') {
      steps {
        sh '''
          curl -sS --fail-with-body --max-time 30 --retry 3 \
            https://api.sume.com/v1/formats/acme/nightly-recap/runs \
            -H "Authorization: Bearer $SUME_API_KEY" \
            -H "Content-Type: application/json" \
            -H "Idempotency-Key: nightly-recap-$(date -u +%F)" \
            -d '{"input":{"feed_url":"https://example.com/nightly.json"},"communication":{"webhook_url":"https://example.com/hooks/sume"}}'
        '''
      }
    }
  }
}

What happens if the build retries or runs twice?

Nothing new is charged. options { retry(3) } re-runs a failed Pipeline, and a manual build later the same UTC day sends the same date key. Sume answers the same key and body with 200 and the original run, idempotency_hit: true: no second run, no second charge. Two builds at the same moment get one 409 idempotency_key_in_use, retryable after about a second. A deliberate second video on the same date needs a new key, such as one ending in -v2.

Should the build wait for the video?

It doesn't need to. The create answers immediately with a receipt and the run itself takes minutes; the communication.webhook_url in the body gets one signed POST when the run completes or fails. If the build must poll instead, double the gap between reads up to a minute and set options { timeout(time: 2, unit: 'HOURS') } above the run's deadline, which is at most 90 minutes after it was created. A Jenkins timeout aborts the build, not the run: abandoning a poll loop does not stop the run or its spend. GitLab scheduled pipeline: run a nightly AI video job shows such a loop.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume