[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 registry caching

FoxxMD (Apr 20, 2026, 4:46 PM UTC) a5cdecf6 74680d36

+82
+82
_posts/2026-04-20-scaling-renovate.md
··· 518 518 ##### Alpine Preset 519 519 520 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. 521 + 522 + ## (Optional) Reducing Registry API Calls with Caching 523 + 524 + If you have a *very large* homelab, say 50+ stacks or 70+ images total, you may want to consider adding a caching layer between Renovate and upstream registries. 525 + 526 + Dockerhub already has pretty restrictive rate limiting and quotas per day. Using a caching layer, especially if you end up pinning many images to digests, can help speed up Renovate's duration and drastically reduce calls to the registry and avoid rate limiting. This is especially useful when first creating your renovate config as you may be invoking Renovate many times to observe created PRs and iterating on your config. 527 + 528 + > If you don't want to go to the trouble of caching during initial renovate config iteration/setup then I would suggest creating a *testing* repository to have Renovate run on. Include only a few stacks with all the image update scenarios you want to detect and use that to iterate on config building, rather than using your entire homelab monorepo as the testing grounds. 529 + {: .prompt-tip} 530 + 531 + When Renovate is fetching updates it is making plain HTTP/S calls to the upstream registries *and not* using the Docker Daemon API. Therefore, we can't use existing docker registry proxies transparently. 532 + 533 + Instead, we will use [CNCF's `distribution`](https://distribution.github.io/distribution/) image as a [pull through cache](https://distribution.github.io/distribution/recipes/mirror) in order to cache image manifest information from [each upstream registry](https://distribution.github.io/distribution/recipes/mirror/#gotcha) we want to cache for. 534 + 535 + In this example I am using `distribution` as a cache for Dockerhub and setting up the container behind Traefik as the reverse proxy. In a docker compose stack: 536 + 537 + ```yaml 538 + services: 539 + dockerio-distribution-mirror: 540 + image: distribution/distribution:latest 541 + networks: 542 + - traefik_overlay 543 + - default 544 + volumes: 545 + # file store for cached data 546 + - ./distribution-proxy/registries/dockerio:/var/lib/registry 547 + labels: 548 + traefik.enable: true 549 + # URL to be used for dockerhub registry mirror 550 + traefik.http.routers.distribution-docker.rule: Host(`registry-docker.example.com`) 551 + traefik.http.services.distribution-docker.loadbalancer.server.port: 5000 552 + traefik.docker.network: internal_overlay 553 + environment: 554 + REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io # the upstream registry to cache 555 + REGISTRY_PROXY_USERNAME: foxxmd 556 + REGISTRY_PROXY_PASSWORD: ${DOCKERHUB_PASSWORD} 557 + REGISTRY_PROXY_TTL: 12h # how long to cache manifests for 558 + REGISTRY_TAGS_MAXTAGS: 10000 # REQUIRED for use with renovatebot 559 + 560 + # valkey service and config below is optional 561 + REGISTRY_REDIS_ADDRS: "[distribution-valkey:6379]" 562 + REGISTRY_STORAGE_DELETE_ENABLED: true 563 + REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: redis 564 + distribution-valkey: 565 + image: valkey/valkey:9.0.3 566 + networks: 567 + - default 568 + volumes: 569 + - ./distribution-proxy/redis:/data 570 + ``` 571 + 572 + Next, in `renovate.json` we add [`registryAliases`](https://docs.renovatebot.com/configuration-options/#registryaliases) to `docker-compose` to tell Renovate that when it sees the `docker.io` registry in an image it should instead use our mirror: 573 + 574 + ```json 575 + "registryAliases": { 576 + "index.docker.io": "registry-docker.example.com", 577 + "docker.io": "registry-docker.example.com" 578 + } 579 + ``` 580 + {: file='docker-compose in renovate.json'} 581 + 582 + EX: For `image: docker.io/library/redis:7` it should instead use `registry-docker.example.com/library.redis:7` 583 + 584 + Finally, we give an explicit hint to Renovate what the default registry is by configuring [`registryUrls`](https://docs.renovatebot.com/configuration-options/#registryurls). Without this config Renovate will still try to use `docker.io/...` when no registry prefix is present. The `registryAliases` config above only tells it what to do when the prefix is *explicitly* in the image. 585 + 586 + Add to **the beginning** of `packageRules`: 587 + 588 + ```json 589 + { 590 + "matchDatasources": ["docker"], 591 + "registryUrls": [ 592 + "https://registry-docker.example.com" 593 + ] 594 + } 595 + ``` 596 + {: file='docker-compose.packageRules in renovate.json'} 597 + 598 + > To cache for more registries you will need to: 599 + > 600 + > * Add an additional compose service for each registry 601 + > * Add a new mapping to `registryAliases` 602 + {: .prompt-tip}