Hi everyone!
While doing prep work for the LTS offering for my employer, I started feeling like I was noticing a pattern with backports and PRs with the security label. It seemed like changes that were intended to be backported often enough ended-up not being backported.
The problem(?)
A feeling’s not enough. I can’t just go with the vibe of how something feels and call it a day. So I took some time to see if I could figure out something robust to disprove what I was feeling.
To make a short story long, I coerced GitHub PR search into showing me likely candidates. This query is not by itself sufficient, since it only shows where backports happened to fail. Sometimes it’s because there’s nothing wrong, and some results are irrelevant since they’ve been handled.
What bothered me was for those where there was no follow-up.
There are two classes of such PRs with no follow-ups:
- The backport failure was noticed, something was done, and there was no cross-referencing.
- The backport failure was missed, and nothing was done.
The former (no cross-referencing) while annoying, is not a big deal. The necessary work has being done.
The latter (when nothing was done) could be an issue. So I took some time and went through the data to figure out if it even is an issue.
The data
I used gh as a quick and dirty way to make GitHub API queries, and jq to transform to CSV.
# NOTE: the query was refined over time, this is only an example.
$ gh search prs --json "id,number,title,state,updatedAt,url" \
--limit 100 --repo "NixOS/nixpkgs" \
--label '1.severity: security' \
--sort 'updated' --order asc \
'"Backport failed for"' "25.11, because" \
'updated:>=2026-05-01' \
| jq . \
| tee updates.json
# The previous unneeded `jq .` is used to pretty-print the output.
$ < updates.json \
jq -r '.[] | [.number, .title, .updatedAt, .url] | @csv' \
> failed.csv
This data was then imported in a (private) Grist document. The nice thing with importing in that Grist document is that it can effectively UPSERT on columns matching, so tracking over time was not painful.
I don’t have the freshest data, since I only have a snapshot of the state on the 12th of May handy, so take this with a grain of salt. On the other hand also consider that this is somewhat representative of a large chunk of time for a supported stable branch, 163 out of 212 days or 163 out of 181 days until the following release.
Out of 137 PRs with failed backports and the security label:
- 106 had a related merged backport.
- 7 were obsoleted or were entirely irrelevant.
- 4 had gotten a backport PR.
- Leaving 20 packages with no follow-up.
These numbers don’t take into account the recently merged PRs. For example, at that time 3 of those PRs without a follow-up had their latest update less than 24 hours prior.
I think this is better than the gut feeling I initially had. The main confounding factor initially was the sheer amount of PRs with failed backports, and an unfortunate sampling either with no follow-up, or lack of communication with follow-ups.
What’s been done
It would be rude to only come here and tell you all about these observations.
Instead, I took some time and thought about what could be done to help either prevent this from happening (impossible) or equip the contributors with the tools to manage these failed backports.
So I came up with a small tweak to the existing backport action, which now also adds a label to track failed backports.
The main reason I’m posting here and now is to make it known what the label is for, now that it had some time to cook and show its pertinence. This new label (8.has: failed backport) is used as a signal that there is some work to do with the PR still.
Before removing the label, either:
- A backport needs to be made and/or cross-referenced.
- A rationale for not needing a backport needs to be added.
As usual with all things Nixpkgs, there is no obligation, no requirement to do anything with this label.
But unusually with a big project such as a distribution, there is a way to achieve “inbox-zero” with this label.
The proposed workflow:
- Search for
sort:updated-asc is:pr label:"8.has: failed backport" - Open the first result, which is the one that was updated the least recently.
- Check what has happened in the PR since the label was added.
- If there is a rationale for not backporting or a cross-reference already, remove the label.
- If the backport target branch is EOL, add a comment stating so, and remove the label.
- Search for related work. Search for the package name through all PRs:
is:pr ${...} in:title. (Thein:titlesearch operator is helpful, but not mandatory.)- If there is an existing backport of the package (for that version or newer), add a comment on either PR for cross-referencing the backport PR, and remove the label.
- If there are further updates to the package, take note of failures or discussions surrounding backports in those, and report back.
- Handle the backport. Report back in the PR.
- If feasible, open the backport PR, cross-reference and remove the label.
- If unneeded or irrelevant (for example different upstream stable branch followed) report so, remove the label.
- For security updates, a backport is unneeded or irrelevant if the security issue does not apply to the version in the stable branch.
- If there are issues or it is non-trivial, either:
- keep the label and sum up your observations in a new comment.
- open a draft PR highlighting the problems, cross-reference, and remove the label.
While doing all that, keep in mind that if there are newer PRs targeting the unstable branch for the same package, make sure that any failed backport for the same package up to the newest one is handled at the same time. That is, if hello: 2.0.1 -> 2.0.2 has the failed backport label, and there are PRs for hello: 2.0.2 -> 2.0.3 and hello: 2.0.3 -> 2.0.4, backport work should make sure to take all those into account, and properly cross-reference.
Also be mindful about the overlap in stable branch support periods during the month where both the new-current-stable, and the now-old-stable are supported. There can and will be cases with a failed backport to only one of the two branches.
And note that this can be made more manageable by scoping it with a topic, for example with the security label.
Early 26.05 stats
For PRs with the backport release-26.05 and security label (limited to the date I write this post), there is currently:
- 81 automated backport requests
- 8 PRs with the failed backport label
- Of these 7 have been successfully made for 26.05, and only failed for 25.11.
Though this is not representative of the whole support cycle for the stable branch. This is not long after the branch-off, when backports are generally more trivial.
The only conclusion that can be made is: backports are trivial right after branch-off ¯\_(ツ)_/¯.
Closing words
Finally, I want to make it clear: this is not a big problem that should scare people off of the stable branch. While there could be some security fixes that it would have benefited from, if not from that lack of execution, it’s not a systematic failure. The few unhandled failed backports are not making NixOS and Nixpkgs stable specially more insecure. The large majority, if not all, of the issues are unlikely to have any real impact. The impactful issues are generally handled with more care, and followed-through to the end.
It is also quite unlikely that any “secure” or “stable” NixOS fork or non-fork offering will magically solve all of that. And it would be rude not to work upstream in Nixpkgs if they would.
Do not panic. Just remember: if you’re a contributor or maintainer, please keep an eye on for that new label on those PRs you have made, reviewed, or merged :).
DISCLAIMER: This post is written by a human.
DISCLOSURE: At the time this is posted, I am an employee at Cyberus Technology. While this is written on the clock, these are my own words and my message.