cron syntax, variables and the difference to a server cronjob
Not every pipeline needs to be triggered by a push or a merge request. Nightly security scans, recurring cleanup jobs or regular dependency updates run best on a time based schedule, regardless of whether anyone actually committed code that day. GitLab's Scheduled Pipelines provide a cron mechanism built directly into the platform for exactly this. This article covers the setup, the cron syntax, working with pipeline variables and why a Scheduled Pipeline is fundamentally different from a classic cronjob on a server.
Table of Contents
- 1. What Time Based Pipelines Are Good For
- 2. Setup: Creating a Schedule in the GitLab UI
- 3. The cron Syntax in Detail and the Role of the Time Zone
- 4. Using Pipeline Schedule Variables for Different Runs
- 5. Separating Scheduled Jobs From Regular Pipelines With rules
- 6. Why a Scheduled Pipeline Is Not a Classic Server Cronjob
- 7. Use Case: Nightly Security Scans
- 8. Use Case: Recurring Maintenance Jobs
- 9. Best Practices and a Comparison to Alternatives
- 10. Summary
- 11. FAQ
1. What Time Based Pipelines Are Good For
The classic pipeline triggers in GitLab, namely push, merge request and manual trigger, cover the day to day of software development well, but they leave out an important category of tasks: everything that needs to happen regularly and independently of code changes. A nightly security scan should still run even if nobody committed anything that day. A weekly dependency update should check whether new versions are available, without anyone having to start the pipeline by hand.
This is exactly what Scheduled Pipelines are for. They are configured under CI/CD > Schedules in the project, and GitLab triggers the stored .gitlab-ci.yml as if a pipeline trigger had arrived from the outside, except CI_PIPELINE_SOURCE is set to schedule instead of push. That makes it possible to distinguish between regular commit pipelines and time based runs within the very same pipeline file, and to give them a different set of jobs, without maintaining two separate pipelines.
2. Setup: Creating a Schedule in the GitLab UI
A new schedule is created under Build > Pipeline schedules and consists at its core of four pieces of information: a description, the cron expression for the timing, the time zone, and the target branch or tag the pipeline runs against. In addition, each schedule can carry its own CI/CD variables that are only set for this particular schedule and never appear in any other pipeline. It is important that the executing user, under whose identity the pipeline runs, has sufficient permissions for every job that gets referenced.
Once created, a schedule can be triggered manually at any time via the Run now button, which is indispensable for testing the configuration. Only once a manual test run completes successfully should you trust that the cron expression will fire as expected. GitLab runs Scheduled Pipelines with a small, technically inherent delay of up to a few minutes, which should be factored in for time critical use cases.
# .gitlab-ci.yml
nightly-security-scan:
stage: test
script:
- echo "Running nightly security scan"
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
3. The cron Syntax in Detail and the Role of the Time Zone
GitLab uses the classic five field cron syntax for schedules: minute, hour, day of month, month and day of week, separated by spaces. The expression 0 2 * * * therefore means every day at two in the morning, while 0 3 * * 1-5 only fires on weekdays at three. Asterisks stand for any possible value in that field, commas separate several specific values, and hyphens define ranges such as 1-5 for Monday through Friday.
A frequently overlooked point is the time zone: it is chosen separately when the schedule is created and is independent of the time zone of the GitLab server or the runners. Anyone who forgets to explicitly set it to their own target time zone may end up puzzled why the nightly run happens in the middle of the day or shifted by several hours, especially on GitLab.com, which defaults to UTC. When clocks change between summer and winter time, the actual execution time can also shift by an hour, which is worth keeping in mind for particularly time sensitive jobs.
4. Using Pipeline Schedule Variables for Different Runs
Every schedule can carry its own CI/CD variables, which are only set during that specific time based run. That makes it possible to reuse the same job in .gitlab-ci.yml for different purposes, for example a generic scan job that either performs a quick daily scan or a comprehensive weekly scan depending on the schedule, controlled through a variable such as SCAN_DEPTH. No separate job definitions need to be maintained for each time based variant this way.
In practice it pays off to create several schedules with clearly worded descriptions, for instance Nightly Quick Scan and Weekly Deep Scan, rather than overloading a single schedule with complex internal branching logic. That makes the configuration in the GitLab UI self explanatory and makes it easy for new team members to understand at a glance which schedule is responsible for what, without having to read the pipeline definition in detail.
# .gitlab-ci.yml
dependency-scan:
stage: test
variables:
SCAN_DEPTH: "quick"
script:
- echo "Running dependency scan with depth $SCAN_DEPTH"
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
5. Separating Scheduled Jobs From Regular Pipelines With rules
The key to distinguishing a regular commit pipeline from a time based run is the predefined variable CI_PIPELINE_SOURCE. It takes the value schedule for Scheduled Pipelines, while it holds push for a regular push and merge_request_event for a merge request. A rules condition such as if: $CI_PIPELINE_SOURCE == schedule lets you enable a job specifically only for time based runs, or conversely, disable it specifically for regular commit pipelines.
Combining several rules conditions is especially useful for finely distinguishing between different trigger types: an extensive integration test that takes several minutes then runs only on Scheduled Pipelines, while every regular push only triggers a fast unit test run. This keeps the feedback time short for developers, without giving up the more thorough but more time consuming check, which then simply happens automatically at night instead.
6. Why a Scheduled Pipeline Is Not a Classic Server Cronjob
At first glance a Scheduled Pipeline looks like a simple replacement for a crontab entry on a server, but the differences are substantial. A classic cronjob runs directly on the target server, with its local environment, its installed packages and its file system, with no containerization or isolation at all. A Scheduled Pipeline, on the other hand, starts a full GitLab runner job, typically inside a fresh Docker container, including a checkout of the repository, cache restoration, and everything else a regular pipeline brings along.
That has concrete practical consequences: a Scheduled Pipeline automatically gets access to pipeline logs, artifacts, the full job history inside the GitLab UI, and can alert on failure through the same notification mechanisms as any other pipeline, for example email or a Slack integration. A classic server cronjob, by contrast, logs at best to a log file or an email to root, without the structured traceability that GitLab's pipeline history provides out of the box. In exchange, a real cronjob remains the better fit for tasks that must run directly, without container overhead, on the target system itself, such as database maintenance on that same host.
7. Use Case: Nightly Security Scans
A particularly common use case for Scheduled Pipelines is full security scanning that would take too long during the day to run on every push. While a fast SAST scan makes sense on every merge request, a comprehensive dependency or container scan with a full database update can take several minutes. A nightly schedule at two or three in the morning ensures this thoroughness still happens daily, without slowing down the developer feedback loop during the day.
It also helps to automatically send the result of such a nightly scan to a Slack channel or by email to the security team whenever new critical findings appear, instead of relying on someone checking the pipeline overview manually every day. A simple job at the end of the pipeline that triggers a webhook notification on failure closes this gap reliably and ensures critical findings do not go unnoticed for days.
# .gitlab-ci.yml
notify-on-finding:
stage: notify
script:
- |
if [ "$SCAN_RESULT" = "critical" ]; then
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Critical security finding in nightly scan"}' \
"$SLACK_WEBHOOK_URL"
fi
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
needs: ["dependency-scan"]
8. Use Case: Recurring Maintenance Jobs
Besides security scans, Scheduled Pipelines are a great fit for maintenance tasks that need to happen regularly but not on every commit. Examples include cleaning up old, no longer needed Docker images in the project's own container registry, deleting expired feature branch environments, renewing SSL certificates through Let's Encrypt, or creating and verifying regular database backups followed by a restore test in an isolated environment.
The advantage over an external cron tool is that these maintenance jobs live directly in the same repository and under the same versioning as the rest of the application code. Changes to the backup strategy or the cleanup logic go through the same merge request review process as any other code change, which considerably improves traceability and quality assurance for security relevant maintenance tasks compared to a script maintained in isolation somewhere on a server.
9. Best Practices and a Comparison to Alternatives
A solid approach to Scheduled Pipelines starts with giving every schedule a meaningful description and regularly checking whether it is still needed. Orphaned schedules whose original purpose is long obsolete waste CI minutes unnecessarily and cause confusion. It is just as important not to silently ignore failures of Scheduled Pipelines, but to set up a notification, since otherwise a nightly security scan can fail unnoticed for weeks.
Compared to alternatives such as a classic server cronjob or a Kubernetes CronJob object, GitLab offers the advantage of a unified interface, versioning the job definition in the same repository, and seamless integration with existing pipeline variables and secrets. The table below compares the three approaches by their key properties to make the decision easier for a given use case.
| Approach | Execution Location | Traceability | Typical Use |
|---|---|---|---|
| GitLab Scheduled Pipeline | GitLab runner, containerized | Full pipeline history and logs | Nightly scans, repository close maintenance |
| Server cronjob (crontab) | Directly on the target server | Only a log file or email to root | Server local tasks without container overhead |
| Kubernetes CronJob | Inside the Kubernetes cluster | kubectl logs, cluster events | Recurring jobs in cluster native setups |
| External scheduler service | Outside GitLab and the server | Depends on the specific tool | Cross system orchestration across platforms |
Mironsoft
CI/CD pipelines, zero-downtime deployments and release automation
Deployments that run without downtime and without the nail-biting?
We review existing GitLab pipelines for fragile deployment steps and missing safeguards, then build a release process with zero-downtime deployments, automated checks and a rollback you can actually trust in an emergency.
Pipeline Review
Checking an existing .gitlab-ci.yml for fragility, missing stages and security gaps.
Zero-Downtime Deployment
Building symlink releases, health checks and rollback strategies for Magento stores.
CI/CD Automation
Connecting tests, security scans and deployments into one reliable pipeline.
10. Summary
GitLab Scheduled Pipelines: Key Takeaways
Time based, not push
Scheduled Pipelines run on a cron schedule, regardless of whether anyone committed that day.
CI_PIPELINE_SOURCE
The schedule value reliably distinguishes time based runs from regular commit pipelines.
Not a server cronjob
Scheduled Pipelines run containerized inside the runner, with full log and artifact history in GitLab.
Typical use cases
Nightly security scans, registry cleanup, backup verification and certificate renewal.