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

ComponentLocationWhat Renovate tracks
HugoDockerfilehugomods/hugo:exts-0.154.0 image tag
PaperMod themethemes/PaperModGit 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:

SettingValue
RoleDeveloper (not Guest)
Scopesapi, write_repository

Copy the token - it starts with glpat-.

4. Add CI Variable

Go to Settings > CI/CD > Variables and add:

SettingValue
KeyRENOVATE_TOKEN
ValueYour token
Mask variableYes
Protect variableNo

5. Create Pipeline Schedule

Go to Build > Pipeline schedules and create:

SettingValue
DescriptionRenovate dependency check
Interval0 6 * * 0
Target branchmain

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

DateComponentFromTo
2025-12-31Hugo0.146.00.154.0
2025-12-31PaperMod5a465177d061d5

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.