I was updating my blog’s Hugo version and PaperMod theme when I realized I hadn’t touched these dependencies in months. The Hugo Docker image was 8 versions behind, and the theme had accumulated dozens of commits. Not broken, just stale.
I wanted something that would automatically check for updates and create merge requests - something I could review and merge on my own schedule, without having to remember to check.
Why Renovate on GitLab
Renovate is a bot that scans repositories for dependencies and creates MRs when updates are available. On GitHub, you can use the hosted Renovate app. On GitLab, it runs as a scheduled CI job in your own pipeline.
This self-hosted approach means no external service dependencies - Renovate runs entirely within GitLab’s CI infrastructure.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ GitLab CI │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌──────────────────────────────────┐ │
│ │ Pipeline │ │ Renovate Job │ │
│ │ Schedule │───▶│ (runs weekly) │ │
│ │ (Sunday 6am) │ │ │ │
│ └─────────────────┘ │ 1. Scan Dockerfile │ │
│ │ 2. Scan git submodules │ │
│ │ 3. Check Docker Hub for updates │ │
│ │ 4. Check GitHub for submodule │ │
│ │ 5. Create MR if update found │ │
│ └──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Merge Request │ │
│ │ "Update hugomods/hugo to 0.155" │ │
│ └──────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
My Dependencies
| Component | Location | What Renovate tracks |
|---|---|---|
| Hugo | Dockerfile | hugomods/hugo:exts-0.154.0 image tag |
| PaperMod theme | themes/PaperMod | Git submodule commit |
Step-by-Step Setup
1. Create Renovate Config
Create .gitlab/renovate.json:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{
"matchManagers": ["dockerfile"],
"matchPackageNames": ["hugomods/hugo"],
"schedule": ["on the first day of the month"]
}
],
"git-submodules": {
"enabled": true
}
}
I set Hugo to monthly because it releases frequently. Without this, I’d get MRs every week.
2. Add CI Job
Add to .gitlab-ci.yml:
stages:
- build
- renovate
renovate:
stage: renovate
image: renovate/renovate:latest
variables:
RENOVATE_PLATFORM: gitlab
RENOVATE_ENDPOINT: https://gitlab.com/api/v4
RENOVATE_REPOSITORIES: $CI_PROJECT_PATH
LOG_LEVEL: info
script:
- renovate --autodiscover=false
only:
- schedules
allow_failure: true
3. Create Project Access Token
Go to Settings > Access Tokens and create a token:
| Setting | Value |
|---|---|
| Role | Developer (not Guest) |
| Scopes | api, write_repository |
Copy the token - it starts with glpat-.
4. Add CI Variable
Go to Settings > CI/CD > Variables and add:
| Setting | Value |
|---|---|
| Key | RENOVATE_TOKEN |
| Value | Your token |
| Mask variable | Yes |
| Protect variable | No |
5. Create Pipeline Schedule
Go to Build > Pipeline schedules and create:
| Setting | Value |
|---|---|
| Description | Renovate dependency check |
| Interval | 0 6 * * 0 |
| Target branch | main |
Gotchas
Getting this working took multiple attempts. Here’s what failed:
Token Role Must Be Developer
I created the token with Guest role initially. Renovate needs Developer to create branches and MRs. The only error was “Authentication failure” - no hint about permissions.
Protected Variables Need Protected Branches
I checked “Protect variable” assuming main was protected. It wasn’t - only master was in my protected branches list. Protected variables are invisible to pipelines on unprotected branches.
Check Settings > Repository > Protected branches before deciding on this setting.
Don’t Re-define CI Variables
My first config had:
variables:
RENOVATE_TOKEN: $RENOVATE_TOKEN
This broke authentication. CI variables are already environment variables - re-defining them in the job can break expansion. Remove this line entirely.
Debugging
Set LOG_LEVEL: debug temporarily to see what Renovate is actually doing.
Result
Renovate now runs weekly and created its first MR within minutes of the first successful run - updating the Docker image in my CI pipeline from v24 to v29.
Update History
| Date | Component | From | To |
|---|---|---|---|
| 2025-12-31 | Hugo | 0.146.0 | 0.154.0 |
| 2025-12-31 | PaperMod | 5a46517 | 7d061d5 |
Conclusion
The setup took longer than expected due to authentication issues, but now it’s zero maintenance. Renovate checks for updates weekly and creates MRs that I can review and merge when convenient.