# ============================================ # Renovate Bot - Self-hosted dependency updater # ============================================ # https://docs.renovatebot.com # Image: renovate/renovate (plain tag = minimal image, "formerly slim"; # -slim suffix was retired after v37.440.x, so we pin the plain tag) # # PILOT SCOPE (intentionally narrow): # Runs weekly (Sun 04:00 Europe/Budapest) as a CronJob and opens # dependency-update PRs against admin/homelab-manifests on Gitea. # Only the `kubernetes` and `helm-values` managers are enabled, and a # default-deny packageRule limits updates to exactly four pilot images: # - ghcr.io/thomiceli/opengist # - louislam/uptime-kuma # - f0rc3/gokapi # - docker.io/calcom/cal.com # minor/patch -> PR with Gitea native auto-merge; major -> waits for # manual approval via a checkbox on the Dependency Dashboard issue. # # Stateless & ephemeral: no Service, Ingress, or PVC. Writable /tmp is an # emptyDir (root FS is read-only); Renovate uses it for git clones + cache. # # Secrets (created manually, NOT in git) come from Secret `renovate-secrets`: # - RENOVATE_TOKEN (Gitea PAT) # - RENOVATE_GITHUB_COM_TOKEN (GitHub PAT, for release notes) # ============================================ --- apiVersion: v1 kind: ConfigMap metadata: name: renovate-config namespace: admin-system labels: app.kubernetes.io/instance: renovate app.kubernetes.io/name: renovate data: config.json: | { "platform": "gitea", "endpoint": "https://gitea.dooplex.hu/api/v1", "gitAuthor": "Renovate Bot ", "repositories": ["admin/homelab-manifests"], "onboarding": false, "requireConfig": "optional", "dependencyDashboard": true, "dependencyDashboardTitle": "Renovate Dependency Dashboard", "prHourlyLimit": 0, "prConcurrentLimit": 0, "enabledManagers": ["kubernetes", "helm-values"], "kubernetes": { "managerFilePatterns": ["/.+\\.ya?ml$/"] }, "packageRules": [ { "description": "Default-deny everything", "matchPackageNames": ["*"], "enabled": false }, { "description": "Tier 1: enable updates for low-risk leaf apps", "matchPackageNames": [ "ghcr.io/thomiceli/opengist", "louislam/uptime-kuma", "f0rc3/gokapi", "docker.io/calcom/cal.com", "advplyr/audiobookshelf", "arcadiatechnology/crafty-4", "codercom/code-server", "ghcr.io/gethomepage/homepage", "ghcr.io/headlamp-k8s/headlamp", "prom/node-exporter", "rommapp/romm", "ghcr.io/stakater/reloader", "privatebin/nginx-fpm-alpine", "flomp/wanderer-db", "flomp/wanderer-web", "registry.k8s.io/kube-state-metrics/kube-state-metrics", "ghcr.io/lukegus/termix" ], "enabled": true }, { "description": "Tier 1: automerge minor/patch after 3-day stability window", "matchPackageNames": [ "ghcr.io/thomiceli/opengist", "louislam/uptime-kuma", "f0rc3/gokapi", "docker.io/calcom/cal.com", "advplyr/audiobookshelf", "arcadiatechnology/crafty-4", "codercom/code-server", "ghcr.io/gethomepage/homepage", "ghcr.io/headlamp-k8s/headlamp", "prom/node-exporter", "rommapp/romm", "ghcr.io/stakater/reloader", "privatebin/nginx-fpm-alpine", "flomp/wanderer-db", "flomp/wanderer-web", "registry.k8s.io/kube-state-metrics/kube-state-metrics", "ghcr.io/lukegus/termix" ], "matchUpdateTypes": ["minor", "patch"], "automerge": true, "automergeType": "pr", "platformAutomerge": true, "minimumReleaseAge": "3 days" }, { "description": "Tier 1: major bumps require dashboard approval (no automerge)", "matchPackageNames": [ "ghcr.io/thomiceli/opengist", "louislam/uptime-kuma", "f0rc3/gokapi", "docker.io/calcom/cal.com", "advplyr/audiobookshelf", "arcadiatechnology/crafty-4", "codercom/code-server", "ghcr.io/gethomepage/homepage", "ghcr.io/headlamp-k8s/headlamp", "prom/node-exporter", "rommapp/romm", "ghcr.io/stakater/reloader", "privatebin/nginx-fpm-alpine", "flomp/wanderer-db", "flomp/wanderer-web", "registry.k8s.io/kube-state-metrics/kube-state-metrics", "ghcr.io/lukegus/termix" ], "matchUpdateTypes": ["major"], "automerge": false, "dependencyDashboardApproval": true } ], "labels": ["renovate"] } --- apiVersion: batch/v1 kind: CronJob metadata: name: renovate namespace: admin-system labels: app.kubernetes.io/instance: renovate app.kubernetes.io/name: renovate app.kubernetes.io/version: "43.197.0" spec: schedule: "0 4 * * 0" timeZone: "Europe/Budapest" concurrencyPolicy: Forbid successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 startingDeadlineSeconds: 600 jobTemplate: metadata: labels: app.kubernetes.io/instance: renovate app.kubernetes.io/name: renovate app.kubernetes.io/version: "43.197.0" spec: template: metadata: labels: app.kubernetes.io/instance: renovate app.kubernetes.io/name: renovate app.kubernetes.io/version: "43.197.0" annotations: # Renovate uses plain X.Y.Z semver tags (no -slim suffix anymore) match-regex.version-checker.io/renovate: '^\d+\.\d+\.\d+$' spec: enableServiceLinks: false restartPolicy: OnFailure containers: - name: renovate image: renovate/renovate:43.197.0 imagePullPolicy: IfNotPresent envFrom: - secretRef: name: renovate-secrets env: - name: TZ value: Europe/Budapest - name: LOG_LEVEL value: info - name: RENOVATE_CONFIG_FILE value: /config/config.json # Renovate needs a writable tmp for git clones + cache; # root FS is read-only so point it at the emptyDir below. - name: TMPDIR value: /tmp resources: requests: cpu: 100m memory: 256Mi limits: cpu: 2000m memory: 2Gi securityContext: runAsNonRoot: true runAsUser: 12021 runAsGroup: 0 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: config mountPath: /config readOnly: true - name: tmp mountPath: /tmp volumes: - name: config configMap: name: renovate-config - name: tmp emptyDir: sizeLimit: 2Gi