Self-hosted Renovate for Drupal — with
drush updatedbandconfig:exportbaked into every MR.
Renovate handles composer.json and composer.lock updates out of the box. Drupal does not care.
When a contrib module is updated, several side effects must be committed alongside the lockfile:
- Schema updates —
hook_update_Nandhook_post_update_NAMEimplementations modify the active configuration and sometimes the database schema. They must be run viadrush updatedband their output captured. - Configuration drift — module updates frequently ship new or altered config entities. Without a
drush config:exportafter the update,config/sync/diverges from the active config. The next deployment will either fail or silently override changes. - Scaffold files —
drupal/core-composer-scaffoldmay regenerateweb/sites/default/default.settings.php,drush.yml, or other managed files. These changes need to be committed as well.
Standard Renovate produces a MR with a correct lockfile and nothing else. Deploying it to a Drupal site without the accompanying config export is a reliability hazard.
Drunovate closes this gap.
Drunovate is a GitLab CI orchestrator built around a custom Docker image and a set of Renovate presets. It runs as a scheduled pipeline and produces MRs that are complete and deployment-ready.
Drunovate/renovate-drupal
├── docker/Dockerfile ──► ghcr.io/ekino/drunovate-runner:latest
├── scripts/*.sh (PHP 8.4 + Composer + Drush + Renovate)
├── renovate/presets/ ──► extended by each target project's renovate.json
└── .gitlab-ci.yml ──► one scheduled job per target Drupal project
│
┌────────────────┼────────────────┐
▼ ▼ ▼
project-a project-b project-c
│
│ for each updated package, Renovate runs
│ postUpgradeTasks inside a disposable container:
│
▼
┌───────────────────────────────┐
│ 1. drush site:install │
│ --existing-config │
│ 2. drush updatedb --yes │
│ 3. drush config:export --yes │
│ 4. capture scaffold changes │
└──────────────┬────────────────┘
│
▼
MR opened on the target project
├── composer.json / composer.lock
├── config/sync/*.yml
└── web/sites/default/* (if scaffold changed)
Each MR produced by Drunovate contains:
| File(s) | Why |
|---|---|
composer.json, composer.lock |
Standard Renovate output |
config/sync/*.yml |
Config export after drush updatedb |
web/sites/default/* |
Scaffold changes, if any |
| Dependency | Version | Notes |
|---|---|---|
| GitLab | 16+ | GitLab.com or self-managed |
| GitLab CI runners | — | Docker executor required |
| PHP | 8.4 | Runtime inside the Docker image |
| Composer | 2.x | Bundled in the image |
| Drush | 13 | Via Drush launcher; resolved from each project's vendor |
| Renovate | 37 | Self-hosted, bundled in the image |
| SQLite | 3.x | Disposable database for postUpgradeTasks |
Drunovate does not require a running database server or a web server. The Drupal bootstrap for
drush updatedbruns entirely on SQLite in an ephemeral CI container.
git clone https://github.com/ekino/Drunovate.git
cd DrunovateAlternatively, use the pre-built Docker image directly without cloning:
ghcr.io/ekino/drunovate-runner:latest
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>ekino/Drunovate//renovate/base-config"
]
}That is the only change required in the target project.
In your Drupal project: Settings → Access Tokens
| Field | Value |
|---|---|
| Name | renovate-bot |
| Role | Developer |
| Scopes | api, read_repository, write_repository |
Store the generated token as a masked + protected CI variable in Drunovate:
RENOVATE_TOKEN_YOUR_PROJECT = glpat-xxxxxxxxxxxx
renovate-your-project:
extends: .renovate-job
script:
- renovate your-group/your-drupal-project
variables:
RENOVATE_TOKEN: "${RENOVATE_TOKEN_YOUR_PROJECT}"
DRUPAL_ROOT: "web"
CONFIG_SYNC_DIR: "../config/sync"Trigger the pipeline from CI/CD → Pipelines → Run pipeline.
Check that:
- Renovate can read the target repository
- A Dependency Dashboard issue is created in the target project
- If updates exist, a MR is opened with
composer.lockandconfig/sync/changes if needed.
Drunovate uses GitLab Project Access Tokens (not a single shared bot PAT) so that each target project retains full control over its own access grants.
| Token | Created in | Stored in | Used for |
|---|---|---|---|
| PAT — service account | GitLab service account | Drunovate CI vars | Read Drunovate presets |
PROJECT_TOKEN_A |
projet-drupal-A | Drunovate CI vars | Read/write project A |
PROJECT_TOKEN_B |
projet-drupal-B | Drunovate CI vars | Read/write project B |
COMPOSER_AUTH |
Packagist | Drunovate CI vars | Private package access |
CI_JOB_TOKEN |
GitLab (automatic) | — | Pull/push Docker image |
Full documentation is in progress. See the README for current setup instructions.
The three presets in renovate/presets/ can be used independently or extended:
drupal-core.json— groups alldrupal/core-*packages into a single MR, enforces astabilityDayswindow, addsdrupal-corelabeldrupal-modules.json— per-module MRs, conservativestabilityDays, adds changelog body notes with Drupal.org SA linksautomerge.json— explicitly disables automerge for alldrupal/*packages regardless of semver range
docker build -f docker/Dockerfile -t drunovate-runner:local .
# Verify installed toolchain
docker run --rm drunovate-runner:local php --version
docker run --rm drunovate-runner:local composer --version
docker run --rm drunovate-runner:local renovate --version
docker run --rm drunovate-runner:local drush --versionThe CI pipeline automatically rebuilds and pushes ghcr.io/ekino/drunovate-runner:latest whenever docker/Dockerfile or scripts/** are modified.
Contributions are welcome. Please read CONTRIBUTING.md before opening a pull request.
Areas where contributions are most valuable:
- Support for PostgreSQL or MySQL as an alternative to the SQLite disposable database
- Support for Drupal multisite configurations
- Additional presets for specific distributions (Commerce, GovCMS, Lightning)
- GitHub Actions equivalent of the CI templates
MIT — see LICENSE.
Built and maintained by ekino. Battle-tested on a fleet of Drupal projects in production.
The following components are in progress and will be added in upcoming commits:
ci/— GitLab CI job templates (generic.renovate-job, scheduled pipeline)docs/— getting started guide, token setup, architecture walkthrough