Compatible where it protects users
Most opinions in this series are departures from Renovate. This one is the opposite. In three places pinup deliberately does not deviate, even where I would have built it differently myself.
Configuration, branch names and annotations follow Renovate. Not out of convenience, but because they live in the users' repositories and not in the tool.
01 — The opinion in one sentence
What is in the repository belongs to its users. Switching tools must not change a byte there.
That concerns three things:
- The configuration. pinup's own file is called
.pinup.yamlor.pinup.jsonc(also.ymlor.json). pinup reads an existingrenovate.json,renovate.json5,.renovatercor.renovaterc.jsonin the repository root just the same, with the same rules: objects are merged, lists replaced,packageRulesconcatenated,nullclears. Two files at once pinup refuses instead of silently picking one. - Branch names and titles. Without a setting of their own, branches start with
renovate/and are named as Renovate names them. pinup adopts open branches instead of duplicating them. - Annotations. A comment such as
# renovate: datasource=docker depName=…above a version line works unchanged.# pinup:works too.
02 — The alternative: a clean break
Many tools start with a format of their own. Dependabot has its dependabot.yml, Renovate its renovate.json. That is a good path for a new tool. It can make its language simpler and carries no legacy.
For pinup a clean break would have meant two things. First, a change in around 200 repositories before the first run shows anything at all. Second, no way back. Whoever tries a new tool should be able to switch it off again without cleaning up.
03 — The price
Compatibility is the most expensive opinion in this series.
Someone else's rules, the odd ones included. pinup has to reproduce Renovate's semantics, down to the presets. Where I would have phrased a rule differently, it still stays the way Renovate reads it.
A rebuild without looking at the code. Renovate is AGPL-3.0, pinup Apache-2.0. I adopt the configuration language but read and copy no code. So I ran the pinned Renovate container and rebuilt its behaviour from recorded inputs and outputs. That takes longer than any fork. An early preset library generated from Renovate's presets I replaced with one written in-house in September 2026.
A moving target. Renovate keeps evolving. Every new version can change behaviour, and pinup only notices when I record anew.
04 — The occasion
The switch in September changed no file in any repository. pinup first ran in shadow mode next to Renovate and compared its plans with the open merge requests. The key for that comparison was the branch name. That only worked because both tools produce the same names. Switching over created no duplicate merge request.
Renaming, but as a step of its own
By now the branches in my estate are called pinup/, and the annotations are moving to # pinup:. That does not contradict the opinion; it follows from it. The rename came as a second, separate step, and it lives in the configuration, not in the code.
GitLab cannot rename the source branch of a merge request. So pinup adopts the still-open renovate/ merge requests through branchPrefixOld instead of opening new ones. The annotations are to change only when a plan finds the same dependencies before and after.
05 — The enforcement
Compatibility nobody measures rots quietly. Three mechanisms hold it.
- Configuration parity. Tests compare the resolved configuration with recorded Renovate output, path by path. If two rules swap places, every line of both rules shows up as a difference.
- Deviations with a reason. Where pinup deliberately computes differently, it is on a list of its own. Every entry needs a reason and must hit a real deviation, or the test turns red.
- A fixed yardstick. Measurements are taken against a pinned Renovate version. A new version I have to record anew deliberately. A job that reports it by itself is planned but not built yet.
Frequently asked questions
Should I rename my renovate.json to .pinup.yaml?+
Not for the switch. pinup reads both the same. Renaming pays off when you want comments in the configuration or want to make clear which tool reads it. pinup refuses only two configuration files at once.
Can I switch back to Renovate?+
Yes, as long as the configuration is a renovate.json and uses no pinup-specific keys. Branch names and annotations are the same unless you renamed them yourself. Renovate then takes over the open branches again, provided pinup committed with the same git identity. Otherwise Renovate treats them as modified by someone else.
Why is pinup not a fork of Renovate?+
Because Renovate is AGPL-3.0 and pinup is meant to stay Apache-2.0. A fork would have carried the licence along. So pinup is a clean-room rebuild: behaviour observed, code not read.
Conclusion
The cheapest migration is the one nobody notices. pinup pays for it with someone else's rules, a rebuild without looking at the code and a moving target. It still gets its own name, just later and as a step of its own.
Next week: how big an update really is. The stricter label wins. The overview of all parts and the terms that recur are in the opening of the series.
How much of your Renovate configuration does pinup use? I check it key by key.
pinup migrate shows for your configuration what it uses and what pinup supports of it. Then a shadow run next to your existing runner, before anything changes.
About the author

Kai Ole Hartwig
Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.
