<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Two technical questions towards GPU/CDI support: AppArmor and manifest capabilities]]></title><description><![CDATA[<p dir="auto">We are working on a concrete, evidence-backed proposal for GPU (Graphical Processor Unit) device access for apps, following on from the long-running request in <a href="https://forum.cloudron.io/topic/12401">https://forum.cloudron.io/topic/12401</a> and the CDI (Container Device Interface) (groundwork described there.</p>
<p dir="auto">The short version of the context: CDI has been enabled by default in Docker since 28.2.0, the docker-ce 29.6.2 that the current installer pins now routes even its own <code>--gpus</code> flag through CDI for both NVIDIA and AMD, and I have working proofs of a real GPU via CDI with the NVIDIA toolkit and of stock docker-ce 29.6.2 consuming a synthetic CDI spec through the same Engine API payload shape dockerode sends.</p>
<p dir="auto">Writing the design up against the box source left us with a small number of questions that only Team Cloudron can answer:</p>
<p dir="auto"><strong>Question 1: the docker-cloudron-app AppArmor profile.</strong></p>
<p dir="auto">A CDI spec injects host paths into the container at create time, typically the driver libraries and a few binaries such as nvidia-smi, as bind mounts described by the spec.</p>
<p dir="auto">Does the <code>docker-cloudron-app</code> profile (setup/start/docker-cloudron-app.apparmor in the box source) permit reads and mmap of libraries mounted in from host driver paths, or would it deny them?</p>
<p dir="auto">We can test this empirically on our own hardware if that is useful, but the profile's intended scope is something only its authors can state, and it determines whether a GPU grant works under the standard app confinement or needs a profile amendment.</p>
<p dir="auto"><strong>Question 2: where manifest <code>capabilities</code> values are validated.</strong></p>
<p dir="auto">Reading the box source, <code>manifest.capabilities</code> appears to be consumed only in src/docker.js, with unrecognised values silently ignored, which would make adding a new capability name purely additive.</p>
<p dir="auto">Is there a whitelist elsewhere, for example in the CLI's manifest validation or in app store submission checks, that would reject a manifest declaring an unknown capability such as <code>gpu</code>?</p>
<p dir="auto"><strong>Question 3: which shape would you prefer, if any?</strong></p>
<p dir="auto">The two natural shapes I found are, first, widening the existing per-app Devices feature (the dashboard Resources form backed by setDevices) so it accepts CDI device names like <code>nvidia.com/gpu=all</code> alongside <code>/dev/</code> paths, and second, a new manifest capability mirroring how <code>vaapi</code> is implemented, with a guard so it no-ops on hosts without a CDI spec.</p>
<p dir="auto">The first reuses the persistence, REST, dashboard, audit and recreate machinery unchanged and keeps the decision with the administrator rather than the packager; the second matches the existing capability precedent.</p>
<p dir="auto">If the team has a preference, or a reason both are wrong, knowing it before anything is written up further would save everyone time.</p>
<p dir="auto">We have a full design sketch with source references, failure modes and non-goals, plus reproducible test transcripts, and are happy to share any of it; there is also an Nvidia machine available for any test the team would find persuasive.</p>
]]></description><link>https://forum.cloudron.io/topic/15756/two-technical-questions-towards-gpu-cdi-support-apparmor-and-manifest-capabilities</link><generator>RSS for Node</generator><lastBuildDate>Sat, 01 Aug 2026 19:55:27 GMT</lastBuildDate><atom:link href="https://forum.cloudron.io/topic/15756.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 30 Jul 2026 16:22:47 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Two technical questions towards GPU/CDI support: AppArmor and manifest capabilities on Sat, 01 Aug 2026 09:59:03 GMT]]></title><description><![CDATA[<p dir="auto">Great, and thanks girish. Cloudron is brilliant!</p>
]]></description><link>https://forum.cloudron.io/post/127580</link><guid isPermaLink="true">https://forum.cloudron.io/post/127580</guid><dc:creator><![CDATA[LoudLemur]]></dc:creator><pubDate>Sat, 01 Aug 2026 09:59:03 GMT</pubDate></item><item><title><![CDATA[Reply to Two technical questions towards GPU/CDI support: AppArmor and manifest capabilities on Sat, 01 Aug 2026 09:41:26 GMT]]></title><description><![CDATA[<p dir="auto">Yeah, feel free to share your patches here though, it's not a problem. As <a class="plugin-mentions-user plugin-mentions-a" href="/user/james" aria-label="Profile: james">@<bdi>james</bdi></a>  said elsewhere, the project is not set up to accept external contribution. Licensing issues aside, there is a lot of testing that goes on (we test each release in over 15 VPS providers, have e2e tests for all features), documentation, product messaging and so on which at the moment all not public. Instead of forking, you can just clone and push your repo.</p>
]]></description><link>https://forum.cloudron.io/post/127578</link><guid isPermaLink="true">https://forum.cloudron.io/post/127578</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Sat, 01 Aug 2026 09:41:26 GMT</pubDate></item><item><title><![CDATA[Reply to Two technical questions towards GPU/CDI support: AppArmor and manifest capabilities on Fri, 31 Jul 2026 21:59:09 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/girish" aria-label="Profile: girish">@<bdi>girish</bdi></a></p>
<p dir="auto">Understood, and thank you for the straight answer. We will not send a patch.</p>
<p dir="auto">Since the capability itself sounded acceptable, here is everything needed to implement it, so it costs whoever picks it up an afternoon rather than a week. No code below, only behaviour and the results of the testing we have already done.</p>
<p dir="auto"><strong>The change, in words</strong></p>
<ul>
<li>In the same place <code>vaapi</code> is handled at container creation, a <code>gpu</code> capability sets <code>HostConfig.DeviceRequests</code> to a single entry: driver <code>cdi</code>, device IDs a one-element list holding the resolved device name, for example <code>nvidia.com/gpu=all</code>. Nothing else in the container options changes.</li>
<li>Guard it so it does nothing on a host with no GPU (Graphics Processing Unit), exactly as <code>vaapi</code> no-ops without <code>/dev/dri</code>.</li>
</ul>
<p dir="auto"><strong>The guard is one API call, not a specification parser</strong></p>
<ul>
<li><code>GET /info</code> reports what the daemon resolved, as <code>DiscoveredDevices</code>. On our test daemon that reads <code>[{"Source":"cdi","ID":"example.com/gpu=all"}]</code>.</li>
<li>So: take entries whose <code>Source</code> is <code>cdi</code> and whose <code>ID</code> matches <code>&lt;vendor&gt;/gpu=&lt;name&gt;</code>, prefer the one ending <code>=all</code>, otherwise take the first, and if there are none, do nothing.</li>
<li>Match the device class rather than the vendor prefix. AMD's toolkit generates <code>amd.com/gpu</code> and Docker 29.3.0 already routes <code>--gpus</code> through CDI (Container Device Interface) for AMD, so vendor-agnostic matching costs nothing and covers the second vendor for free. This is also why we would suggest the name <code>gpu</code> rather than <code>nvidia</code>.</li>
<li>Do not read or compare <code>cdiVersion</code>. The upstream specification is at 1.1.0 while NVIDIA emits 0.7.0 and AMD emits its own, and consumers accept anything up to the version they support.</li>
<li><code>DiscoveredDevices</code> needs Engine API 1.50 or newer. On anything older, scanning <code>/etc/cdi</code> and <code>/var/run/cdi</code> for a specification whose <code>kind</code> ends in <code>/gpu</code> is an adequate fallback.</li>
</ul>
<p dir="auto"><strong>What we tested, so nobody has to repeat it</strong></p>
<ul>
<li><strong>The AppArmor profile does not interfere.</strong> Ubuntu 24.04, docker-ce 29.6.2 and containerd 2.2.6 as your installer pins them, <code>docker-cloudron-app</code> loaded verbatim, container run confined with a read-only root filesystem and an unprivileged user. Injected libraries were read, executed and mmapped, the injected device node opened, and <code>dmesg</code> showed no denials at all. Results were identical unconfined.</li>
<li><strong><code>ReadonlyRootfs: true</code> is not a problem.</strong> An NVIDIA specification runs <code>createContainer</code> hooks that write into the container root filesystem, creating the <code>libcuda.so.1</code> symlinks and running ldconfig. Those hooks run before the runtime applies the read-only remount, so they succeed while the app process itself still cannot write. No carve-out needed.</li>
<li><strong>Host prerequisites stay with the administrator.</strong> The NVIDIA driver and <code>nvidia-container-toolkit</code>, nothing else. No <code>nvidia</code> runtime registration and no daemon configuration change. Toolkit 1.19 ships <code>nvidia-cdi-refresh.path</code>, which regenerates the specification whenever the driver or toolkit is upgraded, so the refresh automation that made this look expensive in 2024 is now upstream's.</li>
</ul>
<p dir="auto"><strong>Two decisions we would leave to you</strong></p>
<ul>
<li>Whether an unmet <code>gpu</code> capability should stay silent like <code>vaapi</code>, or fail visibly. An app quietly running without its GPU may be worse than one that refuses to start.</li>
<li>Whether to gate it as experimental for a first release.</li>
</ul>
<p dir="auto">If a written specification with source references, failure modes and non-goals would be useful, we have one and will post or send it in whatever form suits. There is also an Nvidia machine here for any test that would help, including confirming the AppArmor result against real hardware, which is the one thing our virtual machine could not do.</p>
]]></description><link>https://forum.cloudron.io/post/127566</link><guid isPermaLink="true">https://forum.cloudron.io/post/127566</guid><dc:creator><![CDATA[LoudLemur]]></dc:creator><pubDate>Fri, 31 Jul 2026 21:59:09 GMT</pubDate></item><item><title><![CDATA[Reply to Two technical questions towards GPU/CDI support: AppArmor and manifest capabilities on Fri, 31 Jul 2026 10:23:50 GMT]]></title><description><![CDATA[<p dir="auto">The AppArmor transcript, for the record, and the short version for anyone who wants to confirm it against real hardware.</p>
<h3>What was run</h3>
<ul>
<li><strong>Host:</strong> a throwaway Ubuntu 24.04.4 virtual machine, kernel 6.8.0-136, AppArmor enforcing.</li>
<li><strong>Docker:</strong> docker-ce 29.6.2 with containerd 2.2.6, the versions <code>scripts/installer.sh</code> pins, from the same Docker repository debs. Storage driver overlayfs, Engine API 1.55.</li>
<li><strong>Profile:</strong> <code>setup/start/docker-cloudron-app.apparmor</code> copied verbatim from the box source and loaded with <code>apparmor_parser -r -W</code>.</li>
<li><strong>Device:</strong> a synthetic CDI (Container Device Interface) specification, because there is no GPU (Graphics Processing Unit) in the virtual machine. It reproduces what the profile mediates: a real shared library bind mounted to <code>/usr/lib/x86_64-linux-gnu/libcuda.so.610.43.03</code>, a binary mounted as a stand-in for <code>nvidia-smi</code>, a character device node absent from the image, an injected environment variable, and a <code>createContainer</code> hook doing what <code>create-symlinks</code> and <code>update-ldcache</code> do.</li>
</ul>
<h3>Result: no denials anywhere</h3>
<pre><code>docker run --rm --read-only \
    --security-opt apparmor=docker-cloudron-app \
    --device example.com/gpu=all ubuntu:24.04 sh -c '...'

-- injected env:            CDI_PROOF=it-works
-- read injected library:   0000000 177   E   L   F
-- mmap injected library:   mapped regions: 5
-- execute injected binary: EXEC_OK
-- open injected device:    DEVICE_WRITE_OK
-- hook artefacts:          libcuda.so.1 -&gt; libcuda.so.610.43.03
-- rootfs writable?:        read-only as expected
--- kernel denials:         NONE
</code></pre>
<ul>
<li>The <strong>mmap</strong> line is the load-bearing one: the injected library is loaded through <code>LD_PRELOAD</code> and counted in <code>/proc/self/maps</code>, so the dynamic loader opened and mapped a library that exists in the container only because the specification put it there, under confinement.</li>
<li>The same run <strong>unconfined</strong> gives byte-for-byte the same output, so the profile changes nothing about CDI injection.</li>
<li>A third run with <strong><code>--user 1000:1000</code></strong>, since app processes are not root, also passes, and <code>/proc/self/attr/current</code> inside the container reads <code>docker-cloudron-app (enforce)</code>, confirming the profile was genuinely applied rather than quietly ignored.</li>
</ul>
<h3>To confirm it on real hardware, three commands</h3>
<p dir="auto">For anyone with an NVIDIA box, the toolkit installed and Docker 28.2.0 or newer:</p>
<pre><code># 1. check the pieces
ls /etc/cdi /var/run/cdi                  # a nvidia.yaml should be in one of them
sudo aa-status | grep docker-cloudron-app # the profile should be loaded

# 2. run it in the shape the box creates
sudo docker run --rm --read-only \
    --security-opt apparmor=docker-cloudron-app \
    --device nvidia.com/gpu=all \
    ubuntu:24.04 nvidia-smi

# 3. look for denials
sudo dmesg | grep -i 'apparmor.*DENIED'
</code></pre>
<p dir="auto">The image contains no NVIDIA userspace at all, so every part of the <code>nvidia-smi</code> output comes from the specification. If step 3 prints nothing, the profile blocked nothing.</p>
<p dir="auto">If a specification needs generating first, that is <code>sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml</code>, and adding <code>--user 1000:1000</code> to step 2 is the closest thing to what an app container actually does.</p>
<p dir="auto"><strong>The one gap we cannot close here</strong> is the ioctls that <code>nvidia-smi</code> performs on <code>/dev/nvidia*</code>, since no GPU was in the virtual machine. Opening those device nodes is what the profile mediates and that passed, so we do not expect a surprise, but a run on real hardware would settle it.</p>
]]></description><link>https://forum.cloudron.io/post/127532</link><guid isPermaLink="true">https://forum.cloudron.io/post/127532</guid><dc:creator><![CDATA[LoudLemur]]></dc:creator><pubDate>Fri, 31 Jul 2026 10:23:50 GMT</pubDate></item><item><title><![CDATA[Reply to Two technical questions towards GPU/CDI support: AppArmor and manifest capabilities on Fri, 31 Jul 2026 09:32:24 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/girish" aria-label="Profile: girish">@<bdi>girish</bdi></a></p>
<p dir="auto">Thank you, that is what we needed.</p>
<h3>Your question: no, nvidia's special docker is not needed any more</h3>
<ul>
<li><strong>Gone:</strong> the <code>nvidia</code> runtime registration in <code>/etc/docker/daemon.json</code> and <code>--runtime=nvidia</code>. This is precisely what CDI (Container Device Interface) replaces. Nothing on the Cloudron side would touch the daemon configuration.</li>
<li><strong>Still needed, and still the administrator's job:</strong> the NVIDIA driver, and the <code>nvidia-container-toolkit</code> package. <code>nvidia-ctk cdi generate</code> writes the specification file, and the generated specification calls <code>/usr/bin/nvidia-cdi-hook</code> from that same package when a container starts.</li>
<li><strong>New since the 2024 discussion:</strong> the toolkit keeps the specification current by itself. Version 1.19 ships <code>nvidia-cdi-refresh.path</code>, a systemd path unit that regenerates it whenever the driver or the toolkit is upgraded. The refresh-on-driver-update automation that made this look expensive in 2024 is now upstream's, not yours.</li>
<li><strong>Left on the Cloudron side:</strong> one <code>HostConfig.DeviceRequests</code> entry, <code>{ "Driver": "cdi", "DeviceIDs": ["nvidia.com/gpu=all"] }</code>, plus a guard so the capability does nothing on a host with no specification, in the spirit of <code>vaapi</code>'s check for <code>/dev/dri</code>.</li>
</ul>
<h3>Question 1, AppArmor: we tried, as suggested, and it passes</h3>
<p dir="auto">We built an Ubuntu 24.04 virtual machine, installed docker-ce 29.6.2 and containerd 2.2.6 as your installer pins them, loaded <code>docker-cloudron-app</code> verbatim from the box source, and ran a container in the shape the box creates: confined by the profile, read-only root filesystem, CDI device attached.</p>
<p dir="auto">The specification was synthetic, since there is no GPU (Graphics Processing Unit) in the virtual machine, but it reproduces what the profile actually mediates: driver libraries and a binary bind mounted in from host paths, a device node the image does not have, and a <code>createContainer</code> hook that writes into the container root filesystem.</p>
<ul>
<li><strong>Reading, executing and mmapping the injected files: all fine.</strong> The injected library was loaded through <code>LD_PRELOAD</code> and appears as five mapped regions in <code>/proc/self/maps</code>, which is the operation the question was really about.</li>
<li><strong>The injected device node opened and accepted a write.</strong></li>
<li><strong>Zero kernel denials.</strong> <code>dmesg | grep apparmor.*DENIED</code> is empty after every run.</li>
<li><strong>Identical results unconfined,</strong> so the profile changes nothing about CDI injection.</li>
<li><strong>Confirmed with <code>--user 1000:1000</code>,</strong> since app processes are not root, and <code>/proc/self/attr/current</code> inside the container reads <code>docker-cloudron-app (enforce)</code>, so the profile really was applied.</li>
</ul>
<p dir="auto">What is not proven: no real GPU was involved, so the ioctls <code>nvidia-smi</code> performs on <code>/dev/nvidia*</code> are formally untested. Everything the profile mediates, which is file access, is covered. Full transcript in the next post.</p>
<h3><code>ReadonlyRootfs</code> also passes, which was the sharper risk</h3>
<p dir="auto">An NVIDIA specification does not only bind mount libraries. It runs <code>createContainer</code> hooks that <strong>write into the container root filesystem</strong>, creating the <code>libcuda.so.1</code> style symlinks and running ldconfig.</p>
<ul>
<li><strong>Result: the hooks succeed</strong> on overlayfs, under the profile, as an unprivileged user, while the container process itself is still refused writes on the same filesystem.</li>
<li><strong>Why:</strong> the runtime applies the read-only remount after the <code>createContainer</code> hooks have run. No writable-rootfs carve-out is needed for app containers.</li>
</ul>
<h3>The presence guard turns out to be one API call</h3>
<p dir="auto"><code>vaapi</code> can use <code>existsSync('/dev/dri')</code>, but a CDI name is a lookup key rather than a path, which looked like the one piece of genuinely new logic. It is not:</p>
<pre><code>docker info --format '{{json .DiscoveredDevices}}'
[{"Source":"cdi","ID":"example.com/gpu=all"}]
</code></pre>
<p dir="auto">The same field comes back from <code>GET /info</code> on the socket, so from the box it is a <code>docker.info()</code> call and a search for an entry whose <code>Source</code> is <code>cdi</code>. No directory scanning and no specification parsing. It is available from Engine API 1.50; the daemon under test reported 1.55.</p>
<h3>On the name, we would suggest <code>gpu</code> rather than <code>nvidia</code></h3>
<ul>
<li>AMD's container toolkit now generates CDI specifications too, under <strong><code>amd.com/gpu</code></strong>, and Docker 29.3.0 routes its own <code>--gpus</code> flag through CDI for AMD, as 29.2.2 did for NVIDIA.</li>
<li>A single <code>gpu</code> capability, guarded by a check for any GPU device in the loaded specifications rather than for the NVIDIA vendor prefix, would cover a second vendor with no further code.</li>
</ul>
<hr />
<h3><img src="https://forum.cloudron.io/assets/plugins/nodebb-plugin-emoji/emoji/android/2753.png?v=df184f34222" class="not-responsive emoji emoji-android emoji--question" style="height:23px;width:auto;vertical-align:middle" title="❓" alt="❓" /> Open questions for you</h3>
<ol>
<li><strong>Merge request, or specification first?</strong> We can prepare a patch against master limited to the guard, the <code>DeviceRequests</code> entry in src/docker.js and the manifest documentation. If you would rather read a written specification before reviewing code, we will send that instead.</li>
<li><strong>Is <code>gpu</code> the name you want?</strong> You suggested it, we agree, and the AMD point above is our reason for preferring it over <code>nvidia</code>. Worth confirming before it becomes a documented manifest key.</li>
<li><strong>Silent no-op, or a visible error?</strong> <code>vaapi</code> silently does nothing on a host without <code>/dev/dri</code>. We would mirror that for <code>gpu</code>, but an app that quietly runs without its GPU may be worse than one that refuses to start. Your call, since it sets the precedent.</li>
</ol>
<p dir="auto">There is an RTX 5090 machine here for any test that would help, and the full design sketch with source references and test transcripts is available whenever it is useful.</p>
<p dir="auto">If anyone with a GPU box wants to confirm the AppArmor result against real hardware, it is three commands and we have written them out in the next post.</p>
]]></description><link>https://forum.cloudron.io/post/127531</link><guid isPermaLink="true">https://forum.cloudron.io/post/127531</guid><dc:creator><![CDATA[LoudLemur]]></dc:creator><pubDate>Fri, 31 Jul 2026 09:32:24 GMT</pubDate></item><item><title><![CDATA[Reply to Two technical questions towards GPU/CDI support: AppArmor and manifest capabilities on Fri, 31 Jul 2026 08:16:46 GMT]]></title><description><![CDATA[<p dir="auto">Does this mean we don't need to install nvidia's special docker anymore for the GPU integration to work?</p>
<p dir="auto">For 1. Not sure, you will have to try <img src="https://forum.cloudron.io/assets/plugins/nodebb-plugin-emoji/emoji/android/1f642.png?v=df184f34222" class="not-responsive emoji emoji-android emoji--slightly_smiling_face" style="height:23px;width:auto;vertical-align:middle" title=":)" alt="🙂" /> None of our apps so far need that.</p>
<p dir="auto">For 2,3. Correct, unknown values are ignored. I guess we can add a capability called gpu yeah similar to vaapi</p>
]]></description><link>https://forum.cloudron.io/post/127527</link><guid isPermaLink="true">https://forum.cloudron.io/post/127527</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Fri, 31 Jul 2026 08:16:46 GMT</pubDate></item></channel></rss>