<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://ktherage.github.io/xsl/atom.xsl" media="all"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://ktherage.github.io/tags/ubuntu/</id>
  <title>Kévin THÉRAGE | Expert Symfony Developer - Ubuntu</title>
  <subtitle><![CDATA[Technical blog over web development, Symfony, PHP and best practices. Find tutorials and advices for developers.]]></subtitle>
  <link href="https://ktherage.github.io/tags/ubuntu/atom.xml" rel="self" type="application/atom+xml" />
  <link href="https://ktherage.github.io/tags/ubuntu/" rel="alternate" type="text/html" />
  <updated>2026-04-28T12:39:44+00:00</updated>
  <author>
    <name>Kévin THÉRAGE</name>
    <uri>https://ktherage.github.io/</uri>
  </author>
  <entry xml:lang="en">
    <id>https://ktherage.github.io/blog/ubuntu-25-10-docker-java-cgroupv2-crash/</id>
    <title>Ubuntu 25.10: The Update That Bricked My Java Docker Containers</title>
    <published>2026-04-28T00:00:00+00:00</published>
    <link href="https://ktherage.github.io/blog/ubuntu-25-10-docker-java-cgroupv2-crash/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h2 id="the-quiet-update-that-turned-into-a-nightmare">The Quiet Update That Turned Into a Nightmare</h2>
<p>It was a Friday night, just like any other. I finally decided to do what every good developer avoids: <strong>updating my OS</strong>. Ubuntu 24.04 LTS → 25.10, just a routine little update. <em>"It can only get better,"</em> I told myself with that specific brand of pessimism reserved for those who have seen too many updates go south (Ubuntu 24.10, I’m looking at you!).</p>
<p>I launched the update with confidence. Everything went well. Reboot. Everything works. Perfect.</p>
<p>Except that the following Monday, my E2E tests were failing. The <code>selenium/standalone-chrome:4.5.3</code> Docker container, which worked perfectly the day before, now refused to start. It was crashing in a loop with this magnificent error message:</p>
<pre><code>chrome-1  | java.lang.reflect.InvocationTargetException
chrome-1  |      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
chrome-1  |      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
chrome-1  |      at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
chrome-1  |      at java.base/java.lang.reflect.Method.invoke(Method.java:566)
chrome-1  |      at org.openqa.selenium.grid.Bootstrap.runMain(Bootstrap.java:77)
chrome-1  |      at org.openqa.selenium.grid.Bootstrap.main(Bootstrap.java:70)
chrome-1  | Caused by: java.lang.NullPointerException
chrome-1  |      at java.base/jdk.internal.platform.cgroupv2.CgroupV2Subsystem.getInstance(CgroupV2Subsystem.java:81)
chrome-1  |      at java.base/jdk.internal.platform.CgroupSubsystemFactory.create(CgroupSubsystemFactory.java:113)
chrome-1  |      at java.base/jdk.internal.platform.CgroupMetrics.getInstance(CgroupMetrics.java:167)
chrome-1  |      at java.base/jdk.internal.platform.SystemMetrics.instance(SystemMetrics.java:29)
chrome-1  |      at java.base/jdk.internal.platform.Metrics.systemMetrics(Metrics.java:58)
chrome-1  |      at java.base/jdk.internal.platform.Container.metrics(Container.java:43)
...</code></pre>
<p><strong>My first thought:</strong></p>
<div class="d-flex flex-row m-2 justify-content-center">
  <iframe src="https://giphy.com/embed/4ZxicT7ZQYcLShHOiz" width="480" height="274" style="" frameborder="0" class="giphy-embed" allowfullscreen></iframe>
</div>
<blockquote>
<p>I'm a PHP developer, not a Java one.</p>
</blockquote>
<p><strong>My reflex:</strong></p>
<div class="d-flex flex-row m-2 justify-content-center">
  <iframe src="https://giphy.com/embed/pUVOeIagS1rrqsYQJe" width="480" height="288" style="" frameborder="0" class="giphy-embed" allowfullscreen></iframe>
</div>
<blockquote>
<p>Let's ask someone smarter than me. Gemini cricket (🤖🦗) 🥲.</p>
</blockquote>
<h2 id="the-investigation">The Investigation</h2>
<p>My cricket friend pointed out <strong>cgroupv2</strong> on line <code>CgroupV2Subsystem.java:81</code> and linked it to my system update. But what exactly is <strong>cgroupv2</strong>?</p>
<p>🤖🦗:</p>
<blockquote>
<p>Long story short, it's what allows Docker 🐋 to limit a container's CPU or RAM.</p>
</blockquote>
<p>In concrete terms:</p>
<ul>
<li>Docker tells the JVM: <em>"You are allowed 2GB of RAM."</em></li>
<li>Java reads this info from <strong>cgroups</strong> (resource management files).</li>
<li>Java adjusts its behavior (Heap memory, etc.) accordingly.</li>
</ul>
<p><strong>This is supposed to be a good thing.</strong> It prevents Java from being slaughtered by the host system's <em>OOM Killer</em>. However, Java must parse these files, and that’s where things fall apart.</p>
<blockquote>
<p><strong>Why a crash instead of just an error?</strong>
In the source code of older JVMs, if the path returned by the system interface isn't exactly what's expected, the <code>mountPoint</code> variable remains <code>null</code>. The JVM then attempts to call a method on this non-existent object. It's a classic backfire: the function meant to protect your application becomes the very cause of its summary execution.</p>
</blockquote>
<h2 id="the-plot-twist-the-subtle-difference-between-ubuntu-24-04-and-25-10">The Plot Twist: The Subtle Difference Between Ubuntu 24.04 and 25.10</h2>
<p>This is where it gets fascinating.</p>
<p>The real culprit is the evolution of <strong>systemd</strong> (now at version 258 in Ubuntu 25.10). Since v256, systemd has enforced a "hardening" of the cgroup v2 hierarchy. It no longer just exposes controllers; it organizes them in a much more granular way to isolate services. Legacy Java versions, designed back when the hierarchy was more predictable and less protected, find themselves literally "blind" to this new structure.</p>
<p><strong>And guess what?</strong> The old Java initialization logic (pre-Java 17) is far too rigid to understand this new format.</p>
<p>At startup, the Java inside our container scouts the system, fails to find the "memory" controller exactly where it expected, and silently assigns <code>null</code> to its internal variable. On the very next line, the code tries to call the <code>.getMountPoint()</code> method on this empty object.</p>
<p><strong>BOOM</strong>. NullPointerException. Instant process death.</p>
<p>The culprit wasn't our code or our Docker config, but an old JVM incapable of adapting to a modern Linux kernel's new hierarchy. <em>Fair enough, you might say.</em></p>
<h2 id="the-solution">THE Solution</h2>
<p>The clean solution, the one you should always use in production:</p>
<pre><code class="language-bash hljs bash"><span class="hljs-comment"># Update to a recent version of the image</span>
docker pull selenium/standalone-chrome:4.20.0

<span class="hljs-comment"># OR use an image with Java 17+</span>
docker pull selenium/standalone-chrome:latest</code></pre>
<h2 id="the-survival-hack-disabling-usecontainersupport">The Survival Hack — Disabling UseContainerSupport</h2>
<p>If you cannot update the image (legacy constraints, QA validation, etc.), you can disable container detection:</p>
<pre><code class="language-bash hljs bash"><span class="hljs-comment"># Option 1: Via Docker environment variable</span>
docker run -d \
  -e JAVA_OPTS=<span class="hljs-string">"-XX:-UseContainerSupport"</span> \
  selenium/standalone-chrome:4.5.3

<span class="hljs-comment"># Option 2: Via docker-compose.yml</span>
services:
  chrome:
    image: selenium/standalone-chrome:4.5.3
    environment:
      - JAVA_OPTS=-XX:-UseContainerSupport</code></pre>
<p><strong>⚠️ Important Warning:</strong></p>
<ul>
<li><strong>DO NOT DO THIS IN PRODUCTION.</strong> In my case, this is a <strong>local development</strong> container.</li>
<li>Without <code>UseContainerSupport</code>, Java is unaware of its limits and can be terminated by the host system's OOM Killer.</li>
<li>This solution is a <strong>temporary band-aid</strong> while waiting for an update.</li>
</ul>
<h2 id="the-morale-of-the-story">The Morale of the Story</h2>
<p>Our software ecosystems are <strong>fragile</strong>. A simple Linux kernel update—via an Ubuntu update in my case—can break containers that worked perfectly from one version to another.</p>
<p><strong>Philosophical advice:</strong></p>
<blockquote>
<p><em>Remembering to "clean your room" regularly (I'm sure you get the metaphor) can save a lot of time.</em></p>
</blockquote>
<blockquote>
<p><em>Never perform an OS update on a Friday.</em></p>
</blockquote>
<blockquote>
<p><em>Always make sure to test your Docker containers in a staging environment after a system update.</em></p>
</blockquote>
<blockquote>
<p><em>"Works on my machine" — until the kernel updates.</em></p>
</blockquote>
<h2 id="sources-and-references">Sources and References</h2>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17+containers/" rel="noopener noreferrer">Oracle Docs: Java Container Support</a> </li>
<li><a href="https://ubuntu.com/blog/ubuntu-25-10" rel="noopener noreferrer">Ubuntu 25.10 Release Notes</a></li>
<li><a href="https://docker-java.readthedocs.io/" rel="noopener noreferrer">Docker &amp; Java: Best Practices</a></li>
<li><a href="https://www.kernel.org/doc/Documentation/cgroup-v2.txt" rel="noopener noreferrer">cgroup v2 kernel documentation</a></li>
<li><a href="https://systemd.io/" rel="noopener noreferrer">Systemd News: Changes in unified cgroup hierarchy handling (v256+)</a></li>
</ul>]]>
    </content>
  </entry>
</feed>
