<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Proxmox VE on Charlie Chiang's blog</title><link>https://charlie0129.github.io/blog/categories/proxmox-ve/</link><description>Recent content in Proxmox VE on Charlie Chiang's blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 06 Jul 2026 14:30:00 +0800</lastBuildDate><atom:link href="https://charlie0129.github.io/blog/categories/proxmox-ve/index.xml" rel="self" type="application/rss+xml"/><item><title>Installing Proxmox VE on an OVHcloud Kimsufi KS-5</title><link>https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/</link><pubDate>Mon, 06 Jul 2026 14:30:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/</guid><description>&lt;p>I ordered an OVHcloud Kimsufi KS-5 dedicated server for 19.90 USD/month. It is not fast by modern standards, but it is a very usable small Proxmox box if you install it carefully.&lt;/p>
&lt;p>The goal for this machine was:&lt;/p>
&lt;ul>
&lt;li>Proxmox VE on mirrored ZFS root.&lt;/li>
&lt;li>Keep only part of each SSD for the root pool.&lt;/li>
&lt;li>Use the remaining SSD space as a fast disposable scratch pool.&lt;/li>
&lt;li>Put VMs and CTs behind NAT, because the server only has one usable public IPv4 address and one &lt;code>/128&lt;/code> IPv6 address.&lt;/li>
&lt;li>Keep host writes low where it is easy to do so.&lt;/li>
&lt;/ul>
&lt;p>This post is written from the actual KS-5 I installed. The finished machine is running Proxmox VE 9.2.&lt;/p>
&lt;h2 id="hardware">Hardware
&lt;/h2>&lt;p>The server I received:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Part&lt;/th>
&lt;th>Value&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Product&lt;/td>
&lt;td>Kimsufi KS-5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU&lt;/td>
&lt;td>Intel Xeon E3-1270 v6, 4C/8T, 3.8 GHz base, 4.2 GHz turbo&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RAM&lt;/td>
&lt;td>32 GB DDR4 ECC 2400 MHz&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Storage&lt;/td>
&lt;td>2x Intel P3520 450 GB NVMe SSD&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>NIC&lt;/td>
&lt;td>2x Intel I210 gigabit&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The PCI devices looked like this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">00:00.0 Host bridge: Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (rev 05)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">00:14.0 USB controller: Intel Corporation 100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller (rev 31)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">00:17.0 SATA controller: Intel Corporation Q170/Q150/B150/H170/H110/Z170/CM236 Chipset SATA Controller [AHCI Mode] (rev 31)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">02:00.0 Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD (rev 02)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">03:00.0 Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD (rev 02)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">04:00.0 VGA compatible controller: Matrox Electronics Systems Ltd. MGA G200e [Pilot] ServerEngines (SEP1) (rev 05)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">05:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">06:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The SSDs were not new in power-on hours, but the wear was low and both drives had no media errors:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> nvme smart-log /dev/nvme0n1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">critical_warning : 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">temperature : 23 C
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">available_spare : 98%
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">percentage_used : 6%
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Data Units Read : 8.41 TB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Data Units Written : 95.99 TB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">power_cycles : 107
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">power_on_hours : 45628
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">unsafe_shutdowns : 4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">media_errors : 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">num_err_log_entries : 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="gp">#&lt;/span> nvme smart-log /dev/nvme1n1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">critical_warning : 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">temperature : 23 C
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">available_spare : 98%
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">percentage_used : 7%
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Data Units Read : 142.27 TB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Data Units Written : 112.67 TB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">power_cycles : 74
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">power_on_hours : 54153
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">unsafe_shutdowns : 5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">media_errors : 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">num_err_log_entries : 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For a cheap dedicated server, that is acceptable.&lt;/p>
&lt;h2 id="why-install-proxmox-manually">Why Install Proxmox Manually
&lt;/h2>&lt;p>OVHcloud can install an OS for you from the control panel. I did not use that path for Proxmox.&lt;/p>
&lt;p>The problem is storage layout. Proxmox works best when it owns the disks directly and can put ZFS on the raw devices. The OVHcloud installer tends to build layouts around &lt;code>mdadm&lt;/code> RAID and regular filesystems, or it uses ZFS in a way that treats VM/CT storage more like ordinary directories. That leaves some Proxmox storage features unavailable or awkward, especially thin-provisioned disks and the normal ZFS-backed workflow.&lt;/p>
&lt;p>So I installed Proxmox myself.&lt;/p>
&lt;h2 id="format-nvme-drives-as-4k-lba">Format NVMe Drives as 4K LBA
&lt;/h2>&lt;p>Before installing the OS, boot the server into OVHcloud rescue mode and check the NVMe namespace formats.&lt;/p>
&lt;p>My Intel P3520 drives supported both 512-byte and 4096-byte LBA formats:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> nvme id-ns -H /dev/nvme0n1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">LBA Format 0 : Metadata Size: 0 bytes - Data Size: 512 bytes - Relative Performance: 0x2 Good (in use)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">LBA Format 1 : Metadata Size: 8 bytes - Data Size: 512 bytes - Relative Performance: 0x2 Good
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">LBA Format 2 : Metadata Size: 16 bytes - Data Size: 512 bytes - Relative Performance: 0x2 Good
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">LBA Format 3 : Metadata Size: 0 bytes - Data Size: 4096 bytes - Relative Performance: 0 Best
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">LBA Format 4 : Metadata Size: 8 bytes - Data Size: 4096 bytes - Relative Performance: 0 Best
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">LBA Format 5 : Metadata Size: 64 bytes - Data Size: 4096 bytes - Relative Performance: 0 Best
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">LBA Format 6 : Metadata Size: 128 bytes - Data Size: 4096 bytes - Relative Performance: 0 Best
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The drives defaulted to 512-byte LBA. Since these are enterprise SSDs and 4K was available, I reformatted both namespaces to format 3: 4096-byte sectors with no metadata.&lt;/p>
&lt;p>This erases all data on the drive.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">nvme format /dev/nvme0n1 -l &lt;span class="m">3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">nvme format /dev/nvme1n1 -l &lt;span class="m">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After formatting, &lt;code>nvme list&lt;/code> should show &lt;code>4 KiB + 0 B&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">Node Model Namespace Usage Format FW Rev
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">/dev/nvme0n1 INTEL SSDPE2MX450G7 0x1 450.10 GB / 450.10 GB 4 KiB + 0 B MDV10290
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">/dev/nvme1n1 INTEL SSDPE2MX450G7 0x1 450.10 GB / 450.10 GB 4 KiB + 0 B MDV10290
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="open-the-ipmi-kvm">Open the IPMI KVM
&lt;/h2>&lt;p>In the OVHcloud console, open the IPMI KVM.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm.png"
width="2818"
height="1280"
srcset="https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_bc6d08e12944ca03.webp 480w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_3370d758ca2150cb.jpg 480w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_2fcc7ed7d723c278.webp 1024w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_14482574e10ea07e.jpg 1024w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_e9008af19524a2f7.webp 1536w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_fc9b5c865990ef69.jpg 1536w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_7e26a68501254018.webp 2048w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_29bd49e4b8c28216.jpg 2048w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_53cdbd2324a8def4.webp 2560w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_f474d9413e402a06.jpg 2560w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_3b94483f7d2e5b25.webp 2818w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ovhcloud-console-ipmi-kvm_hu_846985cefeb3b840.jpg 2818w"
loading="lazy"
alt="OVHcloud IPMI KVM console"
class="gallery-image"
data-flex-grow="220"
data-flex-basis="528px"
>
&lt;/p>
&lt;p>The KVM launches through an old Java JNLP applet.&lt;/p>
&lt;p>One annoying detail: the IP address you use to access the OVHcloud console should be the same public client IP used when opening the IPMI KVM. I hit a failure mode where the KVM was blocked because I accessed them through different egress IPs.&lt;/p>
&lt;p>The machine uses an Intel server board. The useful hotkeys are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>F2&lt;/code>: BIOS setup.&lt;/li>
&lt;li>&lt;code>F6&lt;/code>: one-time boot menu.&lt;/li>
&lt;/ul>
&lt;p>The boot logo confirms it is an Intel board:&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/boot-logo.png"
width="398"
height="298"
srcset="https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/boot-logo_hu_2c21f0be3bfabf33.webp 398w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/boot-logo_hu_7af6fe221da0429e.jpg 398w"
loading="lazy"
alt="Intel server board boot logo"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>You can mount a virtual ISO from the JViewer client:&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ipmi-mount-iso.png"
width="896"
height="264"
srcset="https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ipmi-mount-iso_hu_dc684660a0ba7c9c.webp 480w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ipmi-mount-iso_hu_7d179d60b7670a46.jpg 480w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ipmi-mount-iso_hu_4d7ad402416ab7f1.webp 896w, https://charlie0129.github.io/blog/p/kimsufi-ks5-proxmox/images/ipmi-mount-iso_hu_3791067def1c908a.jpg 896w"
loading="lazy"
alt="Mount ISO in IPMI KVM"
class="gallery-image"
data-flex-grow="339"
data-flex-basis="814px"
>
&lt;/p>
&lt;p>Then use &lt;code>F6&lt;/code> and choose the virtual CD-ROM:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Please select boot device:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UEFI IPv4: Intel I210 Network 00 at Baseboard
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UEFI IPv4: Intel I210 Network 00 at Baseboard 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UEFI IPv6: Intel I210 Network 00 at Baseboard
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UEFI IPv6: Intel I210 Network 00 at Baseboard 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Launch EFI Shell
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Enter Setup
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UEFI Virtual CDROM 1.00 # Choose this one!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UEFI Misc Device
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="do-not-stream-the-proxmox-iso-through-jviewer">Do Not Stream the Proxmox ISO Through JViewer
&lt;/h2>&lt;p>My first attempt was to mount the Proxmox installer ISO directly in JViewer and boot it.&lt;/p>
&lt;p>That technically works, but it was unusably slow. Even when I ran JViewer from an OVH server (which should have enough bandwidth), virtual media throughput was capped at around 64 KB/s. Installing Proxmox by streaming a full ISO through that path would take forever.&lt;/p>
&lt;p>The fix is to boot a tiny netboot image first.&lt;/p>
&lt;h2 id="boot-proxmox-through-netbootxyz">Boot Proxmox Through netboot.xyz
&lt;/h2>&lt;p>Download the netboot.xyz UEFI ISO, mount that ISO in JViewer, and boot it. The image is tiny, so the slow virtual media path is no longer a problem.&lt;/p>
&lt;p>In netboot.xyz, choose:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Linux Network Installs (64-bit)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Proxmox
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Proxmox VE Text Installer
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You can choose the debug installer if you want shell access between install stages. That is useful when something goes wrong.&lt;/p>
&lt;p>netboot.xyz downloads the Proxmox installer over the server&amp;rsquo;s own network connection and then boots it. OVHcloud provides DHCP even for dedicated servers, so the installer had network access without manual IP configuration.&lt;/p>
&lt;p>One caveat: after netboot.xyz hands off to the Proxmox installer, the installer does not use a serial console, so Serial over LAN will not work. You still need the JViewer window to see and control the installation.&lt;/p>
&lt;h2 id="install-proxmox-on-zfs-raid1">Install Proxmox on ZFS RAID1
&lt;/h2>&lt;p>In the Proxmox installer, choose ZFS RAID1 across both NVMe drives.&lt;/p>
&lt;p>I intentionally did not give the full SSDs to &lt;code>rpool&lt;/code>. The installer has an &lt;code>hdsize&lt;/code> option in advanced storage options. I set it to 128 GiB so the root pool would be a 2-way mirror, leaving the remaining space on both SSDs unused for a later scratch pool.&lt;/p>
&lt;p>This is the important part: decide this during installation. Growing ZFS into extra space is easy. Shrinking an existing ZFS pool is not.&lt;/p>
&lt;p>The final root layout on my machine:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">nvme0n1 419.2G disk
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-nvme0n1p1 1000K part
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-nvme0n1p2 1G part vfat
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-nvme0n1p3 128G part zfs_member
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">`-nvme0n1p4 290.2G part zfs_member
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="go">nvme1n1 419.2G disk
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-nvme1n1p1 1000K part
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-nvme1n1p2 1G part vfat
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-nvme1n1p3 128G part zfs_member
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">`-nvme1n1p4 290.2G part zfs_member
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>p3&lt;/code> on both drives is the mirrored root pool:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">pool: rpool
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">state: ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">config:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="go"> NAME STATE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rpool ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> mirror-0 ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> nvme-INTEL_SSDPE2MX450G7_CVPF71620037450RGN-part3 ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> nvme-INTEL_SSDPE2MX450G7_CVPF721600N5450RGN-part3 ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>p4&lt;/code> on both drives is the later scratch pool.&lt;/p>
&lt;h3 id="optional-partition-alignment-fix">Optional Partition Alignment Fix
&lt;/h3>&lt;p>The Proxmox installer did not give me a perfectly round 1 MiB-aligned final size for the ZFS partition (&lt;code>nvmeXn1p3&lt;/code>). If you care about this, fix it immediately after installation while the layout is still simple.&lt;/p>
&lt;p>The safe method is:&lt;/p>
&lt;ol>
&lt;li>Use &lt;code>fdisk /dev/nvmeXn1&lt;/code>&lt;/li>
&lt;li>List partitions with &lt;code>p&lt;/code> and note the start/end sector of part 3.&lt;/li>
&lt;li>Delete &lt;code>d&lt;/code> part 3&lt;/li>
&lt;li>Create a new part 3 with the same start sector and a new end sector. Increase the end sector a bit, align &lt;code>end sector - 1&lt;/code> to 1M (256 4k sectors).&lt;/li>
&lt;li>Do not wipe the ZFS signature.&lt;/li>
&lt;li>Write changes with &lt;code>w&lt;/code>.&lt;/li>
&lt;li>Ask ZFS to expand into the recreated partition.&lt;/li>
&lt;/ol>
&lt;p>Example:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">zpool online -e rpool &amp;lt;device&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Note that if you expanded the ZFS part too little (smaller than metaslabs size, typically 1GiB), ZFS may not actually expand the pool to use the empty space. This is fine.&lt;/p>
&lt;p>Find the exact &lt;code>&amp;lt;device&amp;gt;&lt;/code> with:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">zpool status -v rpool
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This is not required for a working system. I did it because I wanted the 128 GiB root partition to be exact and aligned. So later partition 4 will not be misaligned either.&lt;/p>
&lt;h2 id="fix-ovhcloud-boot-to-disk">Fix OVHcloud Boot-to-Disk
&lt;/h2>&lt;p>After a manual install, the server may not boot straight into Proxmox even though the installation succeeded.&lt;/p>
&lt;p>OVHcloud bare-metal boot is not just &amp;ldquo;BIOS loads local disk&amp;rdquo;. The normal path is roughly:&lt;/p>
&lt;ol>
&lt;li>The server PXE-boots from the public interface.&lt;/li>
&lt;li>OVHcloud DHCP gives the server its public IP and an iPXE loader.&lt;/li>
&lt;li>iPXE queries OVHcloud&amp;rsquo;s internal boot service.&lt;/li>
&lt;li>The boot service returns a script based on your configured boot mode.&lt;/li>
&lt;li>In boot-to-disk mode, iPXE uses &lt;code>sanboot&lt;/code> with an EFI bootloader path.&lt;/li>
&lt;/ol>
&lt;p>If you installed through the OVHcloud panel, OVHcloud knows the EFI bootloader path. If you installed manually, that path can be missing or wrong.&lt;/p>
&lt;p>When &lt;code>sanboot&lt;/code> fails, iPXE falls back to rEFInd. rEFInd scans the EFI system partitions and may choose the wrong bootloader. In my case it picked memtest first:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">rEFInd - Booting OS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Starting memtest86+x64.efi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Using load options &amp;#39;&amp;#39;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The rEFInd menu showed both memtest and systemd-boot:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Boot memtest86+x64.efi from 1021 MiB FAT volume
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Boot EFI\systemd\systemd-bootx64.efi from 1021 MiB FAT volume
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Boot memtest86+x64.efi from 1021 MiB FAT volume
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Boot EFI\systemd\systemd-bootx64.efi from 1021 MiB FAT volume
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For Proxmox installed with UEFI and root-on-ZFS, the bootloader is systemd-boot:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">\EFI\systemd\systemd-bootx64.efi
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Set that path through the OVHcloud CLI:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ovhcloud login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ovhcloud baremetal edit nsXXX.ip-X-X-X.eu --efi-bootloader-path &lt;span class="s1">&amp;#39;\EFI\systemd\systemd-bootx64.efi&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After that, boot-to-disk should go directly into Proxmox instead of falling through to rEFInd.&lt;/p>
&lt;h2 id="basic-post-install-zfs-settings">Basic Post-Install ZFS Settings
&lt;/h2>&lt;p>Enable TRIM on the root pool and set basic dataset properties:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">zpool &lt;span class="nb">set&lt;/span> &lt;span class="nv">autotrim&lt;/span>&lt;span class="o">=&lt;/span>on rpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">atime&lt;/span>&lt;span class="o">=&lt;/span>off rpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The default &amp;#39;on&amp;#39; is also lz4. Just set it to lz4 explicitly to be sure.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">compression&lt;/span>&lt;span class="o">=&lt;/span>lz4 rpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 128k is the default (this is good for HDDs). But 16-32k is better for NVMe array and workloads with small files, which is the case for most VMs/CTs and Proxmox OS. I am using 2-way mirror so I don&amp;#39;t need to worry about 4k ashift adding up.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">recordsize&lt;/span>&lt;span class="o">=&lt;/span>16k rpool
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>recordsize&lt;/code> affects file datasets, not zvol block devices. For VM disks backed by ZFS zvols, check the Proxmox storage block size instead. On new Proxmox installations the default is already 16K; older installations may still use 8K. You can check or change it in:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Datacenter -&amp;gt; Storage -&amp;gt; local-zfs -&amp;gt; Edit -&amp;gt; Block Size
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>I also use a few ZFS module options:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># You may remove zfs_arc_max set by the installer if you want to maximize ZFS ARC. Note that ARC may not shrink fast enough under memory pressure, so keep this in mind.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/modprobe.d/zfs.conf &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">options zfs zfs_txg_timeout=30
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">options zfs zfs_trim_txg_batch=128
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">options zfs zfs_dirty_data_sync_percent=80
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">options zfs zfs_delay_min_dirty_percent=95
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">update-initramfs -u -k all
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The intent:&lt;/p>
&lt;ul>
&lt;li>&lt;code>zfs_txg_timeout=30&lt;/code>: reduce idle write frequency.&lt;/li>
&lt;li>&lt;code>zfs_trim_txg_batch=128&lt;/code>: make TRIM batching less tiny for NVMe.&lt;/li>
&lt;li>&lt;code>zfs_dirty_data_sync_percent=80&lt;/code> and &lt;code>zfs_delay_min_dirty_percent=95&lt;/code>: delay throttling until the dirty-data situation is actually serious.&lt;/li>
&lt;/ul>
&lt;p>These are not universal defaults. They fit this small host because the workload is mostly personal VMs/CTs and scratch tasks, not a database with strict latency guarantees.&lt;/p>
&lt;h2 id="reduce-unnecessary-host-writes">Reduce Unnecessary Host Writes
&lt;/h2>&lt;p>I am not using Proxmox clustering on this server, so I disabled the cluster HA services:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">systemctl disable --now pve-ha-crm.service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl disable --now pve-ha-lrm.service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl disable --now corosync.service
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Limit persistent journald usage:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/.*SystemMaxUse.*/SystemMaxUse=128M/g&amp;#39;&lt;/span> /etc/systemd/journald.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl restart systemd-journald
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="configure-bbr">Configure BBR
&lt;/h2>&lt;p>Use BBR with &lt;code>fq&lt;/code>.&lt;/p>
&lt;p>On Debian/Proxmox, a later default sysctl file can set &lt;code>net.core.default_qdisc=fq_codel&lt;/code> after your file during boot. Use a late filename:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/sysctl.d/99-bbr.conf &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.core.default_qdisc = fq
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv4.tcp_congestion_control = bbr
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sysctl --system
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check it:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sysctl net.core.default_qdisc net.ipv4.tcp_congestion_control
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Expected:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">net.core.default_qdisc = fq
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">net.ipv4.tcp_congestion_control = bbr
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="configure-swap">Configure Swap
&lt;/h2>&lt;p>Always have swap when using ZFS, even with enough RAM. It gives the kernel somewhere to go if ARC does not shrink quickly enough under memory pressure, and it reduces the chance that the OOM killer targets useful applications.&lt;/p>
&lt;p>I use ZRAM swap:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> https://github.com/foundObjects/zram-swap.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> zram-swap
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./install.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf zram-swap
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/sysctl.d/zram.conf &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.swappiness = 180
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.watermark_boost_factor = 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.watermark_scale_factor = 125
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.page-cluster = 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sysctl --system
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You may use disk swap if you want.&lt;/p>
&lt;p>My current host has a 38.8 GiB ZRAM swap device:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">NAME TYPE SIZE USED PRIO
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">/dev/zram0 partition 38.8G 0B 15
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="ssh-and-serial-console">SSH and Serial Console
&lt;/h2>&lt;p>I like SSH sessions to die quickly when the client disappears unexpectedly:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ClientAliveInterval 60
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ClientAliveCountMax 3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Put that in &lt;code>sshd_config&lt;/code> or a file under &lt;code>sshd_config.d&lt;/code>, then restart SSH.&lt;/p>
&lt;p>For emergency access, enable OVHcloud serial-over-LAN by adding a serial console to the Proxmox kernel command line:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">additional_cmdline&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;console=tty0 console=ttyS0,115200n8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s2">&amp;#34;s|&lt;/span>$&lt;span class="s2">| &lt;/span>&lt;span class="nv">$additional_cmdline&lt;/span>&lt;span class="s2">|&amp;#34;&lt;/span> /etc/kernel/cmdline
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">proxmox-boot-tool refresh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>On my host, &lt;code>/etc/kernel/cmdline&lt;/code> now contains:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">root=ZFS=rpool/ROOT/pve-1 boot=zfs vmlinuz video=vesafb:ywrap,mtrr initrd=initrd.magic console=tty0 console=ttyS0,115200n8
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="scratch-storage">Scratch Storage
&lt;/h2>&lt;p>The root pool uses 128 GiB on each SSD. The remaining 290.2 GiB on each SSD is free for scratch storage.&lt;/p>
&lt;p>I considered two options:&lt;/p>
&lt;ul>
&lt;li>&lt;code>mdadm&lt;/code> RAID0 + LVM thin + ext4/XFS.&lt;/li>
&lt;li>ZFS striped pool.&lt;/li>
&lt;/ul>
&lt;p>The &lt;code>mdadm&lt;/code> option may win in some microbenchmarks, but the management cost is higher. I used a ZFS striped pool because it is simple and good enough.&lt;/p>
&lt;p>This pool has no redundancy. If either SSD fails, the scratch pool is gone. That is fine for my use case because it is scratch space.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">zpool create -f &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -o &lt;span class="nv">ashift&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">12&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -o &lt;span class="nv">autotrim&lt;/span>&lt;span class="o">=&lt;/span>on &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> scratchpool &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> /dev/disk/by-id/nvme-INTEL_SSDPE2MX450G7_CVPF721600N5450RGN-part4 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> /dev/disk/by-id/nvme-INTEL_SSDPE2MX450G7_CVPF71620037450RGN-part4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">recordsize&lt;/span>&lt;span class="o">=&lt;/span>16k scratchpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">atime&lt;/span>&lt;span class="o">=&lt;/span>off scratchpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">compression&lt;/span>&lt;span class="o">=&lt;/span>lz4 scratchpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">logbias&lt;/span>&lt;span class="o">=&lt;/span>throughput scratchpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs create scratchpool/unsafe
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">sync&lt;/span>&lt;span class="o">=&lt;/span>disabled scratchpool/unsafe
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zfs &lt;span class="nb">set&lt;/span> &lt;span class="nv">copies&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> scratchpool/unsafe
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Current state:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">pool: scratchpool
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">state: ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">config:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="go"> NAME STATE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> scratchpool ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> nvme-INTEL_SSDPE2MX450G7_CVPF721600N5450RGN-part4 ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> nvme-INTEL_SSDPE2MX450G7_CVPF71620037450RGN-part4 ONLINE
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Properties:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">NAME AVAIL MOUNTPOINT RECSIZE COMPRESS ATIME SYNC COPIES
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">scratchpool 562G /scratchpool 16K lz4 off standard 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">scratchpool/unsafe 562G /scratchpool/unsafe 16K lz4 off disabled 1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Use &lt;code>scratchpool/unsafe&lt;/code> only for data you can recreate. &lt;code>sync=disabled&lt;/code> lies to applications about sync writes.&lt;/p>
&lt;h3 id="mdadm--lvm-alternative">mdadm + LVM Alternative
&lt;/h3>&lt;p>If you want the non-ZFS scratch layout, create one extra partition on each SSD and build RAID0:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt install -y mdadm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mdadm --create /dev/md0 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --verbose &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --level&lt;span class="o">=&lt;/span>&lt;span class="m">0&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --raid-devices&lt;span class="o">=&lt;/span>&lt;span class="m">2&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --chunk&lt;span class="o">=&lt;/span>128K &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> /dev/disk/by-id/nvme-INTEL_SSDPE2MX450G7_CVPF721600N5450RGN-part4 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> /dev/disk/by-id/nvme-INTEL_SSDPE2MX450G7_CVPF71620037450RGN-part4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mdadm --detail --scan &amp;gt;&amp;gt; /etc/mdadm/mdadm.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">update-initramfs -u
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For alignment:&lt;/p>
&lt;ul>
&lt;li>mdadm chunk size: 128K. 128K is a good balance between performance and overhead for NVMe drives. If you want to maximize sequential performance, use larger values like 1M. But remember to align filesystem / LVM with this value.&lt;/li>
&lt;li>Data disks: 2.&lt;/li>
&lt;li>Stripe width: 256K.&lt;/li>
&lt;li>LVM data alignment: 1M is fine because it is a multiple of 256K.&lt;/li>
&lt;/ul>
&lt;p>NVMe (physical) → mdadm RAID0 → LVM PV → LVM LV / thin pool → filesystem&lt;/p>
&lt;p>When you create LVM on top of RAID-0, you must ensure:&lt;/p>
&lt;ul>
&lt;li>LVM PE size = multiple of mdadm chunk size&lt;/li>
&lt;li>LVM PV starts aligned to mdadm stripe boundary&lt;/li>
&lt;/ul>
&lt;p>Create the LVM layer:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The default dataalignment value is 1M, which is a multiple of 256K, so it&amp;#39;s fine.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pvcreate --dataalignment 1M /dev/md0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Default VG extent size is often 4M, which is multiples of 1M data alignment, &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># which is also multiples of 256K stripe width. Using the default value is fine.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">vgcreate --physicalextentsize 4M vg_scratch /dev/md0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Thin pool chunk size defines how much physical space is reserved when any &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># part of a chunk is touched. It does not defined write size. So it can be &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># smaller than 256k. The default 64k is fine. I will just match it with the &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># stripe width = 256k.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The default metadata size can be too big if you are using small driver &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># like 16G Optane drive. Calculate the desired metadata size (bytes)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># by `48 * (total size / chunk size)`.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lvcreate -l 100%FREE --thin vg_scratch/scratch_thin --chunksize 256k --poolmetadatasize 512M
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>When formatting a filesystem inside a VM or manually on the host, pass the stripe geometry:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ext4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># stride = mdadm chunk size / filesystem block size = 128k / 4k = 32&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># stripe-width = stride * disks = 32 * 2 = 64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkfs.ext4 -b &lt;span class="m">4096&lt;/span> -E &lt;span class="nv">stride&lt;/span>&lt;span class="o">=&lt;/span>32,stripe-width&lt;span class="o">=&lt;/span>&lt;span class="m">64&lt;/span> /dev/vg_scratch/lv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># XFS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># su = mdadm chunk size = 128k&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># sw = data disks = 2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkfs.xfs -d &lt;span class="nv">su&lt;/span>&lt;span class="o">=&lt;/span>128k,sw&lt;span class="o">=&lt;/span>&lt;span class="m">2&lt;/span> /dev/vg_scratch/lv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Fast but unsafe mount options for scratch data:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ext4: noatime,nodiratime,nobarrier,data=writeback,commit=60
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">xfs: noatime,nodiratime,logbufs=8,logbsize=256k
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For CTs, Proxmox does not make it convenient to set filesystem mount options on a normal CT mountpoint. If you need custom options, create the LV manually, mount it on the host, and bind-mount it into the CT:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">lvcreate -V 16G -T vg_scratch/scratch_thin -n scratch_test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkfs.xfs -d &lt;span class="nv">su&lt;/span>&lt;span class="o">=&lt;/span>128k,sw&lt;span class="o">=&lt;/span>&lt;span class="m">2&lt;/span> /dev/vg_scratch/scratch_test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p /mnt/scratch_test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mount -o noatime,nodiratime,logbufs&lt;span class="o">=&lt;/span>8,logbsize&lt;span class="o">=&lt;/span>256k /dev/vg_scratch/scratch_test /mnt/scratch_test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod &lt;span class="m">777&lt;/span> /mnt/scratch_test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pct &lt;span class="nb">set&lt;/span> &lt;span class="m">199&lt;/span> -mp0 /mnt/scratch_test,mp&lt;span class="o">=&lt;/span>/mnt/scratch_test
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If your workload is random-write heavy and can fit on one SSD, skip RAID0 and LVM entirely. A single raw disk or single partition is often better for small random writes than a layered RAID0 + LVM-thin setup.&lt;/p>
&lt;h3 id="using-scratch-storage-in-vms-and-cts">Using Scratch Storage in VMs and CTs
&lt;/h3>&lt;p>For VMs, add a virtual disk on the scratch pool. Enable discard and SSD emulation on the virtual disk. Inside the guest, enable periodic TRIM. When formatting the disk, make sure the filesystem is aligned to the underlying ZFS volsize or LVM stripe geometry. For example, if underlying ZFS volsize is 16K, use &lt;code>mkfs.ext4 -O bigalloc -C 16384 /dev/yourdisk&lt;/code>. If the underlying storage is LVM+RAID, use the correct &lt;code>stride&lt;/code> and &lt;code>stripe-width&lt;/code> options when formatting.&lt;/p>
&lt;p>For CTs, add a mountpoint on the scratch pool. ZFS mountpoints are automatically aligned. For LVM+RAID, make sure the filesystem is aligned to the underlying geometry. TRIM is handled by the host for ZFS mountpoints. For LVM+RAID, run &lt;code>pct fstrim&lt;/code> on the CT.&lt;/p>
&lt;p>To use the scratch pool for temporary data, you can use bind-mounts to mount common scratch directories to scratch disk mountpoints. This avoids changing the config for each applications such as Docker, containerd and etc.&lt;/p>
&lt;p>Suppose the scratch disk is mounted at &lt;code>/mnt/unsafe&lt;/code>, and you want to use it for Docker, containerd, and ~/.cache. Write the following lines in &lt;code>/etc/fstab&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">/&lt;/span>&lt;span class="n">mnt&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">unsafe&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">docker&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">lib&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">docker&lt;/span> &lt;span class="n">none&lt;/span> &lt;span class="n">rbind&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">/&lt;/span>&lt;span class="n">mnt&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">unsafe&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">containerd&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">lib&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">containerd&lt;/span> &lt;span class="n">none&lt;/span> &lt;span class="n">rbind&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">/&lt;/span>&lt;span class="n">mnt&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">unsafe&lt;/span>&lt;span class="o">/.&lt;/span>&lt;span class="n">cache&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="o">/.&lt;/span>&lt;span class="n">cache&lt;/span> &lt;span class="n">none&lt;/span> &lt;span class="n">rbind&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir -p /mnt/unsafe/&lt;span class="o">{&lt;/span>docker,containerd,.cache&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf /var/lib/docker /var/lib/containerd /root/.cache
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p /var/lib/docker /var/lib/containerd /root/.cache
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mount -av
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="trim">TRIM
&lt;/h2>&lt;p>For ZFS pools:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">zpool &lt;span class="nb">set&lt;/span> &lt;span class="nv">autotrim&lt;/span>&lt;span class="o">=&lt;/span>on rpool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zpool &lt;span class="nb">set&lt;/span> &lt;span class="nv">autotrim&lt;/span>&lt;span class="o">=&lt;/span>on scratchpool
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For VMs, enable discard and SSD emulation on virtual disks, then enable periodic TRIM inside the guest.&lt;/p>
&lt;p>For CTs backed by ZFS mountpoints, the host handles it. For CTs backed by other storage such as LVM thin, run &lt;code>pct fstrim&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &amp;gt;/usr/local/bin/trimcts.sh &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">for id in $(pct list | awk &amp;#39;$2 == &amp;#34;running&amp;#34; {print $1}&amp;#39;); do
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> echo &amp;#34;==&amp;gt; Trimming CT $id&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> pct fstrim &amp;#34;$id&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">done
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x /usr/local/bin/trimcts.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then run it from cron or a systemd timer. For example, weekly from cron:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">0&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">7&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">usr&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">local&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">trimcts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sh&lt;/span> &lt;span class="o">&amp;gt;/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nb">log&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">trimcts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">log&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="proxmox-networking-on-a-single-public-ip">Proxmox Networking on a Single Public IP
&lt;/h2>&lt;p>Proxmox creates a bridge by default and attaches the physical NIC to it. That is usually the right setup when the server is in your own LAN.&lt;/p>
&lt;p>It is not right for this Kimsufi server.&lt;/p>
&lt;p>The server has one usable public IPv4 address and one &lt;code>/128&lt;/code> IPv6 address. It does not have a routed public subnet for VMs and CTs. If VMs are bridged directly to the public interface, they do not get usable public addresses.&lt;/p>
&lt;p>So I do this instead:&lt;/p>
&lt;ul>
&lt;li>Put the public IPv4 and public IPv6 directly on &lt;code>eno1&lt;/code>, not &lt;code>vmbr0&lt;/code>. By default, Proxmox puts the public IP on the &lt;code>vmbr0&lt;/code> bridge, which is not what we want. You should move it to the physical interface.&lt;/li>
&lt;li>Create &lt;code>vmbr0&lt;/code> as an internal bridge with no physical ports.&lt;/li>
&lt;li>Put VMs and CTs on &lt;code>vmbr0&lt;/code>.&lt;/li>
&lt;li>NAT outbound traffic from &lt;code>vmbr0&lt;/code> to &lt;code>eno1&lt;/code>.&lt;/li>
&lt;li>Port-forward selected inbound ports to service CTs.&lt;/li>
&lt;/ul>
&lt;p>Current &lt;code>/etc/network/interfaces&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">auto lo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface lo inet loopback
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">auto eno1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface eno1 inet static
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> address &amp;lt;public-ipv4&amp;gt;/24
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> gateway &amp;lt;public-ipv4-gateway&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface eno1 inet6 static
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> address &amp;lt;public-ipv6&amp;gt;/128
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> gateway &amp;lt;public-ipv6-gateway&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface eno2 inet manual
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">auto vmbr0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface vmbr0 inet static
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> address 10.187.54.1/24
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bridge-ports none
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bridge-stp off
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bridge-fd 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface vmbr0 inet6 static
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> address fc10:187:54::1/64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">source /etc/network/interfaces.d/*
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Enable forwarding:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/sysctl.d/99-forward.conf &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv4.ip_forward = 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv6.conf.all.forwarding = 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sysctl --system
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>About NAT66: it is usually the wrong IPv6 design. If you have a routed IPv6 prefix, route it to your VM bridge. On this server I only have one &lt;code>/128&lt;/code>, so NAT66 is the practical workaround if VMs/CTs need outbound IPv6.&lt;/p>
&lt;h2 id="nat-and-port-forwarding">NAT and Port Forwarding
&lt;/h2>&lt;p>NAT rewrites packet addresses as traffic crosses the host.&lt;/p>
&lt;p>For outbound IPv4 NAT, a CT sends a packet like:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">src=10.187.54.100:51514 dst=1.1.1.1:443
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After MASQUERADE on the host:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">src=&amp;lt;public-ipv4&amp;gt;:51514 dst=1.1.1.1:443
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Linux conntrack records that translation so the reply can be translated back and forwarded to the CT.&lt;/p>
&lt;p>There is a Proxmox-specific caveat when the firewall is enabled on a VM or CT network interface (&lt;code>firewall=1&lt;/code>). Proxmox places that interface behind an &lt;code>fwbr*&lt;/code> firewall bridge. With bridge netfilter enabled, conntrack can record the connection while it crosses the firewall bridge, before it reaches the host&amp;rsquo;s normal NAT path. The later &lt;code>FORWARD&lt;/code> rule can accept the packet, but &lt;code>POSTROUTING&lt;/code> may reuse the already tracked non-NATed connection instead of attaching MASQUERADE or SNAT. The packet then leaves the public interface with its private source address and never receives a reply.&lt;/p>
&lt;p>Put the firewall-bridge tracking in a separate conntrack zone by adding these hooks to the &lt;code>vmbr0&lt;/code> stanza in &lt;code>/etc/network/interfaces&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"> post-up iptables -t raw -C PREROUTING -i fwbr+ -j CT --zone 1 2&amp;gt;/dev/null || iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1 2&amp;gt;/dev/null || true
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> post-up ip6tables -t raw -C PREROUTING -i fwbr+ -j CT --zone 1 2&amp;gt;/dev/null || ip6tables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> post-down ip6tables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1 2&amp;gt;/dev/null || true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The &lt;code>-C&lt;/code>/&lt;code>||&lt;/code> guard matters on a dual-stack &lt;code>vmbr0&lt;/code>: ifupdown2 can run the hook once for each address family.&lt;/p>
&lt;p>The IPv4 rules cover MASQUERADE/SNAT, while the IPv6 rules are also needed when using NAT66. A characteristic symptom is that NAT works for a guest without the Proxmox interface firewall but fails for an otherwise identical firewall-enabled guest. In conntrack output, the broken flow&amp;rsquo;s reply destination remains the guest&amp;rsquo;s private address instead of the host&amp;rsquo;s public address. Allowing all &lt;code>OUT&lt;/code> and &lt;code>FORWARD&lt;/code> traffic does not fix it because this is a conntrack/NAT-path problem, not a filter-policy rejection.&lt;/p>
&lt;p>Inbound port forwarding is DNAT. A client connects to the public server:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">src=198.51.100.20:41000 dst=&amp;lt;public-ipv4&amp;gt;:443
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The host rewrites the destination before routing:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">src=198.51.100.20:41000 dst=10.187.54.3:443
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>I use my own small tool, &lt;code>iptfwd&lt;/code>, to manage these rules from a config file: &lt;a class="link" href="https://github.com/charlie0129/iptfwd" target="_blank" rel="noopener"
>charlie0129/iptfwd&lt;/a>.&lt;/p>
&lt;p>Example config for this topology:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">defaults&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">public_iface&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">eno1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">private_iface&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">vmbr0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">manage_filter&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enable_ip_forwarding&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">nat&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">intranet-v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">source&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">10.187.54.0&lt;/span>&lt;span class="l">/24&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">masquerade&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">intranet-v6&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">source&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">fc10:187:54::/64&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">snat&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">to_source&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">&amp;lt;public-ipv6&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">rules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service-http-v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">proto&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tcp&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">public_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">10.187.54.3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service-https-v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">proto&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tcp&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">public_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">443&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">10.187.54.3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">443&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service-http-v6&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">proto&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tcp&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">public_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">fc10:187:54::3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service-https-v6&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">proto&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tcp&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">public_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">443&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">fc10:187:54::3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">443&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For IPv4, MASQUERADE is convenient. For NAT66, I prefer explicit SNAT to the public IPv6 address. IPv6 interfaces can have multiple addresses, and explicit SNAT makes the translated source predictable.&lt;/p>
&lt;p>If you do not want to use &lt;code>iptfwd&lt;/code>, the equivalent basic iptables rules are:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">iptables -t nat -A POSTROUTING -s 10.187.54.0/24 -o eno1 -j MASQUERADE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iptables -A FORWARD -i vmbr0 -o eno1 -j ACCEPT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iptables -A FORWARD -i eno1 -o vmbr0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">public_ipv6&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;YOUR_PUBLIC_IPV6&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ip6tables -t nat -A POSTROUTING -s fc10:187:54::/64 -o eno1 -j SNAT --to-source &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$public_ipv6&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ip6tables -A FORWARD -i vmbr0 -o eno1 -j ACCEPT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ip6tables -A FORWARD -i eno1 -o vmbr0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt install iptables-persistent
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">netfilter-persistent save
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Before setting up DHCP, test NAT manually:&lt;/p>
&lt;ol>
&lt;li>Create a CT on &lt;code>vmbr0&lt;/code>.&lt;/li>
&lt;li>Set IPv4 to something like &lt;code>10.187.54.100/24&lt;/code>.&lt;/li>
&lt;li>Set IPv4 gateway to &lt;code>10.187.54.1&lt;/code>.&lt;/li>
&lt;li>Set IPv6 to something like &lt;code>fc10:187:54::100/64&lt;/code>.&lt;/li>
&lt;li>Set IPv6 gateway to &lt;code>fc10:187:54::1&lt;/code>.&lt;/li>
&lt;li>Test:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ping www.google.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ping6 www.google.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If that fails, debug NAT before adding DHCP.&lt;/p>
&lt;h2 id="dhcp-and-dns-with-pi-hole">DHCP and DNS with Pi-hole
&lt;/h2>&lt;p>I use Pi-hole as DHCP and DNS for the private bridge because it has a nice web UI and makes static leases easy.&lt;/p>
&lt;p>Create a small Alpine CT:&lt;/p>
&lt;ul>
&lt;li>Bridge: &lt;code>vmbr0&lt;/code>.&lt;/li>
&lt;li>IPv4: &lt;code>10.187.54.2/24&lt;/code>.&lt;/li>
&lt;li>IPv4 gateway: &lt;code>10.187.54.1&lt;/code>.&lt;/li>
&lt;li>IPv6: &lt;code>fc10:187:54::2/64&lt;/code>.&lt;/li>
&lt;li>IPv6 gateway: &lt;code>fc10:187:54::1&lt;/code>.&lt;/li>
&lt;li>CPU: 1 core is enough.&lt;/li>
&lt;li>RAM: 128 MB is enough.&lt;/li>
&lt;li>Disk: 512 MB is enough.&lt;/li>
&lt;/ul>
&lt;p>Install Pi-hole:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apk update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apk add curl bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -sSL https://install.pi-hole.net &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># You may disable query logs to reduce writes to the database.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you forget the web password:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pihole setpassword
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Reduce query-log database writes:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pihole-FTL --config database.DBinterval &lt;span class="m">1800&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In the Pi-hole UI:&lt;/p>
&lt;ul>
&lt;li>Enable DHCP for &lt;code>10.187.54.0/24&lt;/code>.&lt;/li>
&lt;li>Set the router/gateway to &lt;code>10.187.54.1&lt;/code>.&lt;/li>
&lt;li>Configure DNS as desired.&lt;/li>
&lt;li>Disable NTP sync under &lt;code>Settings -&amp;gt; All Settings -&amp;gt; Network Time Sync&lt;/code>. Uncheck &lt;code>ntp.ipv4/ipv6/sync.active&lt;/code>. An unprivileged CT cannot set system time anyway.&lt;/li>
&lt;/ul>
&lt;p>Alpine log cleanup I use for small CTs:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/periodic/hourly/logtruncate &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">LOG_DIR=&amp;#34;/var/log&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">MAX_SIZE=$((4 * 1024 * 1024))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">find &amp;#34;$LOG_DIR&amp;#34; -type f ! -name &amp;#39;*.0&amp;#39; | while read -r file; do
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> size=$(stat -c %s &amp;#34;$file&amp;#34; 2&amp;gt;/dev/null || stat -f %z &amp;#34;$file&amp;#34; 2&amp;gt;/dev/null || echo 0)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> if [ &amp;#34;$size&amp;#34; -gt &amp;#34;$MAX_SIZE&amp;#34; ]; then
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> logger -t logrotate &amp;#34;Rotating $file ($size bytes)&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> cp &amp;#34;$file&amp;#34; &amp;#34;$file.0&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> truncate -s 0 &amp;#34;$file&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> logger -t logrotate &amp;#34;Rotated $file&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> fi
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">done
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x /etc/periodic/hourly/logtruncate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/^SYSLOGD_OPTS=.*/SYSLOGD_OPTS=&amp;#34;-t -s 0&amp;#34;/&amp;#39;&lt;/span> /etc/conf.d/syslog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/periodic/daily/apkcacheclean &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">find /var/cache/apk -type f -name &amp;#39;*.apk&amp;#39; -mtime +7 -delete
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">find /var/cache/apk -type f -name &amp;#39;*.tar.gz&amp;#39; -mtime +90 -delete
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x /etc/periodic/daily/apkcacheclean
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="host-monitoring">Host Monitoring
&lt;/h2>&lt;p>I keep monitoring tools in Podman containers instead of installing everything directly on the Proxmox host.&lt;/p>
&lt;p>Install Podman:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># So Intel PCM can work.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> msr &amp;gt;&amp;gt; /etc/modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">modprobe msr
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install podman to run monitoring tools in containers. Do not include any unnecessary packages to keep the host clean and minimal. Remember to install aardvark-dns to make container DNS work.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt install --no-install-recommends podman aardvark-dns
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>On current Debian/Proxmox, Podman should use &lt;code>overlay&lt;/code> storage on ZFS:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">podman info --format &lt;span class="s1">&amp;#39;{{.Store.GraphDriverName}}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Expected:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">overlay
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Make &lt;code>podman-restart.service&lt;/code> also handle containers with &lt;code>restart-policy=unless-stopped&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir -p /etc/systemd/system/podman-restart.service.d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/systemd/system/podman-restart.service.d/override.conf &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">[Service]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always --filter restart-policy=unless-stopped
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">ExecStop=/usr/bin/podman $LOGGING stop --all --filter restart-policy=always --filter restart-policy=unless-stopped
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl daemon-reload
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl &lt;span class="nb">enable&lt;/span> podman-restart.service
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Enable the Podman socket and Docker-compatible CLI:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">systemctl &lt;span class="nb">enable&lt;/span> --now podman.socket
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt install --no-install-recommends docker-cli docker-compose
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context create podman --docker &lt;span class="s2">&amp;#34;host=unix:///run/podman/podman.sock&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context use podman
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>An example monitoring compose file:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">vmagent&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">vmagent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">victoriametrics/vmagent:v1.144.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mem_limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">512M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GOMEMLIMIT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">900MiB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">httpListenAddr=0.0.0.0:8428&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">cacheExpireDuration=5m&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">promscrape.config=/etc/vmagent/scrape.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.url=https://XXX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Your VictoriaMetrics remote write endpoint&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.basicAuth.username=XXX&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.basicAuth.password=XXX&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.maxDiskUsagePerURL=128MiB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.tmpDataPath=/var/vmagent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.vmProtoCompressLevel=3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.flushInterval=66s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">remoteWrite.label=instance=XXX&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">extra_hosts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;host.docker.internal:host-gateway&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./vmagent/config:/etc/vmagent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./vmagent/data:/var/vmagent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">smartctlexporter&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">smartctlexporter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">prometheuscommunity/smartctl-exporter:v0.14.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mem_limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">64M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">user&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GOMEMLIMIT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">110MiB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">privileged&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">nodeexporter&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nodeexporter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">prom/node-exporter:v1.11.1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mem_limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">64M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GOMEMLIMIT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">110MiB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">privileged&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">path.rootfs=/host&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">web.listen-address=:9100&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">network_mode&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">host&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pid&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">host&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/:/host:ro,rslave&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">podmanexporter&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">podmanexporter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">quay.io/navidys/prometheus-podman-exporter:v1.21.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mem_limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">64M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">user&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">privileged&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GOMEMLIMIT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">110MiB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CONTAINER_HOST&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unix:///run/podman/podman.sock&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">/run/podman/podman.sock:/run/podman/podman.sock&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">web.listen-address=:9882&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cgroupexporter&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cgroupexporter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ghcr.io/arianvp/cgroup-exporter:0.3.3-amd64&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mem_limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">64M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GOMEMLIMIT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">110MiB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">/sys/fs/cgroup:/sys/fs/cgroup:ro&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- -&lt;span class="l">listen-address=:3232&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pcm&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pcm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">opcm/pcm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mem_limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">96M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">privileged&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="service-ct">Service CT
&lt;/h2>&lt;p>I keep public-facing services in a separate CT and forward only selected ports from the host.&lt;/p>
&lt;p>Create an unprivileged Alpine CT on &lt;code>vmbr0&lt;/code>, then:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apk add openssh openssh-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-update add sshd default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service sshd start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Add this to &lt;code>/etc/ssh/sshd_config&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">PermitRootLogin yes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ClientAliveInterval 60
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ClientAliveCountMax 3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install Podman inside the CT:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &amp;gt;/usr/local/bin/enablecgroup2nesting &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> echo &amp;#34;Enabling cgroup v2 nesting&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> mkdir -p /sys/fs/cgroup/init
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> xargs -rn1 &amp;lt; /sys/fs/cgroup/cgroup.procs &amp;gt; /sys/fs/cgroup/init/cgroup.procs || :
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> sed -e &amp;#39;s/ / +/g&amp;#39; -e &amp;#39;s/^/+/&amp;#39; &amp;lt; /sys/fs/cgroup/cgroup.controllers \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;gt; /sys/fs/cgroup/cgroup.subtree_control
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">fi
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x /usr/local/bin/enablecgroup2nesting
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/init.d/cgroup2nesting &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">#!/sbin/openrc-run
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">description=&amp;#34;Enable nesting of cgroup2.&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">depend()
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> keyword -docker -podman -prefix -systemd-nspawn -vserver -wsl
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> after sysfs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">start()
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> ebegin &amp;#34;Enabling cgroup v2 nesting&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> /usr/local/bin/enablecgroup2nesting
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> eend $?
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x /etc/init.d/cgroup2nesting
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-update add cgroup2nesting default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service cgroup2nesting start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apk add podman iptables
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/^#log_size_max =.*/log_size_max = 1048576/&amp;#39;&lt;/span> /etc/containers/containers.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-update add podman
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service podman start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apk add docker-cli docker-cli-compose
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context create podman --docker &lt;span class="s2">&amp;#34;host=unix:///run/podman/podman.sock&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context use podman
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Also check out the Pi-Hole setup section for Alpine log cleanup and other small CT optimizations. You must do this because Alpine does not have log rotation by default. You disk will fill up if you do not manage logs.&lt;/p>
&lt;p>Then run Caddy or Traefik in that CT for reverse proxy and TLS termination. Forward 80 and 443 from the host to the service CT with &lt;code>iptfwd&lt;/code> or iptables.&lt;/p>
&lt;p>If you use a privileged CT with Podman over ZFS, you may need an overlay mount helper:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &amp;gt;/usr/local/bin/zfsoverlaymount &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">/bin/mount -t overlay overlay &amp;#34;$@&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x /usr/local/bin/zfsoverlaymount
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt;/etc/containers/storage.conf &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">[storage.options]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">mount_program = &amp;#34;/usr/local/bin/zfsoverlaymount&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>I prefer unprivileged CTs unless there is a specific reason not to use them.&lt;/p>
&lt;h2 id="smtp-notifications">SMTP Notifications
&lt;/h2>&lt;p>By default, Proxmox may send mail directly and anonymously to the email address you entered during installation. Some providers reject that mail.&lt;/p>
&lt;p>Configure a real SMTP target instead:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Datacenter -&amp;gt; Notifications -&amp;gt; Notification Targets -&amp;gt; Add -&amp;gt; SMTP
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="final-state">Final State
&lt;/h2>&lt;p>The host ended up with:&lt;/p>
&lt;ul>
&lt;li>Proxmox VE 9.2.&lt;/li>
&lt;li>Mirrored ZFS root pool on 128 GiB from each NVMe SSD.&lt;/li>
&lt;li>Striped ZFS scratch pool on the remaining SSD space.&lt;/li>
&lt;li>4K LBA on both NVMe drives.&lt;/li>
&lt;li>Private-only &lt;code>vmbr0&lt;/code> for VMs and CTs.&lt;/li>
&lt;li>IPv4 NAT and IPv6 NAT66 from &lt;code>vmbr0&lt;/code> to &lt;code>eno1&lt;/code>.&lt;/li>
&lt;li>80/443 forwarded to a service CT.&lt;/li>
&lt;li>Pi-hole handling private DHCP and DNS.&lt;/li>
&lt;li>Podman-based host monitoring.&lt;/li>
&lt;/ul>
&lt;p>For 19.90 USD/month, this is a surprisingly useful little virtualization box.&lt;/p></description></item><item><title>Proxmox VE Shared GPU Installation Guide</title><link>https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/</link><pubDate>Sat, 16 Aug 2025 20:49:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Most schools and universities in China have a limited number of GPU servers (often only one or two, or even none). This makes the management of GPU development machines quite different than corporation which have virtually unlimited GPU servers.&lt;/p>
&lt;p>Let&amp;rsquo;s consider a normal lab environment with 12 students, each needing a GPU server for their projects, and we have one GPU server with 8 GPUs available.&lt;/p>
&lt;ul>
&lt;li>If we give each student a dedicated GPU server, we would need 12 servers, this is impractical and costly.&lt;/li>
&lt;li>If we let all the students share a single GPU server (all students have access to all resources), it would be very difficult to manage, as each student would need to install their own software and dependencies, leading to conflicts. Soon the GPU server will become a hot mess. Making each student use a different user account would not solve the problem, as they would still share the same OS and software environment, and will potentially break each other&amp;rsquo;s environment.&lt;/li>
&lt;li>If we make use of IOMMU and PCIe passthrough, we can assign a virtual machine with one dedicated passthrough&amp;rsquo;d GPU to each student. This approach has almost no interference between students, as each student has their own OS and software environment, even with it&amp;rsquo;s own GPU. However, this leads to a lot of wasted resources, because most of the time the GPU is idle, and each student will only have access to one GPU at a time, even if all of the GPUs are idle.&lt;/li>
&lt;li>If we use GPU virtualization, we can assign a virtual machine with a shared GPU to each student. This way, each student can use the GPU resources as needed, and the GPU can be shared among multiple students. This approach is more efficient and cost-effective, as it allows for better resource utilization. However, it requires a GPU that supports virtualization, such as NVIDIA&amp;rsquo;s vGPU or AMD&amp;rsquo;s MxGPU. Most consumer GPUs do not support virtualization, so this approach is not feasible for most schools and universities.&lt;/li>
&lt;/ul>
&lt;p>This is where containerization comes in. By using containerization, we can create a shared GPU development server that runs a container for each student. Each container can have its own software environment, and all (or some of) of the GPUs can be shared among multiple containers. This approach is more efficient and cost-effective, as it allows for better resource utilization, and does not require a GPU that supports virtualization. However, it should be noted that this approach is not as isolated as virtual machines, as all containers share the same kernel and GPU resources. Therefore, it is important to ensure that the containers are properly configured, and that the students are aware of the limitations and potential issues that may arise from sharing the same GPU resources (for example, if one student runs a GPU-intensive task, it may affect the performance of other students&amp;rsquo; containers).&lt;/p>
&lt;p>We will be using the CT (LXC Containers) in Proxmox VE to achieve this. Why not use Docker? Because Docker is meant for running applications, not for running full Linux distros. Although you can use something like sysbox to run a full Linux distro in Docker, i would still prefer to use LXC Containers, as they are built for this purpose and are already built into Proxmox VE, making it easier to manage and deploy.&lt;/p>
&lt;h2 id="proxmox-ve-installation">Proxmox VE Installation
&lt;/h2>&lt;p>Download the latest Proxmox VE ISO from the &lt;a class="link" href="https://www.proxmox.com/en/downloads" target="_blank" rel="noopener"
>official website&lt;/a>. At the time of writing, the latest version is Proxmox VE 9.0. Use whatever method you prefer to install Proxmox VE, such as using a USB drive.&lt;/p>
&lt;p>I will use IPMI of the server to install Proxmox VE, as it is the most convenient method for me. You can also use a monitor and keyboard to install Proxmox VE if you prefer.&lt;/p>
&lt;p>Mount the Proxmox VE installation ISO as virtual media so we can boot into.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188.png"
width="2560"
height="1600"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_fc3819d67899781d.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_9d957dfbafccb232.jpg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_f638cbe753e8df54.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_96242ea226abd1df.jpg 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_bb01bd1f795bfa17.webp 1536w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_a80d7d3f160c23f3.jpg 1536w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_2406379d72ef1f1b.webp 2048w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_d73dbdf56d3ff481.jpg 2048w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_6f21ae6ec6e3868b.webp 2560w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250816213214188_hu_7b94c32487992fd7.jpg 2560w"
loading="lazy"
alt="image-20250816213214188"
class="gallery-image"
data-flex-grow="160"
data-flex-basis="384px"
>
&lt;/p>
&lt;p>Now reboot the server into the virtual media. Supermicro motherboards lets you invode the boot menu using F11. Your motherboard may have a different shortcut.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm1.jpeg"
width="1024"
height="768"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm1_hu_8ba3cfc0a697fe04.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm1_hu_9c46554a70e3f68b.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm1_hu_c7fc7a1ae7b4113d.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm1_hu_1b4cb758cf2fff8a.jpeg 1024w"
loading="lazy"
alt="kvm1"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>Choose our CDROM virtual media.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm2.jpeg"
width="800"
height="600"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm2_hu_9017089d67a44bb4.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm2_hu_e0cae96a987e1778.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm2_hu_209d9180b54cd190.webp 800w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm2_hu_bb993296d9dc2ad2.jpeg 800w"
loading="lazy"
alt="kvm2"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>Boot into the Proxmox install menu (I prefer terminal UI over GUI ones).&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm5.jpeg"
width="1024"
height="768"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm5_hu_bf96deb29f237104.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm5_hu_fbc78cf467a77c00.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm5_hu_9225fe3a3bdcfe99.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm5_hu_d4626b04625ebb28.jpeg 1024w"
loading="lazy"
alt="kvm5"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>After you accepted the license, you will need to choose the target installtion disk. You should choose the boot SSDs on your server, not data drives. The drive in picture is 3*1.92TB SSDs in RAID 5.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm6.jpeg"
width="1280"
height="1024"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm6_hu_dbb426d48c671cc8.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm6_hu_6064335627e5139e.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm6_hu_7f1d8d51d68f2aa4.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm6_hu_208bd54bae9f396f.jpeg 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm6_hu_736c681430510399.webp 1280w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm6_hu_e7bd64cc7009b50f.jpeg 1280w"
loading="lazy"
alt="kvm6"
class="gallery-image"
data-flex-grow="125"
data-flex-basis="300px"
>
&lt;/p>
&lt;p>Toggle &lt;code>Advanced options&lt;/code>. Keep ext4, we will not be using ZFS. I know ZFS has CoW, snapshotting, compressing, checksums, and a ton of other features. But we will not use ZFS, because it will potentially cause some problems, e.g., high IO load when using RAID-Z zvols, extremely slow container start times when using ZFS storage driver in Docker, SSD write amplifications, low SSD random read/write performance, ZFS ARC not being given back to the OS as fast as needed on high memory pressure systems leading to OOM, and a ton of other issues that I previously encountered. To save me some trouble, I will use the battle-tested ext4.&lt;/p>
&lt;ul>
&lt;li>Total size: keep as-is&lt;/li>
&lt;li>Swap size: 0, we don&amp;rsquo;t need it, our memory is large enought (1TiB) and we will later use ZRAM as swap&lt;/li>
&lt;li>Maximum root volume size: 100 (GiB), make it slightly larger so we can install things into the root volume, but not too large to occupy the data volume space.&lt;/li>
&lt;/ul>
&lt;p>Other options can be left empty.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm7.jpeg"
width="1280"
height="1024"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm7_hu_2ff35877d03636be.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm7_hu_378d3db78e6dd1f1.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm7_hu_dc35289a6d315b63.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm7_hu_5a57bbb82297cfbc.jpeg 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm7_hu_3de3e3022862cd88.webp 1280w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm7_hu_9f57d55dfbd59de8.jpeg 1280w"
loading="lazy"
alt="kvm7"
class="gallery-image"
data-flex-grow="125"
data-flex-basis="300px"
>
&lt;/p>
&lt;p>Other install steps (Keyboard setup, root password) can proceed as you normally would do. Remember to set a complex root password.&lt;/p>
&lt;p>Regarding to IP addresses:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>If you are going to use a static IP address like I do, you can just set it here and forget about it.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If you are going to use a dynamic (DHCP) addresses, you should keep what&amp;rsquo;s already in here (static IP). You don&amp;rsquo;t need to change anything. The valid IP address should already be automatically discovered from DHCP servers and filled in. After the installation, you can change the static IP mode to DHCP mode by running &lt;code>vi /etc/network/interfaces&lt;/code> and make the following edits:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl"> auto vmbr0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">- iface vmbr0 inet static
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">&lt;/span>&lt;span class="gi">+ iface vmbr0 inet dhcp # Enable DHCP and remove static IP.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">&lt;/span>&lt;span class="gd">- address 10.112.154.220/16
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">- gateway 10.112.0.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">&lt;/span> bridge-ports eno1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bridge-stp off
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bridge-fd 0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After you enabled DHCP in the config, apply it by &lt;code>ifreload -a&lt;/code>. Check if it takes effect by &lt;code>ip a show vmbr0&lt;/code>, you should see something like &lt;code>valid_lft 7160sec preferred_lft 7160sec&lt;/code>. If there is no &lt;code>xx sec&lt;/code> inside it, your DHCP is malfunctioning.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@z8:/etc/network# ip a show vmbr0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">4: vmbr0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP group default qlen 1000
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> link/ether 30:13:8b:6d:3a:a6 brd ff:ff:ff:ff:ff:ff
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> inet 10.112.154.220/16 brd 10.112.255.255 scope global dynamic vmbr0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> valid_lft 7160sec preferred_lft 7160sec
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> inet6 fe80::3213:8bff:fe6d:3aa6/64 scope link
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> valid_lft forever preferred_lft forever
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm8.jpeg"
width="1280"
height="1024"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm8_hu_f1e09d0126385e40.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm8_hu_cf2d14b24937eaa4.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm8_hu_f8717c460e0c3fb.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm8_hu_c48c549747fce87b.jpeg 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm8_hu_bf0bf072e6823127.webp 1280w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm8_hu_52691de358776305.jpeg 1280w"
loading="lazy"
alt="kvm8"
class="gallery-image"
data-flex-grow="125"
data-flex-basis="300px"
>
&lt;/p>
&lt;h2 id="proxmox-ve-post-installtion">Proxmox VE Post-Installtion
&lt;/h2>&lt;p>Log in as root as we will make a few modifications.&lt;/p>
&lt;h3 id="change-apt-sources">Change APT Sources
&lt;/h3>&lt;p>In China, we don&amp;rsquo;t really have a good global Internet connection, so we will change APT source to mirrors in China.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Change APT source mirrors&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/deb.debian.org/mirrors.ustc.edu.cn/g&amp;#39;&lt;/span> /etc/apt/sources.list.d/debian.sources
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/security.debian.org/mirrors.ustc.edu.cn/g&amp;#39;&lt;/span> /etc/apt/sources.list.d/debian.sources
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Change Ceph source mirrors&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -f /etc/apt/sources.list.d/ceph.sources &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">CEPH_CODENAME&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sb">`&lt;/span>ceph -v &lt;span class="p">|&lt;/span> grep ceph &lt;span class="p">|&lt;/span> awk &lt;span class="s1">&amp;#39;{print $(NF-1)}&amp;#39;&lt;/span>&lt;span class="sb">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">source&lt;/span> /etc/os-release
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> cat &amp;gt; /etc/apt/sources.list.d/ceph.sources &lt;span class="s">&amp;lt;&amp;lt;EOF
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Types: deb
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">URIs: https://mirrors.ustc.edu.cn/proxmox/debian/ceph-$CEPH_CODENAME
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Suites: $VERSION_CODENAME
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Components: no-subscription
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Remove enterprise repo and add no-subscription repo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Remove enterprise sources&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm /etc/apt/sources.list.d/pve-enterprise.sources
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add no-subscription sources&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt; /etc/apt/sources.list.d/pve-no-subscription.sources &lt;span class="s">&amp;lt;&amp;lt;EOF
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Types: deb
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">URIs: https://mirrors.ustc.edu.cn/proxmox/debian/pve
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Suites: trixie
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Components: pve-no-subscription
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="change-ct-template-sources">Change CT Template Sources
&lt;/h3>&lt;p>Use mirrors in China.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sed -i.bak &lt;span class="s1">&amp;#39;s|http://download.proxmox.com|https://mirrors.ustc.edu.cn/proxmox|g&amp;#39;&lt;/span> /usr/share/perl5/PVE/APLInfo.pm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl restart pvedaemon
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="stop-cluster-services">Stop Cluster Services
&lt;/h3>&lt;p>We are not using PVE clusters. Disable them. If you are using PVE clusters, you probably will not be reading this guide :P&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">systemctl disable --now pve-ha-crm.service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl disable --now pve-ha-lrm.service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl disable --now corosync.service
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="install-common-tools">Install Common Tools
&lt;/h3>&lt;p>Useful tools that will be used regularly.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt install htop sysstat vim sudo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="configure-shell">Configure Shell
&lt;/h3>&lt;p>I like ZSH and my dotfiles, so I will use them.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt install git zsh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> https://github.com/charlie0129/dotfiles.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> dotfiles
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./bootstrap.sh -f
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chsh -s /usr/bin/zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Run zsh and follow the instructions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zsh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="configure-zram-swap">Configure ZRAM Swap
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> https://github.com/foundObjects/zram-swap.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> zram-swap
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./install.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf zram-swap
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The default lz4 is a good balance between speed and compression ratio. If you want to sacrifice speed for better compression, you can change the config to use zstd as the compression method.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/_zram_algorithm=.*/_zram_algorithm=&amp;#34;zstd&amp;#34;/g&amp;#39;&lt;/span> /etc/default/zram-swap
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Apply&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">systemctl restart zram-swap
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Tune kernel parameters to make better use of ZRAM&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &lt;span class="s">&amp;lt;&amp;lt;EOF &amp;gt; /etc/sysctl.d/zram.conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.swappiness = 180
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.watermark_boost_factor = 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.watermark_scale_factor = 125
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">vm.page-cluster = 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Apply&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sysctl --system
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="enable-ipv6-slaac">Enable IPv6 SLAAC
&lt;/h3>&lt;p>Proxmox disables IPv6 by default. To enable IPv6 SLAAC:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &lt;span class="s">&amp;lt;&amp;lt;EOF &amp;gt; /etc/sysctl.d/ipv6.conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv6.conf.default.accept_ra = 2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv6.conf.all.accept_ra = 2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv6.conf.default.forwarding = 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv6.conf.all.forwarding = 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv6.conf.default.proxy_ndp = 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">net.ipv6.conf.all.proxy_ndp = 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Apply it&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sysctl --system
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Note: the following command may break your internet connection.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl restart networking
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="trim-optimizations">TRIM Optimizations
&lt;/h3>&lt;p>TRIM will help prolong SSD lifespan and maintain performance. Enable it on the LVM thin pool.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/.*issue_discards = .*/\tissue_discards = 1/g&amp;#39;&lt;/span> /etc/lvm/lvm.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Since LXCs are just on the host, it will automatically issue discards if the underlying storage supports it, so no further action is needed. If you are using VM, be sure to use SCSI disks (VirtIO SCSI Single) with &lt;code>discard=on&lt;/code> option. I&amp;rsquo;ve heard that VirtIO Block devices will not work with dicards but I haven&amp;rsquo;t tested it yet. Correct me if I&amp;rsquo;m wrong.&lt;/p>
&lt;h3 id="limit-journal-size">Limit Journal Size
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/.*SystemMaxUse.*/SystemMaxUse=32M/g&amp;#39;&lt;/span> /etc/systemd/journald.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Apply&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">systemctl daemon-reload
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl restart systemd-journald
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="nvidia-p2p-driver-installation">NVIDIA P2P Driver Installation
&lt;/h2>&lt;p>Instead of regular NVIDIA drivers, we will be installing P2P-enabled drivers to force enable PCIe P2P capabilities on consumer cards (like GeForce RTX 4090). This will bring performance boost (~10%) across multiple scenarios. For details, refer to my blog post: &lt;a class="link" href="https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/" >Enabling PCIe P2P on NVIDIA RTX 4090s&lt;/a>&lt;/p>
&lt;h3 id="disable-iommu">Disable IOMMU
&lt;/h3>&lt;p>Since PCIe P2P in Linux doesn&amp;rsquo;t work so well with IOMMU-enabled systems (there are many potential issues you may run into), you may as well just disable IOMMU. Note that if you have more than 255 CPU cores, you will only have access to 255 CPUs due to APIC fallback.&lt;/p>
&lt;p>To do disable IOMMU, you can either&lt;/p>
&lt;ul>
&lt;li>Disable Intel VT-d (AMD is on by default) in BIOS&lt;/li>
&lt;li>Disable &lt;code>intel_iommu&lt;/code> or &lt;code>amd_iommu&lt;/code> in Linux kernel parameters&lt;/li>
&lt;/ul>
&lt;p>To disable &lt;code>intel_iommu&lt;/code> or &lt;code>amd_iommu&lt;/code> in Linux kernel parameters, use the following command to add &lt;code>intel_iommu=off&lt;/code> and &lt;code>amd_iommu=off&lt;/code> to your &lt;code>GRUB_CMDLINE_LINUX_DEFAULT&lt;/code>. BTW: I also reduce the screen resolution to 1024x768 because it&amp;rsquo;s a server&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=&amp;#34;intel_iommu=off amd_iommu=off video=1024x768@60&amp;#34;/g&amp;#39;&lt;/span> /etc/default/grub
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Reboot your system to see the effect.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">reboot
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Make sure the follow command produces NO entries. If there are output, it means IOMMU is not correctly disabled.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ls /sys/class/iommu
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="disable-acs">Disable ACS
&lt;/h3>&lt;p>You may need to disable ACS to get PCIe P2P to work, refer to &lt;a class="link" href="https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/troubleshooting.html" target="_blank" rel="noopener"
>https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/troubleshooting.html&lt;/a> (PCI Access Control Services)&lt;/p>
&lt;h3 id="enable-above-4g-decoding">Enable Above 4G Decoding
&lt;/h3>&lt;p>PCIe P2P will need to access each GPU&amp;rsquo;s memory, so the PCIe BAR should be large enough to cover GPU&amp;rsquo;s memory. You must enable &lt;code>Above 4G Decoding&lt;/code> in your motherboard settings to make the PCIe BAR large enough to work. Enable it in your motherboard&amp;rsquo;s BIOS.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm3.jpeg"
width="800"
height="600"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm3_hu_7e15f21db3b3c17e.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm3_hu_9e3424f688b5b212.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm3_hu_c195e513c0a13d42.webp 800w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm3_hu_7d51eb80b8d4fadf.jpeg 800w"
loading="lazy"
alt="kvm3"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;h3 id="install-official-driver">Install Official Driver
&lt;/h3>&lt;p>Before we install the P2P kernel module, we need to install the official drivers.&lt;/p>
&lt;p>Since we are installing unofficial P2P kernel modules, there is a limited number of driver version that will work. The version of the P2P kernel modules MUST be the same as the official driver we are going to install.&lt;/p>
&lt;p>Refer to &lt;a class="link" href="https://github.com/tinygrad/open-gpu-kernel-modules" target="_blank" rel="noopener"
>https://github.com/tinygrad/open-gpu-kernel-modules&lt;/a> to see what versions are available. I also have my patches that works with much newer driver versions (&lt;code>575.57.08&lt;/code>) (at the time of writing) &lt;a class="link" href="https://github.com/charlie0129/open-gpu-kernel-modules" target="_blank" rel="noopener"
>https://github.com/charlie0129/open-gpu-kernel-modules&lt;/a> .&lt;/p>
&lt;p>I will be using version &lt;code>575.57.08&lt;/code>, so we should download the official driver of the same version (575.57.08). You should download the &lt;code>.run&lt;/code> file, e.g., &lt;code>NVIDIA-Linux-x86_64-575.57.08.run&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Assume you download the installer as NVIDIA-Linux-x86_64-575.57.08.run&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">installer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;NVIDIA-Linux-x86_64-575.57.08.run&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x &lt;span class="nv">$installer&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Skip kernel modules because we will install P2P-patched version later.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">./&lt;span class="nv">$installer&lt;/span> --no-kernel-modules
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Choose whatever you want, doesn&amp;rsquo;t matter.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000515967.png"
width="1332"
height="936"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000515967_hu_98208fee79b653b6.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000515967_hu_8c48fb6ddb80aa09.jpg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000515967_hu_961f2daf7702c60.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000515967_hu_c210bf19507e4f39.jpg 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000515967_hu_b2550ba7a4857e9c.webp 1332w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000515967_hu_e39d158f30e89b6a.jpg 1332w"
loading="lazy"
alt="image-20250817000515967"
class="gallery-image"
data-flex-grow="142"
data-flex-basis="341px"
>
&lt;/p>
&lt;p>Yes, disable nouveau, we will be using NVIDIA drivers.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000549521.png"
width="1332"
height="936"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000549521_hu_c04467322fc6b9b5.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000549521_hu_6960c0b2e9c6b7b7.jpg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000549521_hu_3b938860c46b37b2.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000549521_hu_166683d5e7642037.jpg 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000549521_hu_6cffe724c88436b4.webp 1332w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000549521_hu_74c62616de8a0c23.jpg 1332w"
loading="lazy"
alt="image-20250817000549521"
class="gallery-image"
data-flex-grow="142"
data-flex-basis="341px"
>
&lt;/p>
&lt;p>Do not abort, just continue. Nouveau will be disabled on next boot.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000652879.png"
width="1332"
height="936"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000652879_hu_d23455096df2bfa2.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000652879_hu_6b7514f546ecd5da.jpg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000652879_hu_849962d25bf10f61.webp 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000652879_hu_d5e6cd44b8bf36e5.jpg 1024w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000652879_hu_7936dd81616107f0.webp 1332w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/image-20250817000652879_hu_a5cd53c580d81314.jpg 1332w"
loading="lazy"
alt="image-20250817000652879"
class="gallery-image"
data-flex-grow="142"
data-flex-basis="341px"
>
&lt;/p>
&lt;p>Just choose the default option for every step that comes later.&lt;/p>
&lt;p>Remove the driver installation file because we will not be using it later.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">rm &lt;span class="nv">$installer&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You can skip reboot now. We will reboot after we installed the kernel modules.&lt;/p>
&lt;h3 id="install-p2p-enabled-kernel-modules">Install P2P-Enabled Kernel Modules
&lt;/h3>&lt;p>Make sure the version of the P2P-enabled kernel modules match the version of the driver. I will use veriosn 575.57.08.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> -b 575.57.08-p2p https://github.com/charlie0129/open-gpu-kernel-modules.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>To build kernel modules, install build dependencies and kernel source. &lt;code>sudo&lt;/code> installed because the install script uses &lt;code>sudo&lt;/code> but we don&amp;rsquo;t have it now.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt install sudo build-essential proxmox-headers-&lt;span class="k">$(&lt;/span>uname -r&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Build and install the kernel module&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">./install.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You can safely ignore the NVIDIA-SMI failure as long as the build succeeds (you should see a &lt;code>DEPMOD /lib/modules/6.14.8-2-pve&lt;/code> line) because we haven&amp;rsquo;t rebooted yet (so NVIDIA-SMI can&amp;rsquo;t be used).&lt;/p>
&lt;p>Remove the source after installing&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf open-gpu-kernel-modules
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Reboot your system and you should see &lt;code>nvidia-smi&lt;/code> running fine.&lt;/p>
&lt;p>To tell if P2P is enabled, we will use a simple method (performance testing will be done later):&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">nvidia-smi -q &lt;span class="p">|&lt;/span> grep -i bar -A &lt;span class="m">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You should see &lt;code>&amp;gt; 2048 MiB&lt;/code> BAR1 Total memory, &lt;code>32768 MiB&lt;/code> in my case.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> BAR1 Memory Usage
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Total : 32768 MiB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Used : 2 MiB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Free : 32766 MiB
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="enable-persistence-mode">Enable Persistence Mode
&lt;/h3>&lt;p>Persistence mode will:&lt;/p>
&lt;ul>
&lt;li>Keep the GPU driver running so program can start faster&lt;/li>
&lt;li>Lower GPU power mode when it&amp;rsquo;s idle to save power. For RTX 4090s, it can drop from ~70W to ~10W.&lt;/li>
&lt;li>Make sure &lt;code>/dev/nvidia*&lt;/code> device nodes are ready. This is useful because we are passing the GPU devices &lt;code>/dev/nvidia*&lt;/code> to CTs (LXC Containers) later, it requires the device nodes to be present on start up for auto-started CTs.&lt;/li>
&lt;/ul>
&lt;p>So you really should enable it.&lt;/p>
&lt;p>To enable persistence mode:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">/usr/bin/nvidia-smi -pm &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>To make it persistent across reboots, we will use a cron job.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">crontab -e
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Add a line to run &lt;code>nvidia-smi&lt;/code> on boot:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">@reboot /usr/bin/nvidia-smi -pm &lt;span class="m">1&lt;/span> &amp;gt;/dev/null 2&amp;gt;&lt;span class="p">&amp;amp;&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>Why not use &lt;code>nvidia-persistenced&lt;/code> systemd service? Because for whatever reason, it does not initialize the device nodes &lt;code>/dev/nvidia*&lt;/code> correctly, so auto-started CTs will encounter &lt;code>Cuda failure 'unknown error'&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> /usr/share/doc/NVIDIA_GLX-1.0/samples
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tar jxf nvidia-persistenced-init.tar.bz2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> nvidia-persistenced-init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./install.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/blockquote>
&lt;p>You should feel &lt;code>nvidia-smi&lt;/code> runs much faster than before.&lt;/p>
&lt;h2 id="build-ct-templates">Build CT Templates
&lt;/h2>&lt;p>We will build a CT Template that has everything a student will need:&lt;/p>
&lt;ul>
&lt;li>Common build dependencies&lt;/li>
&lt;li>CUDA&lt;/li>
&lt;li>Docker&lt;/li>
&lt;li>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>Download the Linux distro you want. I chose Ubuntu 24.04, not because I like Ubuntu (I use Debian), but because most students only know Ubuntu.&lt;/p>
&lt;p>Create an unprivileged CT just like you normally would. Just remember to give it a bit more disk space (32GB or more) because we will install CUDA later and CUDA is really large.&lt;/p>
&lt;h3 id="install-docker">Install Docker
&lt;/h3>&lt;p>After you created the CT, shut it down. Add additional settings to allow Docker to use overlayfs driver, otherwise Docker images will be extremely inefficient in CTs.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This command should be run on the host, not CT.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Assume 8000 is the ID of your template CT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &lt;span class="s">&amp;lt;&amp;lt;EOF &amp;gt;&amp;gt; /etc/pve/local/lxc/8000.conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.apparmor.profile: unconfined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.cgroup.devices.allow: a
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.cap.drop:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>All following commands should be run in the CT unless specified otherwise.&lt;/p>
&lt;p>Add Docker configuration. Systemd journal is used as log driver to make use of previously configured journal size limit. By writing logs to a centralized location, it&amp;rsquo;s also useful if you want to write logs to memory to reduce disk writes (e.g. log2ram).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir -p /etc/docker/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &lt;span class="s">&amp;lt;&amp;lt;EOF &amp;gt; /etc/docker/daemon.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;#34;live-restore&amp;#34;: true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;#34;experimental&amp;#34;: true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;#34;log-driver&amp;#34;: &amp;#34;journald&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install Docker&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">DOWNLOAD_URL&lt;/span>&lt;span class="o">=&lt;/span>https://mirrors.ustc.edu.cn/docker-ce
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -fsSL https://get.docker.io &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="revert-containerd-config">Revert Containerd Config
&lt;/h3>&lt;p>Docker modified containerd config. We will revert it to the default config.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">containerd config default &amp;gt;/etc/containerd/config.toml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="add-gpus-to-ct">Add GPUs to CT
&lt;/h3>&lt;p>Choose which GPU you need to passthrough by looking for the index in &lt;code>nvidia-smi&lt;/code>, or by &lt;code>ls -l /dev/nvidia*&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This command should be run on the host, not CT.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ls -l /dev/nvidia*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">0&lt;/span> 2025-08-17 01:47:58 /dev/nvidia0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">1&lt;/span> 2025-08-17 01:48:00 /dev/nvidia1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">2&lt;/span> 2025-08-17 01:48:01 /dev/nvidia2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">3&lt;/span> 2025-08-17 01:48:02 /dev/nvidia3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">4&lt;/span> 2025-08-17 01:48:03 /dev/nvidia4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">5&lt;/span> 2025-08-17 01:48:05 /dev/nvidia5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">6&lt;/span> 2025-08-17 01:48:06 /dev/nvidia6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw-rw- &lt;span class="m">1&lt;/span> root root 195, &lt;span class="m">7&lt;/span> 2025-08-17 01:48:07 /dev/nvidia7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">... &lt;span class="o">(&lt;/span>omitted&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For example, if want to add all 8 GPUs (0, 1, 2, 3, 4, 5, 6, 7) to CT, I will need to run:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This command should be run on the host, not CT.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Assume 8000 is the ID of your template CT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &lt;span class="s">&amp;lt;&amp;lt;EOF &amp;gt;&amp;gt; /etc/pve/local/lxc/8000.conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia1 dev/nvidia1 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia2 dev/nvidia2 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia3 dev/nvidia3 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia4 dev/nvidia4 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia5 dev/nvidia5 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia6 dev/nvidia6 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia7 dev/nvidia7 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Note the &lt;code>lxc.mount.entry: /dev/nvidiaX dev/nvidiaX none bind,optional,create=file&lt;/code> lines. Each line represents a single GPU to add to the CT. I will add 8 GPUs, so I add 8 lines from &lt;code>nvidia0&lt;/code> through &lt;code>nvidia7&lt;/code>. Remove or add them as needed.&lt;/p>
&lt;p>Check if the GPUs are there inside CT:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ls -l /dev/nvidia*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">00&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">modeset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">43&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">uvm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">43&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">uvm&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tools&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">47&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">58&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">00&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">01&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">02&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">03&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">05&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">06&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia6&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">07&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidia7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">crw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rw&lt;/span>&lt;span class="o">-&lt;/span> &lt;span class="n">nobody&lt;/span> &lt;span class="n">nogroup&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="mi">2025&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">08&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span> &lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">47&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">58&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nvidiactl&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>We haven&amp;rsquo;t installed drivers yet, so nvidia-smi is not available.&lt;/p>
&lt;h3 id="install-gpu-driver">Install GPU Driver
&lt;/h3>&lt;p>Important: you should use the same driver version as the host (&lt;code>575.57.08&lt;/code> in my case).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">wget &amp;lt;driver-runfile-url&amp;gt; -O driver.run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x driver.run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">driver.run --no-kernel-modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm driver.run
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Just install as normal.&lt;/p>
&lt;p>Now you should be able to use &lt;code>nvidia-smi&lt;/code> and see the GPUs you added to the CT (8 GPUs in my case).&lt;/p>
&lt;h3 id="install-cuda">Install CUDA
&lt;/h3>&lt;p>Important: current driver version MUST be greater than (or equal to) the driver required by CUDA. For example, CUDA 12.9.0 will work on my machine because CUDA 12.9.0 requires &lt;code>575.00&lt;/code>, and my current driver version is &lt;code>575.57.08&lt;/code> (greater than &lt;code>575.00&lt;/code>).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">wget &amp;lt;cuda-runfile-url&amp;gt; -O cuda.run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x cuda.run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cuda.run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm cuda.run
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Remember to deselect the driver (we already installed the correct driver):&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌──────────────────────────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ CUDA Installer │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ - [ ] Driver &amp;lt;== deselect this │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ [ ] 575.51.03 &amp;lt;== deselect this │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ + [X] CUDA Toolkit 12.9 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ [ ] CUDA Demo Suite 12.9 &amp;lt;== deselect this (not useful, takes up space) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ [ ] CUDA Documentation 12.9 &amp;lt;== deselect this (not useful, takes up space) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ - [ ] Kernel Objects │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ [ ] nvidia-fs │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Options │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Install │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Up/Down: Move | Left/Right: Expand | &amp;#39;Enter&amp;#39;: Select | &amp;#39;A&amp;#39;: Advanced options │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└──────────────────────────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After CUDA installation, configure &lt;code>PATH&lt;/code> and &lt;code>LD_LIBRARY_PATH&lt;/code> per printed instructions.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">... (omitted)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Please make sure that
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - PATH includes /usr/local/cuda-12.9/bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - LD_LIBRARY_PATH includes /usr/local/cuda-12.9/lib64, or, add /usr/local/cuda-12.9/lib64 to /etc/ld.so.conf and run ldconfig as root
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">... (omitted)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Note that you can use &lt;code>/usr/local/cuda&lt;/code> instead of &lt;code>/usr/local/cuda-XX.X&lt;/code> so it&amp;rsquo;s independent of CUDA versions.&lt;/p>
&lt;p>Also add &lt;code>export CUDA_HOME=/usr/local/cuda&lt;/code> to your shell rc file.&lt;/p>
&lt;p>nvcc should be available after a shell reload:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># nvcc -V&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">nvcc&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">NVIDIA&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">Cuda&lt;/span> &lt;span class="n">compiler&lt;/span> &lt;span class="n">driver&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Copyright&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="mi">2005&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2025&lt;/span> &lt;span class="n">NVIDIA&lt;/span> &lt;span class="n">Corporation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Built&lt;/span> &lt;span class="n">on&lt;/span> &lt;span class="n">Wed_Apr__9_19&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">24&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">57&lt;/span>&lt;span class="n">_PDT_2025&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Cuda&lt;/span> &lt;span class="n">compilation&lt;/span> &lt;span class="n">tools&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">release&lt;/span> &lt;span class="mf">12.9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">V12&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">9.41&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Build&lt;/span> &lt;span class="n">cuda_12&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">9.&lt;/span>&lt;span class="n">r12&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">compiler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">35813241&lt;/span>&lt;span class="n">_0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="install-nccl">Install NCCL
&lt;/h3>&lt;p>You will need NCCL for most GPU-related communications.&lt;/p>
&lt;p>You should find a NCCL that&amp;rsquo;s compatible with your CUDA version. For example, NCCL &lt;code>2.27.3&lt;/code> is compatible with my CUDA version &lt;code>12.9&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Download NCCL package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wget https://xxx/nccl_2.27.3-1+cuda12.9_x86_64.txz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tar Jxf nccl_2.27.3-1+cuda12.9_x86_64.txz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Set some variables&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">NCCL_VERSION&lt;/span>&lt;span class="o">=&lt;/span>2.27
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p /usr/local/nccl-&lt;span class="nv">$NCCL_VERSION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cp -vRf nccl_2.27.3-1+cuda12.9_x86_64/* /usr/local/nccl-&lt;span class="nv">$NCCL_VERSION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf nccl_2.27.3-1+cuda12.9_x86_64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ln -s /usr/local/nccl-&lt;span class="nv">$NCCL_VERSION&lt;/span> /usr/local/nccl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;/usr/local/nccl/lib&amp;#34;&lt;/span> &amp;gt;&amp;gt;/etc/ld.so.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ldconfig
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ln -s /usr/local/nccl/include/nccl.h /usr/local/include/nccl.h
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Remove NCCL package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm https://xxx/nccl_2.27.3-1+cuda12.9_x86_64.txz
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Also add &lt;code>export NCCL_HOME=/usr/local/nccl&lt;/code> in your shell rc file,&lt;/p>
&lt;h3 id="install-nvidia-container-toolkit">Install NVIDIA Container Toolkit
&lt;/h3>&lt;p>To run Docker containers with GPUs.&lt;/p>
&lt;p>Note that the URLs below are mirrors in China. You can use original URL if you prefer.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://mirrors.ustc.edu.cn/libnvidia-container/gpgkey &lt;span class="p">|&lt;/span> sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -s -L https://mirrors.ustc.edu.cn/libnvidia-container/stable/deb/nvidia-container-toolkit.list &lt;span class="p">|&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> sed &lt;span class="s1">&amp;#39;s#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/nvidia.github.io/mirrors.ustc.edu.cn/g&amp;#39;&lt;/span> /etc/apt/sources.list.d/nvidia-container-toolkit.list
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get install -y nvidia-container-toolkit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Configure Docker and Containerd to use NVIDIA Container Toolkit&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">nvidia-ctk runtime configure --runtime&lt;span class="o">=&lt;/span>docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">nvidia-ctk runtime configure --runtime&lt;span class="o">=&lt;/span>containerd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Since we are running inside unprivileged containers, we need to set &lt;code>no-cgroups&lt;/code> to true&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="install-nvidia-nsight-systems">Install NVIDIA Nsight Systems
&lt;/h3>&lt;p>So we can profile applications running on the GPU.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">wget https://xxx/NsightSystems-linux-cli-public-2025.3.1.90-3582212.deb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dpkg -i NsightSystems-linux-cli-public-2025.3.1.90-3582212.deb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -f NsightSystems-linux-cli-public-2025.3.1.90-3582212.deb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="functionality-tests">Functionality Tests
&lt;/h3>&lt;h4 id="nccl">NCCL
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/NVIDIA/nccl-tests.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> nccl-tests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git reset --hard 903918f &lt;span class="c1"># I only tested this commit.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>P2P ON: &lt;code>NCCL_P2P_LEVEL=sys ./build/all_reduce_perf --minbytes 8 --maxbytes 128M --stepfactor 2 --ngpus 8&lt;/code> (change &lt;code>--ngpus&lt;/code> accordingly)&lt;/p>
&lt;p>P2P OFF: &lt;code>NCCL_P2P_DISABLE=1 ./build/all_reduce_perf --minbytes 8 --maxbytes 128M --stepfactor 2 --ngpus 8&lt;/code> (change &lt;code>--ngpus&lt;/code> accordingly)&lt;/p>
&lt;p>You should see a bandwidth increase after P2P is on.&lt;/p>
&lt;h4 id="cuda-p2p">CUDA P2P
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/NVIDIA/cuda-samples.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git reset --hard 9c688d7 &lt;span class="c1"># I only tested this commit.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> cuda-samples/Samples/5_Domain_Specific/p2pBandwidthLatencyTest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./p2pBandwidthLatencyTest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You should see lower GPU-GPU latency, higher bandwidth if P2P is on.&lt;/p>
&lt;h3 id="prepare-for-templating">Prepare for Templating
&lt;/h3>&lt;p>Check if there are unnecessary files and remove them.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ls -l
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Remove history&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">unset&lt;/span> HISTFILE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm .*_history
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> -n &amp;gt;/var/log/lastlog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> -n &amp;gt;/var/log/wtmp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> -n &amp;gt;/var/log/btmp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">journalctl --vacuum-time&lt;span class="o">=&lt;/span>1s
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -f /var/log/*.log
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Now, it&amp;rsquo;s ready for converting to a template. To give each student a new development container, just clone this template.&lt;/p>
&lt;h2 id="other-minor-settings">Other Minor Settings
&lt;/h2>&lt;h3 id="raid-card-write-back">RAID Card Write Back
&lt;/h3>&lt;p>To achieve maximum write speed, you can enable &lt;code>write back&lt;/code> mode on your RAID card, so data is written to cache first. This will significantly (yes, by A LOT, sometimes over 100x) improve random write speed on HDDs (SSDs are already very fast). If your RAID card has a battery, you don&amp;rsquo;t need to worry about data integrity in case a power failure.&lt;/p>
&lt;p>Write cache policy:&lt;/p>
&lt;ul>
&lt;li>&lt;code>Write Throuh&lt;/code>: no cache&lt;/li>
&lt;li>&lt;code>Write Back&lt;/code>: cached if battery backup is present on the RAID card&lt;/li>
&lt;li>&lt;code>Always Write Back&lt;/code>: always cached&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm4.jpeg"
width="800"
height="600"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm4_hu_cf6cf63f2dd5b21c.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm4_hu_e57234e7f2dfb528.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm4_hu_8b6806be98b2182f.webp 800w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm4_hu_c76764e8bd8ebf46.jpeg 800w"
loading="lazy"
alt="kvm4"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;h2 id="obstacles">Obstacles
&lt;/h2>&lt;h3 id="only-255-cores-are-recognized---x2apic">Only 255 Cores are Recognized - x2APIC
&lt;/h3>&lt;p>I noticed that I have one CPU offline. It should recognize 256 CPUs but I only have 255 CPUs.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># lscpu
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Architecture: x86_64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CPU op-mode(s): 32-bit, 64-bit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Address sizes: 48 bits physical, 48 bits virtual
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Byte Order: Little Endian
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">CPU(s): 256
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> On-line CPU(s) list: 0-254
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Off-line CPU(s) list: 255 &amp;lt;--------------- one CPU is offline -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Vendor ID: AuthenticAMD
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Model name: AMD EPYC 7763 64-Core Processor
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CPU family: 25
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Model: 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Thread(s) per core: 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Core(s) per socket: 64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Socket(s): 2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After some digging, I learned that I need to enable &lt;code>x2APIC&lt;/code> on both the motherboard and kernel to have more than 255 CPU cores recognized.&lt;/p>
&lt;p>In BIOS &lt;code>Local APIC Mode&lt;/code> should already be &lt;code>x2APIC&lt;/code> .&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm9.jpeg"
width="800"
height="600"
srcset="https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm9_hu_2d8789994fa6a9a2.webp 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm9_hu_21d37ab5a4ab49.jpeg 480w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm9_hu_fe3ad68b7055c380.webp 800w, https://charlie0129.github.io/blog/p/proxmox-ve-shared-gpu-installation-guide/images/kvm9_hu_7ce9a82287fa5f2a.jpeg 800w"
loading="lazy"
alt="kvm9"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>Now I realized it can be because I disabled IOMMU for PCIe P2P before. It turns out that disableing IOMMU can limit the number of available logical cores to 255. The reason is that the Linux kernel disables x2APIC in this case and falls back to APIC, which can only enumerate a maximum of 255 (logical) cores.&lt;/p>
&lt;p>I will keep IOMMU disabled because I need PCIe P2P to function (I don&amp;rsquo;t want to deal with P2P with IOMMU on). Just forget about the 256th core :p.&lt;/p></description></item><item><title>Share NVIDIA GPU between CTs in Proxmox VE</title><link>https://charlie0129.github.io/blog/p/pve-ct-share-nvidia-gpu/</link><pubDate>Mon, 04 Nov 2024 12:57:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/pve-ct-share-nvidia-gpu/</guid><description>&lt;h2 id="background">Background
&lt;/h2>&lt;p>Consider a small lab, students need to use GPU for their projects. We have a NVIDIA GPU in our Proxmox VE server, and we want to share the GPU between multiple containers so that multiple students can use the GPU at the same time.&lt;/p>
&lt;p>Why not use a VM? Because a GPU can only be passed through to one VM at a time (only one student can use the GPU at a time). And resources are not flexible in VMs.&lt;/p>
&lt;p>Why not create multiple users in the host and let them run their programs in the host? Because we want to isolate the students from the host, so that they can&amp;rsquo;t access the host and other students&amp;rsquo; data.&lt;/p>
&lt;p>Why not use Docker? Because Docker containers doesn&amp;rsquo;t have a full init system, and it&amp;rsquo;s hard to run some applications.&lt;/p>
&lt;p>Since we use PVE and it has LXC containers built-in (called CT), it is a perfect choice.&lt;/p>
&lt;h2 id="install-drivers-on-the-host">Install Drivers on the Host
&lt;/h2>&lt;p>Make sure the GPU is detected by the host. Note the NVIDIA GPUs &lt;code>3b:00.0&lt;/code> (Your address may differ).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> lspci &lt;span class="p">|&lt;/span> grep -i nvidia
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">3b:00.0 VGA compatible controller: NVIDIA Corporation TU104GL [Quadro RTX 5000] (rev a1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">3b:00.1 Audio device: NVIDIA Corporation TU104 HD Audio Controller (rev a1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">3b:00.2 USB controller: NVIDIA Corporation TU104 USB 3.1 Host Controller (rev a1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">3b:00.3 Serial bus controller [0c80]: NVIDIA Corporation TU104 USB Type-C UCSI Controller (rev a1)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>You may ask: does your entire lab only own one RTX 5000? What kind of lab is this? Are you cave people?&lt;/p>
&lt;p>Yes, although we have multiple projects worth over millions of Chinese Yuan, most of the money is gone to the some other places (which I cannot publicly speak on the Internet 🤫 ). And the professors have no emphasis on students&amp;rsquo; growth.
As a result, we are actually poor as hell.&lt;/p>
&lt;p>Since almost no one knows how to properly configure a Linux server, I want to help my classmates to learn more and let them use the only GPU. But to be honest, I won&amp;rsquo;t benefit from doing this. It&amp;rsquo;s just voluntary work.&lt;/p>&lt;/blockquote>
&lt;p>Install prerequisites. Note that I am using &lt;code>pve-headers-$(uname -r)&lt;/code> to install the headers for the current kernel. If you are using a different kernel, you may need to install the headers for that kernel. Also, you may want to use &lt;code>linux-headers-$(uname -r)&lt;/code> instead of &lt;code>pve-headers-$(uname -r)&lt;/code> if you are not using Proxmox VE.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> apt install -y gcc make pve-headers-&lt;span class="k">$(&lt;/span>uname -r&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Download CUDA toolkit from &lt;a class="link" href="https://developer.nvidia.com/cuda-downloads" target="_blank" rel="noopener"
>here&lt;/a> and install it. Drivers are included in the CUDA toolkit so you don&amp;rsquo;t need to install drivers separately.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> wget &amp;lt;cuda-runfile-download-url&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> ./cuda_12.2.2_535.104.05_linux.run --silent
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The default installation options will work fine. If anything fails, you can check the log file at &lt;code>/var/log/cuda-installer.log&lt;/code> for CUDA logs and &lt;code>/var/log/nvidia-installer.log&lt;/code> for NVIDIA driver logs.&lt;/p>
&lt;p>PS: You need to blacklist &lt;code>nouveau&lt;/code> driver. This is automatically done by PVE. If not, you can do this by creating a file &lt;code>/etc/modprobe.d/blacklist-nouveau.conf&lt;/code> with the following content: &lt;code>blacklist nouveau&lt;/code>. Then run &lt;code>update-initramfs -u&lt;/code> to update the initramfs.&lt;/p>
&lt;p>PPS: If you used to passthrough this GPU to a VM, be sure to remove the GPU from the VM&amp;rsquo;s hardware configuration in PVE otherwise PVE will bound the GPU to &lt;code>vfio-pci&lt;/code> (see &lt;code>Kernel driver in use&lt;/code> row in &lt;code>lspci -k&lt;/code>) and cannot be used by the host.&lt;/p>
&lt;p>PPPS: Some kernel versions are known to have problems with NVIDIA drivers. If you encounter problems, you may need to downgrade/upgrade the kernel. For example, kernel version 5.10.0 is known to have &lt;code> make[3]: *** No rule to make target 'scripts/module.lds', needed by '/tmp/selfgz38416/NVIDIA-Linux-x86_64-560.35.03/kernel-open/nvidia.ko'&lt;/code> error.&lt;/p>
&lt;p>After installation finished, check if the driver is loaded.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> nvidia-smi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Tue Nov 5 09:56:44 2024
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+---------------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-----------------------------------------+----------------------+----------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| | | MIG M. |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|=========================================+======================+======================|
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| 0 Quadro RTX 5000 Off | 00000000:3B:00.0 Off | Off |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| 33% 44C P0 28W / 230W | 0MiB / 16384MiB | 6% Default |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| | | N/A |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+-----------------------------------------+----------------------+----------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+---------------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| Processes: |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| GPU GI CI PID Type Process name GPU Memory |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| ID ID Usage |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|=======================================================================================|
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| No running processes found |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+---------------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="allow-nvidia-device-passthrough-in-ct">Allow NVIDIA Device Passthrough in CT
&lt;/h2>&lt;p>Now we need to allow the CT to access the GPU. I am using an unprivileged container here. Edit the CT&amp;rsquo;s configuration file (&lt;code>/etc/pve/local/lxc/&amp;lt;id&amp;gt;.conf&lt;/code>). Add the following lines to the end of the file.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl"> arch: amd64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> cores: 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> features: nesting=1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> hostname: ct-gpu-tmpl-deb127-cu122
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> memory: 4096
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> net0: name=eth0,bridge=vmbr0,firewall=1,hwaddr=AA:AB:F0:07:42:D0,ip=dhcp,type=veth
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ostype: debian
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rootfs: local-zfs:basevol-8001-disk-0,size=16G
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> swap: 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unprivileged: 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># These lines allow the container to access specific character devices (c) with rwm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># permissions (read, write, modify). These are needed for NVIDIA GPU access.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.cgroup.devices.allow: c 195:* rwm
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.cgroup.devices.allow: c 509:* rwm
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.cgroup.devices.allow: c 235:* rwm
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">&lt;/span># These lines mount various GPU-related devices from the host into the container.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>PS: If you cannot use &lt;code>nvidia-smi&lt;/code> (it says &lt;code>Failed to initialize NVML: Unknown Error&lt;/code>), there is a possibility that you are using &lt;code>cgroup2&lt;/code>. Change all the &lt;code>lxc.cgroup.devices.allow&lt;/code> lines to &lt;code>lxc.cgroup2.devices.allow&lt;/code>.&lt;/p>
&lt;p>&lt;strong>Explanation:&lt;/strong>&lt;/p>
&lt;p>Allows container access to NVIDIA device nodes:&lt;/p>
&lt;ul>
&lt;li>&lt;code>c 195:*&lt;/code> - NVIDIA character devices&lt;/li>
&lt;li>&lt;code>c 509:*&lt;/code> - NVIDIA UVM devices&lt;/li>
&lt;li>&lt;code>c 235:*&lt;/code> - NVIDIA CTL devices&lt;/li>
&lt;/ul>
&lt;p>Maps the following host GPU devices into container:&lt;/p>
&lt;ul>
&lt;li>&lt;code>/dev/nvidia0&lt;/code> - Main GPU device&lt;/li>
&lt;li>&lt;code>/dev/nvidiactl&lt;/code> - NVIDIA control device&lt;/li>
&lt;li>&lt;code>/dev/nvidia-modeset&lt;/code> - Display mode setting&lt;/li>
&lt;li>&lt;code>/dev/nvidia-uvm&lt;/code> - Unified memory management&lt;/li>
&lt;li>&lt;code>/dev/nvidia-uvm-tools&lt;/code> - UVM diagnostic tools&lt;/li>
&lt;li>&lt;code>/dev/dri&lt;/code> - Direct Rendering Infrastructure&lt;/li>
&lt;li>&lt;code>/dev/fb0&lt;/code> - Framebuffer device&lt;/li>
&lt;/ul>
&lt;p>Mount options:&lt;/p>
&lt;ul>
&lt;li>&lt;code>bind&lt;/code>: Mount as a bind mount&lt;/li>
&lt;li>&lt;code>optional&lt;/code>: Don&amp;rsquo;t fail if device doesn&amp;rsquo;t exist&lt;/li>
&lt;li>&lt;code>create=file/dir&lt;/code>: Create the mount point if it doesn&amp;rsquo;t exist&lt;/li>
&lt;/ul>
&lt;p>Note that if you are using a different GPU, you may need to change the device numbers. For example, &lt;code>/dev/nvidia1&lt;/code> instead of &lt;code>/dev/nvidia0&lt;/code>. You can find the device numbers in &lt;code>nvidia-smi&lt;/code> output.&lt;/p>
&lt;h2 id="install-drivers-in-ct">Install Drivers in CT
&lt;/h2>&lt;p>Log into the CT. All the following commands are run in the CT.&lt;/p>
&lt;p>You should be able to see NVIDIA devices inside the CT:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> ls -l /dev/nvidia*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">---------- 1 root root 0 Nov 5 02:31 /dev/nvidia-modeset
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">crw-rw-rw- 1 nobody nogroup 507, 0 Nov 5 01:56 /dev/nvidia-uvm
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">crw-rw-rw- 1 nobody nogroup 507, 1 Nov 5 01:56 /dev/nvidia-uvm-tools
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">crw-rw-rw- 1 nobody nogroup 195, 0 Nov 5 01:56 /dev/nvidia0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">crw-rw-rw- 1 nobody nogroup 195, 255 Nov 5 01:56 /dev/nvidiactl
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install CUDA and drivers, just like you would on a physical machine, except that you don&amp;rsquo;t need to install the kernel modules. I will install CUDA 12.2 (drivers are included in the CUDA installer).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> wget &amp;lt;cuda-runfile-download-url&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> apt install -y gcc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> ./cuda_12.2.2_535.104.05_linux.run --extract&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">pwd&lt;/span>&lt;span class="k">)&lt;/span>/cu122
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Note that I extracted the installer to manually install it because we want to skip kernel module installation and such options are not exposed in the installer.&lt;/p>
&lt;p>Install the bundled drivers:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">cd cu122
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">./NVIDIA-Linux-x86_64-535.104.05.run --no-nouveau-check --no-kernel-modules --silent
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Run &lt;code>nvidia-smi&lt;/code> to check if the driver is loaded.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> nvidia-smi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Tue Nov 5 05:49:02 2024
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+---------------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-----------------------------------------+----------------------+----------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| | | MIG M. |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|=========================================+======================+======================|
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| 0 Quadro RTX 5000 Off | 00000000:3B:00.0 Off | Off |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| 33% 38C P0 23W / 230W | 0MiB / 16384MiB | 0% Default |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| | | N/A |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+-----------------------------------------+----------------------+----------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+---------------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| Processes: |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| GPU GI CI PID Type Process name GPU Memory |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| ID ID Usage |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|=======================================================================================|
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| No running processes found |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">+---------------------------------------------------------------------------------------+
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>We can now see that the GPU is accessible in the CT.&lt;/p>
&lt;p>Let&amp;rsquo;s continue with the CUDA installation. Remember to uncheck the driver installation option because we have already installed the drivers above.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">./cuda-linux.12.2.2-535.104.05.run
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>After a successful installation, you should add cuda binaries to PATH. Instructions should be printed at the end of the installation. Then you can run &lt;code>nvcc&lt;/code> to see if CUDA is installed correctly.&lt;/p>
&lt;p>Everything should be working by this point.&lt;/p>
&lt;h2 id="missing-nvidia-uvm-and-high-idle-power-draw">Missing &lt;code>nvidia-uvm&lt;/code> and High Idle Power Draw
&lt;/h2>&lt;p>One problem I encountered is that when the host reboots, the GPU is not accessible in the CT. This is because &lt;code>nvidia-uvm&lt;/code> device isn&amp;rsquo;t created until an application attempts to interact with the graphics card. This is a problem because no application will interact with the GPU at boot, so no &lt;code>nvidia-uvm&lt;/code> device is created. But the CT needs the &lt;code>nvidia-uvm&lt;/code> device bind-mounted at CT-startup in order to access the GPU.&lt;/p>
&lt;p>Also, the graphics card have insanely high power draw at idle (over 100 Watts). The GPU is in P0 and never leaves it. We can use &lt;code>nvidia-persistenced&lt;/code> to let the GPU enter a low-power state (P8) when not in use.&lt;/p>
&lt;p>To solve this, we can run &lt;code>nvidia-smi -pm 1&lt;/code> (which enables &lt;code>nvidia-persistenced&lt;/code> and keeps nvidia character device and handles frequency scaling) at boot. Add the following line to the host&amp;rsquo;s crontab to run &lt;code>nvidia-smi -pm 1&lt;/code> at boot.&lt;/p>
&lt;p>PS: This only works if the host is a headless server (no monitor attached). If you have a monitor attached, you may need to run &lt;code>nvidia-smi&lt;/code> below instead.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> crontab -e
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">@reboot /usr/bin/nvidia-smi -pm 1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="downsides">Downsides
&lt;/h2>&lt;p>Despite the fact that this method works best for us, there are some downsides:&lt;/p>
&lt;ul>
&lt;li>The CT will have full access to the GPU. If one CT uses all the GPU memory, other CTs will be starving. So you must trust the users of the CTs. This is not a problem for us because we know each other.&lt;/li>
&lt;li>Driver updates are a bit more complicated. You need to update the drivers on the host and in all of the CTs. It&amp;rsquo;s best to not update the drivers too often.&lt;/li>
&lt;li>The CTs share the same kernel with the host. To avoid potential compatibility issues, we don&amp;rsquo;t update the kernel unless necessary.&lt;/li>
&lt;/ul></description></item></channel></rss>