What is actually sitting on nine ordinary servers

For several weeks now this series has argued that the number of vulnerabilities means nothing. Claiming that is easy. Showing it is harder. So here it is, on my own infrastructure, with raw numbers instead of an anecdote.
Three disclaimers first, because without them this piece would be dishonest. One: this is my own infrastructure and it is not subject to any audit. This is operational material, not evidence of compliance and not a deployment case study. Two: these are nine machines belonging to a one-person company. Your numbers will be different, the structure probably similar. Three: every number here comes from a single measurement taken on 19 September 2026, except for one place where I go back to August, and there I say so plainly, because the difference is the point.
What these machines are
Nine servers, eight of them active. Nothing exotic: four running Ubuntu 24.04, two on Ubuntu 22.04, one on Ubuntu 26.04, two on Debian 13. The usual mix for a small technical company. Three are exposed to the internet, five sit on the internal network. Three run containers, five do not, and that distinction turns out to matter more than everything else.
None of them is past end of support. The nearest deadline is Ubuntu 22.04 in April 2027, and it affects two machines. That is the dull part of this story, and a good thing too, because an operating system past EOL deserves its own article, and that one is not dull at all.
The first number, the meaningless one
Across eight active machines the register currently holds 165,608 open entries. If I wanted to impress somebody, I would stop at that sentence and add an exclamation mark.
The severity breakdown looks like this:
| Severity | Instances | Unique CVEs |
|---|---|---|
| Critical | 376 | 46 |
| High | 10,382 | 756 |
| Medium | 141,949 | 5075 |
| Low | 12,862 | 817 |
| Unknown | 39 | 18 |
The first column is the number a dashboard shows: 165,608. The second adds up to 6649 unique vulnerabilities. The gap exists because a single vulnerability sitting on four machines counts four times, and rightly so, since it has to be fixed in four places. But you settle it once and click four times.
The first column says how much work there is. The second says how much thinking. Confusing the two is the exact moment a vulnerability register turns into an empty table ticked off once a quarter.
Where the crowd comes from
This is the interesting part, and not at all where I expected it. The first twenty-five places on the package list are taken by one thing: the kernel. I grouped those twenty-five by kernel version, because that is how you see what the number is made of.
| Kernel version | Packages in the top 25 | Instances each | Total |
|---|---|---|---|
6.8.0-139 |
5 | 8694 | 43,470 |
6.8.0-134 |
5 | 3929 | 19,645 |
7.0.0-27 |
5 | 3913 | 19,565 |
6.8.0-136 |
5 | 3016 | 15,080 |
| shared across versions | 2 | 11,193 | 22,386 |
The third column is the important one. Every package belonging to one kernel version carries exactly the same entry count, to the unit, because it is the same list of vulnerabilities counted once per package the distribution carved that kernel into: headers separately, modules separately, tools separately.
The Linux kernel is the largest single piece of software on a server and has more published vulnerabilities than anything else installed on it. On top of that, my fleet carries the four versions in the table side by side, plus a fifth, 6.8.0-137, which did not fit into the top 25 in full. Old kernels stay after an upgrade until somebody cleans up, and each carries its own full set of packages.
The five rows of that table come to 120,146 entries. With the remaining kernel packages that made the top of the list, it comes to roughly a hundred and twenty-nine thousand, close to eighty per cent of the whole register. Every other piece of software on eight servers, every database, web server, library and tool, fits into the fifth that is left.
For comparison: in August the top of this list was vim with 688 entries. I removed it where it was not needed, and today it has one. That was a single command and I still think it is the cheapest list reduction there is. It just does not work on the kernel, because you cannot uninstall the kernel.
How much of this is work at all
And here comes the number that changes the whole picture: how many of these have a fixed version available at all. One technical caveat first, because without it these numbers would not square with the previous ones. Whether a fixed version exists does not live in the register but in the last scan, so the two have to be matched up, and that match yields 166,961 pairs rather than 165,608 entries. The surplus comes from the same vulnerability showing up twice on one machine, once in the system and once in a container image. I will come back to that below, because it matters more than it looks. Either way the proportion is unambiguous:
| Status | Instances | Packages |
|---|---|---|
| A fixed version exists | 17,230 | 310 |
| None, waiting on the distribution | 149,731 | 449 |
Ninety per cent of this list is nobody's backlog. These are vulnerabilities for which the vendor or the distribution has not shipped a fix yet. They cannot be fixed, only known. Among the critical ones the proportion is similar: 111 matches with a patch, 276 without.
Look at the right-hand column. Those 17,230 fixable instances live in 310 packages. That is how many rows a list of things to do would have, if it showed tasks instead of vulnerabilities. A hundred and sixty-five thousand entries against three hundred and ten rows, and it is the same fleet at the same moment.
What is actually dangerous
Severity is an opinion about how much damage a vulnerability could do if somebody used it. It says nothing about whether anybody is using it. So I checked two things beyond severity.
The first is the KEV catalogue maintained by the US agency CISA, a list of vulnerabilities with evidence that someone has actually used them in an attack. I ran that check in August, it came back zero, and I very nearly left it in this article that way. Good news, stated plainly, because good news is a result too.
The zero was not true.
The check compares my vulnerabilities against a copy of the catalogue I keep locally. A month later I went and looked at the copy itself. The CISA catalogue holds 1716 entries today. My database knew 322 in August, and 390 by the time I looked. So the answer "none of your vulnerabilities is being exploited" was produced by comparing against not quite a quarter of the list, and it sounded exactly as confident as the truth would have sounded. Nothing turned red, because as far as the mechanism was concerned everything was fine: question asked, answer returned.
With the catalogue filled in, the result looks different. Seven unique vulnerabilities on my fleet are in the KEV catalogue, sitting in 95 places across five machines. All seven show up on machines exposed to the internet, and two of them, both in kernel packages, on internal ones as well. The rest are apache2, git, inetutils-syslogd, starlette and libfreetype6. Four of them have a fixed version available, which makes them a package upgrade rather than a project. Libfreetype6 has none at all, and that is the one sitting on the internet side. For the two kernel ones I did not check, and I am not going to guess.
And here is the part that stopped me for a while: not one of those seven carries a critical severity rating. Five are high, two are medium. Sorting the list by severity would never put any of them near the top. The very vulnerabilities somebody is known to be using right now sit in my table below forty-six unique vulnerabilities marked critical.
Seven vulnerabilities known to be used in attacks somewhere in the world are sitting on my fleet. That is not an accident and not evidence of neglect, and it does not mean anyone attacked me in particular. The kernel, apache2, git, a syslog daemon, a font library, a web framework. Ordinary software that someone found a hole in after I had installed it. I run a company that makes its living watching vulnerabilities, and I have them at home like everybody else. The cobbler's children go barefoot, except there is nothing to hide here: vulnerabilities are not a sign of a mess, they are something that arrives on its own, every week, to anyone who keeps a server switched on. The difference between a company in control of this and a company that is not has nothing to do with how many it has. It is whether it knows which seven of the hundred and sixty-five thousand entries matter, and whether it notices the eighth on the day it appears.
I am keeping both numbers here, August's zero and September's seven, because the gap between them says more than either one alone. A green status only tells you the check ran. It does not tell you how much it covered. Today it covers the whole catalogue and the whole fleet, 6649 vulnerabilities out of 6649, so an answer of "none" would finally mean none. It took me a month to tell those two apart on my own tool, so if you have something that has not flagged anything in a long time, go and check its reach rather than its status.
The second thing beyond severity is EPSS, a statistical forecast of the probability that a given vulnerability will be exploited within the next thirty days. A disclaimer right away, because I have just described what happens when you skip it: I have EPSS for 4841 of the 6649 vulnerabilities. For the remaining 1808 I do not know, and I am not going to gloss over that.
Within those 4841, fourteen vulnerabilities outside the KEV catalogue fall into the top ten per cent by probability of exploitation. Six of them are rated high, eight medium. Again, not one critical.
Add the two lists up. Out of 6649 unique vulnerabilities, twenty-one have either evidence of exploitation or a high probability of it. Twenty-one things to think about out of five thousand pages of printout, and none of them is where somebody sorting by severity is looking.
Whose job is this anyway
Now the split that articles about vulnerabilities almost never mention, and that decides everything in a larger company. The scanner looks at two entirely different layers: the host operating system, and the contents of the container images running on that host. I separated them.
| Layer | Instances | Unique CVEs | Packages | Machines |
|---|---|---|---|---|
| Host | 163,840 | 5936 | 505 | 8 |
| Container images | 3121 | 1099 | 279 | 3 |
At first glance the case looks obvious: containers are under two per cent of the list, noise, safe to ignore. And that is exactly the mistake this whole article warns against, because once again the counter says nothing about what counts. The same table with one more column:
| Layer | Instances | With a patch available | Share |
|---|---|---|---|
| Host | 163,840 | 14,632 | 8.9% |
| Container images | 3121 | 2598 | 83% |
On the host, under nine per cent of the entries can be fixed today; everything else is waiting on the distribution. In container images, eighty-three per cent can be fixed, because the fix is already in a newer version of the image rather than in a queue at the distribution. That still means a rebuild and a redeploy, which is real work. But it is work you can schedule, rather than waiting on somebody else.
So the layer that looks like noise in the ranking is the one where work actually closes something. And that is the whole point: these are two different kinds of work, two different calendars and, in a company larger than mine, two different people. System packages, kernels and maintenance windows are the infrastructure administrator's desk. Images, Dockerfiles, tags and pipelines are the container team's desk. Neither of them can close this list alone, and a register that does not separate them will show both the same hundred-page table, and both will put it off.
The individual images make it concrete:
| Image | Instances | Unique CVEs | Critical | With a patch |
|---|---|---|---|---|
check-mk-raw |
1346 | 540 | 0 | 1152 |
uptime-kuma |
502 | 323 | 15 | 380 |
glitchtip |
409 | 224 | 4 | 217 |
The largest image on the list has not a single critical entry and almost everything in it can be patched. The second, nearly three times smaller, has fifteen. Sorting by entry count, I would have started with the wrong one.
This split also surfaced something I did not expect, and it is entirely my own doing. The same portainer/agent runs on two of my machines in two different versions, 2.39.2 and 2.39.3. traefik:v3.7 is two different images on two machines under the same tag. Same story with postgres:18-alpine. The tag says the same thing is running everywhere. The scan says it is not. To see this you have to look at the image digest rather than its name, and that is exactly the kind of drift that grows quietly across a year in a larger fleet.
Who is in the firing line
Machine exposure is the only context that turns this list into an order of action:
| Classification | Machines | Instances | Critical |
|---|---|---|---|
| Internet-facing | 3 | 85,205 | 33 |
| Internal only | 5 | 80,403 | 24 |
The numbers are almost equal, which is interesting in itself. But those thirty-three critical entries and all seven KEV vulnerabilities sit on the side visible from the internet, while the other twenty-four are behind a closed door. Same severity, completely different urgency.
What I did not know in August, and why that was my fault
A month ago I could not have drawn that table, because most of my machines carried no exposure tag at all. At the exact moment the data finally suggested what to deal with first, I was missing the last piece of the puzzle: whether that machine is reachable from the internet or sits in a closed network.
That was not a missing tool, it was my own debt. Tagging nine machines is ten minutes of work I never did because it never felt urgent. I eventually spent those ten minutes, and they are the reason the KEV sentence above is "all seven are on the internet side" instead of a row of identifiers.
I mention it because it is typical. The context that turns a list of vulnerabilities into a list of decisions is almost always knowledge someone keeps in their head rather than in a system. As long as it lives there, it cannot be searched, handed over, or shown to an auditor.
What this article does not contain
Honesty requires showing what I have not done, too.
My decision register is empty. Not "nearly empty". All 165,608 open entries carry not a single decision row: no assigned owner, no deadline, no deliberate risk acceptance with a justification. I collect the data and look at it, and that is only half the job. The other half starts the moment somebody writes next to an entry: "leaving this until March, because X, and we review it then".
Not everything that leaves the list is the process working. Over the last month 419 entries dropped out of the register. Of those, 238 were closed by a plain apt upgrade I ran by hand on three machines because that is how my calendar fell. If I had not separated that out, this article would be crediting a product with a decision I made myself and would have made without it.
I am not saying any of this is fixed. When an entry disappears from a scan, all I know is that the scanner no longer sees it. It might have been upgraded, but the package might equally have been uninstalled, or the scanner changed how it identifies it. Those are two different things, and calling both of them a fix is the same kind of lie as a green status on an incomplete catalogue.
What follows from this
A hundred and sixty-five thousand is the size of a problem nobody gets their head around. If your tool is an export to a spreadsheet, that number is all you get. Not because the scanner is bad, but because the scanner reports everything it sees, and a spreadsheet has no way of telling a fifth kernel version from a syslog daemon exposed to the internet.
The same fleet, at the same moment, described differently: twenty-one vulnerabilities with evidence of exploitation or a high probability of it, three hundred and ten packages that can be upgraded today, and one container image with fifteen critical entries. That is already a list you can read over a coffee and split between two people.
None of those numbers required a new feature. They required three things that were in the data from the start and were not being used: a complete catalogue of exploited vulnerabilities, the information about whether a fixed version exists, and a tag saying which machines are visible from the internet.
How to check this for yourself
You do not have to take my word for it, and you do not have to buy anything to repeat this exercise. Run a scanner on one of your machines and ask the result four questions. How many unique CVEs, rather than entries. How many of them have a fixed version available at all. How many sit in container images and how many in the host system. And finally: which of them are on a machine reachable from the internet.
If the answers look like mine, and I would bet they will, you have better material for a conversation with your board than any presentation. Not "we have a hundred and sixty-five thousand vulnerabilities", but "seven of them are confirmed as exploited in the wild and all of them sit on internet-facing machines, ninety per cent of the list is waiting on a fix from the distribution and nobody is going to do anything about that, and what can actually be closed fits into three hundred and ten packages and into images we rebuild every week anyway".
That is the difference between a register that frightens people and a register you can actually run.
One closing note, because it follows directly from the split between the layers. This list lands on two desks, and in many companies those desks are in different rooms: the infrastructure administrator owns kernels and maintenance windows, the container team owns images and pipelines. I run SYSDEVOPS.EU precisely because I do both and I see where the two drift apart. This article is entirely about my own fleet, so take that as context for where it comes from, not as a pitch.
Weekly CVE digest
One email a week with newly published vulnerabilities worth knowing about. No account needed.
This digest covers new vulnerabilities in general, not your servers. If you want to know which of them actually run in your infrastructure, that is what Secvalis does: it scans your machines and reports only what concerns them.

