[READ-ONLY] Mirror of https://github.com/FoxxMD/blog. blog.foxxmd.dev
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(renovate): Add PR noise reduction

FoxxMD (Apr 20, 2026, 4:18 PM UTC) 74680d36 dcdae4c7

+163
+163
_posts/2026-04-20-scaling-renovate.md
··· 355 355 356 356 * avoid spamming Komodo Action webhook with any/all push/pr actions from our repo 357 357 * only re-deploy changed Stacks that don't include the Stacks we potentially need to make the deployments happen in the first place 358 + 359 + ## Reducing Renovate PR Noise 360 + 361 + Nick's guide [provides a good starting point](https://nickcunningh.am/blog/how-to-automate-version-updates-for-your-self-hosted-docker-containers-with-gitea-renovate-and-komodo#configure-renovate-in-the-repo) for renovate config by showing that some packages can be ignored or clamped based on version matching. However, any further config it left up to the reader. While his example is a good starting point the config as described is not sufficient to make good use of renovate bot for a non-trivial lab, like once you get past 20 stacks. 362 + 363 + ### The Problem 364 + 365 + #### No Project Context 366 + 367 + The default PR/commit title template for Renovate only tells you the *image* that is being updated and to what version. This is fine when Renovate is being used in a single-project setting where there is maybe only one `compose.yaml` stack with a few images, but in the homelab monorepo where there can be many dozens of stack files this is not helpful at a glance: 368 + 369 + ![no stack context](assets/img/renovate/nocontext.png) 370 + _What stack do each of these images belong to? What folder? Which project?_ 371 + 372 + To get this context you need to 1) open the PR page and 2) switch to the Files Changed view. This is too many steps when you just want an at-a-glance view or are reviewing many opened PRs. 373 + 374 + #### No Version Bump Context 375 + 376 + The above screenshot reveals another issue. *What kind of update is this?* Minor? Major? Patch bump? The PR details page does describe this, but that means opening every single PR. Not useful for skimming new PRs to see what needs to be triaged. 377 + 378 + #### Updates for Common Dependencies Pinned by Project 379 + 380 + Though some projects do a [good job](https://github.com/immich-app/immich/blob/f909648bce8cf181512f388072abb6d1141f8a23/docker/docker-compose.yml#L52) of pinning their dependencies to specific digests or patch versions, most do not. You'll often find projects that only specify a major version like `postgres:14` or `redis:8`. 381 + 382 + Usually, it's inferred by these projects that this version must stay the same. Even if the dependency can be updated to the next major version with breaking compatibility, the project may still depend on the version in their compose.yaml stack for some specific behavior. 383 + 384 + Additionally, many projects in the selfhosted community use the *same, known, common dependencies* in this style. Dependencies like databases, cache, and queues are common in projects found in the homelab. With the Renovate config given in Nick's guide, all of these dependencies get PRs to bump major/minor versions when we really do not want them. It adds a ton of noise and alarm fatigue to have to constantly close these. 385 + 386 + ### The Solution 387 + 388 + #### Better PR Titles and Labels 389 + 390 + First, in your repo create new labels for the different type of version updates, like this: 391 + 392 + ![repo labels](assets/img/renovate/labels.png) 393 + _Version Update type labels added to the repo for PRs/Issues_ 394 + 395 + Next, in your `renovate.json` add to the `docker-compose` object: 396 + 397 + {% raw %} 398 + ```json 399 + "prBodyNotes": [ 400 + "Updates for stacks in `{{packageFileDir}}`." 401 + ] 402 + ``` 403 + {: file='docker-compose in renovate.json'} 404 + {% endraw %} 405 + 406 + and in the `docker-compose.packageRules` list **at the beginning** at the following entry: 407 + 408 + {% raw %} 409 + ```json 410 + { 411 + "matchPackageNames": [ 412 + "/.*/" 413 + ], 414 + "addLabels": [ 415 + "{{updateType}}" 416 + ], 417 + "commitMessageExtra": "in stack {{packageFileDir}} from {{currentVersion}} to {{#if isPinDigest}}{{{newDigestShort}}}{{else}}{{#if isMajor}}{{prettyNewMajor}}{{else}}{{#if isSingleVersion}}{{prettyNewVersion}}{{else}}{{#if newValue}}{{{newValue}}}{{else}}{{{newDigestShort}}}{{/if}}{{/if}}{{/if}}{{/if}}", 418 + "enabled": true 419 + } 420 + ``` 421 + {: file='docker-compose.packageRules in renovate.json'} 422 + {% endraw %} 423 + 424 + This will modify PR titles, add labels new PRs, and include a searchable folder name in the PR body: 425 + 426 + ![PRs with labels](assets/img/renovate/context.png) 427 + _PRs specify where compose file is located, from version, and version label_ 428 + 429 + The `commitMessageExtra` property modifies our PRs titles so that they include 430 + 431 + * the folder path to the `compose.yaml` where the image is located (`in stacks stack/karakeep`) 432 + * the current version of the image immediately proceeding the proposed version update (`from 1.6.2 to 1.42.1`) 433 + 434 + and `addLabels` makes Renovate add a label with the version update type (`minor`). If you chose distinctive colors then it is now easy to see at a glance what type of version update is being proposed. 435 + 436 + #### Disable Updates for Common Dependencies 437 + 438 + To fix [PR noise from common dependencies](#updates-for-common-dependencies-pinned-by-project) we can disable updates types based on package regex patterns. Add these two entries **at the end** of your `docker-compose.packageRules` list: 439 + 440 + ```json 441 + { 442 + "description": "Common images that may have breaking changes between any non-patch versions (will only open patch PRs)", 443 + "matchPackageNames": [ 444 + "/influxdb/", 445 + "/mysql/", 446 + "/mongo/", 447 + "/elasticsearch/", 448 + "/keydb/", 449 + "/rabbitmq/", 450 + "/mariadb/", 451 + "/etcd/" 452 + ], 453 + "matchUpdateTypes": [ 454 + "major", 455 + "minor" 456 + ], 457 + "enabled": false 458 + } 459 + ``` 460 + {: file='docker-compose.packageRules in renovate.json'} 461 + 462 + ```json 463 + { 464 + "description": "Common images that may have breaking changes between major versions (will only open patch/minor PRs)", 465 + "matchPackageNames": [ 466 + "/couchdb/", 467 + "/redis/", 468 + "/valkey/", 469 + "/postgres/", 470 + "/postgis/", 471 + "/pgadmin/", 472 + "/clickhouse/", 473 + "/grafana/" 474 + ], 475 + "matchUpdateTypes": [ 476 + "major" 477 + ], 478 + "enabled": false 479 + } 480 + ``` 481 + {: file='docker-compose.packageRules in renovate.json'} 482 + 483 + This will ensure that PRs are only opened for these images if the version update is both backwards compatible and unlikely to break the main service's usage of the dependency. I assigned these by going to each depedency's website and verifying their version update policy, or defaulting to patch-only if no policy was found. 484 + 485 + > If you have specific versions of any of these you want to override per project then add another entry to `packageRules` **after** the above entries and use either [`matchPackageNames`](https://docs.renovatebot.com/configuration-options/#packagerulesmatchpackagenames) or [`matchFileNames`](https://docs.renovatebot.com/configuration-options/#packagerulesmatchfilenames) to match your specific scenario. 486 + {: .prompt-tip} 487 + 488 + #### (Optional) More Noise Reducton 489 + 490 + To further reduce PR noise you can add these other options to `renovate.json`: 491 + 492 + ##### Major Dependency Approval 493 + 494 + Make sure [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) is enabled at the top-level of your `renovate.json` 495 + 496 + Use [`dependencyDashboardApproval`](https://docs.renovatebot.com/key-concepts/dashboard/#require-approval-for-major-updates) to redirect all major version bumps to the dependency dashboard. This will make Renovate list the update in the dashboard *first*, where you can then enable it via checkbox to have Renovate create the PR for it the next time it is run. Add to `docker-compose` object: 497 + 498 + ```json 499 + "major": { 500 + "dependencyDashboardApproval": true 501 + } 502 + ``` 503 + {: file='renovate.json'} 504 + 505 + ##### Minimum Release Age 506 + 507 + Use [`minimumReleaseAge`](https://docs.renovatebot.com/configuration-options/#minimumreleaseage) to also redirect PRs to the Dependency Dashboard. Setting a time value for this option means that all *unique* version updates that would become PRs are instead added to the Dashboard if the *last* digest update for that version is newer than Time X. 508 + 509 + EX `"minimumReleaseAge": "4 day"` means if the digest for `redis:9` was created less than 4 days ago then PR will be opened and instead it will be shown on the Dashboard as a Pending Update. 510 + 511 + Add add the top-level: 512 + 513 + ```json 514 + "minimumReleaseAge": "4 day" 515 + ``` 516 + {: file='renovate.json'} 517 + 518 + ##### Alpine Preset 519 + 520 + Add [`workarounds:doNotUpgradeFromAlpineStableToEdge`](https://docs.renovatebot.com/presets-workarounds/#workaroundsdonotupgradefromalpinestabletoedge) to the `renovate.json` `extends` list to prevent PRs that try to upgrade alpine images.
assets/img/renovate/context.png

This is a binary file and will not be displayed.

assets/img/renovate/labels.png

This is a binary file and will not be displayed.

assets/img/renovate/nocontext.png

This is a binary file and will not be displayed.