Introduction
Renovate can be hosted as a managed app, but many teams prefer to run it themselves to keep tokens in their own vault, apply stricter maintenance windows, and tune performance for large mono-repos. This guide shows how to run Renovate in a self-hosted GitLab CI pipeline with reproducible builds, clear scheduling, and fast feedback loops.
When Self-Hosting Makes Sense
- You need Renovate to respect maintenance windows or change freezes enforced via pipeline schedules.
- Credentials (registry tokens, Personal Access Tokens (PATs)) must stay in a private network segment.
- Large repos need more CPU/RAM than the shared Renovate Cloud offers.
- You want Renovate logs and metrics shipped to your own observability stack.
Reference Architecture
The setup is intentionally split into three repos to keep concerns clear:
- renovate-runner: GitLab CI pipeline with group-scoped autodiscovery jobs.
- renovate-config: Shared Renovate presets (e.g.,
default.json). - Application repos: Lightweight
renovate.jsonpointing at the shared preset.
This separation lets you update schedules, runner sizing, and shared defaults without touching app repos.
Pipeline (renovate-runner/.gitlab-ci.yml)
stages:
- renovate
.renovate_template:
stage: renovate
image: renovate/renovate:latest
variables:
RENOVATE_PLATFORM: "gitlab"
RENOVATE_AUTODISCOVER: "true"
RENOVATE_LOG_LEVEL: "debug"
RENOVATE_ONBOARDING: "false"
script:
- renovate
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule" && ($RENOVATE_TARGET_JOB == null || $RENOVATE_TARGET_JOB == "" || $RENOVATE_TARGET_JOB == $CI_JOB_NAME)'
renovate_group_platform:
extends: .renovate_template
variables:
RENOVATE_AUTODISCOVER_FILTER: "platform/**"
renovate_group_example:
extends: .renovate_template
variables:
RENOVATE_AUTODISCOVER_FILTER: "example/**"
renovate_group_backends:
extends: .renovate_template
variables:
RENOVATE_AUTODISCOVER_FILTER: "backends/**"
renovate_group_homepage:
extends: .renovate_template
variables:
RENOVATE_AUTODISCOVER_FILTER: "homepage/**"
Key points:
- Single template keeps image, platform, logging, and onboarding consistent.
- Per-group jobs scope autodiscovery via
RENOVATE_AUTODISCOVER_FILTERso scans stay fast and isolated. - Rule limits execution to scheduled pipelines and can target a single job when
RENOVATE_TARGET_JOBmatchesCI_JOB_NAME(runs all when unset).
Shared Preset (renovate-config/default.json)
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard",
":semanticCommits",
":prHourlyLimit4",
":prConcurrentLimit10"
],
"labels": ["dependencies", "renovate"],
"rebaseWhen": "behind-base-branch",
"rangeStrategy": "replace",
"separateMajorMinor": true,
"separateMinorPatch": false,
"packageRules": [
{
"matchUpdateTypes": ["patch"],
"automerge": true,
"automergeType": "pr"
},
{
"matchUpdateTypes": ["minor", "major"],
"automerge": false
}
]
}
App Repo Reference (renovate.json)
{
"extends": ["local>platform/renovate-config:default"]
}
local> tells Renovate to fetch the preset from the renovate-config repo at runtime, so changes to shared rules apply everywhere without updating app repos.
CI/CD variables
RENOVATE_TOKEN(masked): GitLab token for therenovate-botuser withapiandread_repository.RENOVATE_ENDPOINT: Override for self-managed GitLab (if not using gitlab.com). For example:https://git.yourcompany.org/api/v4RENOVATE_HOST_RULES: JSON/YAML for host-specific auth (e.g., custom registries). For example[{"matchHost": "git.yourcompany.org", "token": "BgPgKXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.01.XXXXXXXXX" } ]GITHUB_COM_TOKEN(masked): GitHub PAT for GraphQL to avoid rate limits; no special scopes needed.
Observability and Debugging
- Add
LOG_LEVEL=debugtemporarily to inspect datasource requests and onboarding logic. - If MRs are not created, check branch protection and
gitlabIgnoreApprovalsin Renovate docs. - Use
RENOVATE_DRY_RUN=fullfor safe validation before enabling schedules. - For stuck pipelines, cap runtime with GitLab job timeout and set
RENOVATE_EXIT_CODE=1on failure so you notice regressions.
