<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Charlie Chiang's blog</title><link>https://charlie0129.github.io/blog/</link><description>Recent content on Charlie Chiang's blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 05 Sep 2026 22:00:00 +0800</lastBuildDate><atom:link href="https://charlie0129.github.io/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Building Alpine Linux Disk Images Without a VM</title><link>https://charlie0129.github.io/blog/p/alpine-image-builder/</link><pubDate>Sat, 05 Sep 2026 22:00:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/alpine-image-builder/</guid><description>&lt;p>In &lt;a class="link" href="https://charlie0129.github.io/blog/p/alpine-minimal-btrfs-install/" >Minimal Alpine Linux on a 1 GB Btrfs Root Disk&lt;/a> I wrote down the recipe I use to build a tiny Alpine instance: boot the ISO in a VM, run &lt;code>setup-alpine&lt;/code> with &lt;code>disk=none&lt;/code>, partition by hand, &lt;code>setup-disk -m sys /mnt&lt;/code>, reboot, then &lt;code>qemu-img convert&lt;/code> the disk. It works, and I still use that post as the explanation of &lt;em>why&lt;/em> the pieces are the way they are.&lt;/p>
&lt;p>What I got tired of is the process. Every image costs a VM, a console session, and a sequence of interactive answers, and none of it is reproducible: if I want the same image with ext4 instead of btrfs, I do the whole thing again and hope I remember every step.&lt;/p>
&lt;p>So I replaced it with a script. It builds the disk image as a &lt;strong>file&lt;/strong>, on any Linux host, with no VM and no prompts. It also fixes the two things I did not like about the old recipe: the image now boots under &lt;strong>both BIOS and UEFI&lt;/strong>, and every choice is configurable.&lt;/p>
&lt;p>The bundle for this post contains everything:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="mkalpine.sh" >&lt;code>mkalpine.sh&lt;/code>&lt;/a> — the builder&lt;/li>
&lt;li>&lt;a class="link" href="config.example.sh" >&lt;code>config.example.sh&lt;/code>&lt;/a> — every knob with comments&lt;/li>
&lt;li>&lt;a class="link" href="testboot.sh" >&lt;code>testboot.sh&lt;/code>&lt;/a> — boot the result under QEMU and check it came up&lt;/li>
&lt;li>&lt;a class="link" href="testmatrix.sh" >&lt;code>testmatrix.sh&lt;/code>&lt;/a> — build and boot every supported combination&lt;/li>
&lt;li>&lt;code>hooks/&lt;/code> — the customizations, one file each&lt;/li>
&lt;/ul>
&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">sudo ./mkalpine.sh -f alpine.img
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>That is the whole interface. About a minute later there is a 512 MiB sparse image, 102 MiB of it actually allocated, that boots on either firmware. The image is deliberately small — &lt;code>70-growroot&lt;/code> expands the root filesystem to fill whatever disk it lands on, so &lt;code>IMAGE_SIZE&lt;/code> only has to hold the build.&lt;/p>
&lt;h2 id="why-no-vm-is-needed">Why No VM Is Needed
&lt;/h2>&lt;p>The manual recipe suggests that installing Alpine requires a running Alpine. It does not. Once you look at what the installer actually does, almost all of it is file manipulation:&lt;/p>
&lt;ol>
&lt;li>Unpack a root filesystem.&lt;/li>
&lt;li>&lt;code>apk add&lt;/code> a kernel, an initramfs generator, and a bootloader.&lt;/li>
&lt;li>Write &lt;code>/etc/fstab&lt;/code>, &lt;code>/etc/inittab&lt;/code>, and the network config.&lt;/li>
&lt;li>Run &lt;code>mkinitfs&lt;/code> and &lt;code>grub-install&lt;/code>.&lt;/li>
&lt;li>Enable some OpenRC services, which is &lt;code>ln -s&lt;/code> in a directory.&lt;/li>
&lt;/ol>
&lt;p>Only steps 2 and 4 need to &lt;em>execute&lt;/em> Alpine binaries, and a chroot is enough for that. There is no step that needs a booted kernel, a real block device, or firmware.&lt;/p>
&lt;p>Alpine&amp;rsquo;s own &lt;a class="link" href="https://gitlab.alpinelinux.org/alpine/alpine-conf/-/blob/master/setup-disk.in" target="_blank" rel="noopener"
>&lt;code>setup-disk&lt;/code>&lt;/a> cannot be reused here, unfortunately. It sources &lt;code>libalpine.sh&lt;/code>, calls &lt;code>lbu package&lt;/code>, and assumes throughout that it is running on a booted live system. So &lt;code>mkalpine.sh&lt;/code> reimplements the same steps directly, following the structure of upstream &lt;a class="link" href="https://github.com/alpinelinux/alpine-make-vm-image" target="_blank" rel="noopener"
>&lt;code>alpine-make-vm-image&lt;/code>&lt;/a> and the bootloader logic from &lt;code>setup-disk&lt;/code>.&lt;/p>
&lt;p>The pipeline is:&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-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">truncate&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">s&lt;/span> &lt;span class="mi">512&lt;/span>&lt;span class="n">M&lt;/span> &lt;span class="n">image&lt;/span> &lt;span class="n">create&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="n">sparse&lt;/span> &lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sfdisk&lt;/span> &lt;span class="n">partition&lt;/span> &lt;span class="n">it&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">losetup&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">P&lt;/span> &lt;span class="n">get&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">loopNp1&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="n">p3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">mkfs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vfat&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">mkfs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">btrfs&lt;/span> &lt;span class="n">make&lt;/span> &lt;span class="n">filesystems&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">mount&lt;/span> &lt;span class="n">mount&lt;/span> &lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">then&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">boot&lt;/span> &lt;span class="n">inside&lt;/span> &lt;span class="n">it&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tar&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="n">minirootfs&lt;/span> &lt;span class="n">unpack&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">base&lt;/span> &lt;span class="n">system&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chroot&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">apk&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="n">kernel&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mkinitfs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">grub&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fs&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">mkinitfs&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">grub&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">install&lt;/span> &lt;span class="n">bootloader&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">both&lt;/span> &lt;span class="n">firmwares&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">hooks&lt;/span>&lt;span class="o">/&lt;/span> &lt;span class="n">customizations&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">fstrim&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">umount&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">losetup&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="n">compact&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">release&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">qemu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">img&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">zstd&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">gzip&lt;/span> &lt;span class="n">optional&lt;/span> &lt;span class="n">output&lt;/span> &lt;span class="n">formats&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>QEMU is not needed to build. It is used only for &lt;code>qcow2&lt;/code> output and by the boot tests.&lt;/p>
&lt;p>One line of that pipeline is less innocent than it looks. &lt;code>losetup -P&lt;/code> asks the kernel to scan the partition table, but the &lt;code>/dev/loopNp*&lt;/code> nodes are created by &lt;em>udev&lt;/em>, after &lt;code>losetup&lt;/code> has already exited, so they have to be waited for — and on a host with no udev at all, such as a container whose &lt;code>/dev&lt;/code> is a plain tmpfs, they have to be created by hand from the device numbers the kernel publishes in &lt;code>/sys/block/loopN/loopNpM/dev&lt;/code>. Both paths are in the script, and the interesting part is the order they are tried in: reaching for &lt;code>mknod&lt;/code> early is a mistake, because when udev gets round to the same events it unlinks your node and makes its own, and for the instant in between the path does not exist. On a test host, that instant landed exactly on &lt;code>mkfs.vfat&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;/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">mkfs.vfat: unable to open /dev/loop0p2: No such file or directory
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>So the escalation is now slow on purpose — &lt;code>udevadm settle&lt;/code>, a second, &lt;code>partx -a&lt;/code>, another second, and only then &lt;code>mknod&lt;/code> — and once the nodes do appear there is one more &lt;code>settle&lt;/code> before anything opens them, so a replacement in flight finishes before &lt;code>mkfs&lt;/code> runs rather than during it.&lt;/p>
&lt;h2 id="the-disk-layout">The Disk Layout
&lt;/h2>&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">GPT (default; the protective MBR still carries GRUB&amp;#39;s boot.img)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> # size type mount contents
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 1 MiB ef02 BIOS boot - grub core.img (x86_64 only)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 64 MiB ef00 ESP FAT32 /boot vmlinuz-virt, initramfs-virt,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> grub/, EFI/BOOT/BOOTX64.EFI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 rest L Linux / btrfs (default), ext4 or xfs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">BIOS : firmware -&amp;gt; MBR boot.img -&amp;gt; p1 core.img -&amp;gt; /boot/grub/grub.cfg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UEFI : firmware -&amp;gt; p2 /EFI/BOOT/BOOTX64.EFI -&amp;gt; /boot/grub/grub.cfg
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Two details are worth calling out.&lt;/p>
&lt;p>&lt;strong>&lt;code>/boot&lt;/code> is the ESP.&lt;/strong> This is not just to save a partition. It means GRUB only ever has to read FAT. Btrfs, ext4 and XFS roots all work without GRUB parsing them at all, and no on-disk feature the root filesystem gains later can break the bootloader. The old post used Syslinux on ext4 &lt;code>/boot&lt;/code>; this is less fragile.&lt;/p>
&lt;p>&lt;strong>GRUB is installed to the removable path.&lt;/strong> &lt;code>grub-install --removable --no-nvram&lt;/code> writes &lt;code>EFI/BOOT/BOOTX64.EFI&lt;/code> (or &lt;code>BOOTAA64.EFI&lt;/code>), which is what firmware boots when NVRAM has no entry for the disk. We cannot write the target machine&amp;rsquo;s NVRAM from a build host anyway, and cloud firmware is generally starting from a blank slate.&lt;/p>
&lt;p>On aarch64 the BIOS partition is omitted and only UEFI is set up: Alpine ships &lt;code>grub-efi&lt;/code> for arm64 but there is no &lt;code>grub-bios&lt;/code>, because the &lt;code>i386-pc&lt;/code> target is x86-only.&lt;/p>
&lt;h3 id="the-mbr-variant">The MBR Variant
&lt;/h3>&lt;p>Some providers&amp;rsquo; image import still rejects GPT. &lt;code>PARTITION_TABLE=mbr&lt;/code> handles that:&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">DOS/MBR
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> # start type mount contents
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - sector 0 MBR + gap - grub boot.img (sector 0) and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> core.img (the ~1 MiB gap before p1)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 2048 0xEF, bootable /boot same as above
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 after p1 0x83 / root
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>No dedicated BIOS boot partition is needed, because GRUB&amp;rsquo;s &lt;code>i386-pc&lt;/code> target embeds &lt;code>core.img&lt;/code> in the gap between the MBR and the first partition — which is about 1 MiB, given the 2048-sector start. UEFI still works on most firmware, since the ESP is located by partition type &lt;code>0xEF&lt;/code>. That is a widely followed convention rather than something the spec promises, so MBR mode is &amp;ldquo;BIOS guaranteed, UEFI very likely&amp;rdquo;, and GPT stays the default.&lt;/p>
&lt;h2 id="what-it-trusts">What It Trusts
&lt;/h2>&lt;p>The bootstrap is a single &lt;code>alpine-minirootfs&lt;/code> tarball, resolved from &lt;code>latest-releases.yaml&lt;/code> for the branch:&lt;/p>
&lt;ul>
&lt;li>TLS gets the file from the mirror.&lt;/li>
&lt;li>Alpine publishes a &lt;code>.sha256&lt;/code> sidecar next to every release artifact. The build fails and deletes the download on a mismatch.&lt;/li>
&lt;li>Everything after that is &lt;code>apk&lt;/code>, with Alpine&amp;rsquo;s signing keys, from the tarball&amp;rsquo;s own keyring.&lt;/li>
&lt;/ul>
&lt;p>I use the minirootfs rather than a pinned &lt;code>apk.static&lt;/code> deliberately: a hardcoded &lt;code>apk.static&lt;/code> checksum drifts out of sync with the branch, and the in-image &lt;code>apk&lt;/code> always matching the branch matters now that 3.23+ ships apk-tools 3.&lt;/p>
&lt;p>&lt;code>ALPINE_BRANCH=latest-stable&lt;/code> is the default and gets resolved once, at build time. The repositories written &lt;em>into&lt;/em> the image always point at the concrete branch (&lt;code>v3.24&lt;/code>), never at &lt;code>latest-stable&lt;/code>, so a later &lt;code>apk upgrade&lt;/code> on the running machine does not silently jump to the next stable release.&lt;/p>
&lt;h2 id="configuration">Configuration
&lt;/h2>&lt;p>Copy the example and edit it; &lt;code>./config.sh&lt;/code> is picked up automatically.&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">cp config.example.sh config.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$EDITOR&lt;/span> config.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo ./mkalpine.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Every variable is &lt;code>: &amp;quot;${VAR:=default}&amp;quot;&lt;/code> in the script, so the environment works too, which is what I use for one-offs:&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">sudo &lt;span class="nv">IMAGE_SIZE&lt;/span>&lt;span class="o">=&lt;/span>2G &lt;span class="nv">ROOT_FS&lt;/span>&lt;span class="o">=&lt;/span>xfs ./mkalpine.sh out.img
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Secrets default to &lt;em>nothing&lt;/em>. &lt;code>ROOT_PASSWORD_HASH=&amp;quot;&amp;quot;&lt;/code> locks the root account, so the failure mode of forgetting to set it is &amp;ldquo;cannot log in&amp;rdquo;, not &amp;ldquo;known root password&amp;rdquo;, and the example hash ships commented out next to the &lt;code>openssl passwd -6&lt;/code> that generates one. &lt;code>config.sh&lt;/code> and the image files are in &lt;code>.gitignore&lt;/code>.&lt;/p>
&lt;p>If the build host reaches the internet through a proxy, an exported &lt;code>http_proxy&lt;/code> / &lt;code>https_proxy&lt;/code> / &lt;code>no_proxy&lt;/code> is picked up as-is, and &lt;code>BUILD_HTTP_PROXY&lt;/code> / &lt;code>BUILD_HTTPS_PROXY&lt;/code> / &lt;code>BUILD_NO_PROXY&lt;/code> set one for the build alone. It covers both halves of the download: the minirootfs tarball on the host, and &lt;code>apk&lt;/code> plus whatever a hook fetches inside the chroot. Both cases of each name are set in the chroot&amp;rsquo;s environment, because curl reads only the lowercase &lt;code>http_proxy&lt;/code> while apk and git take either — and they go into the &lt;em>environment&lt;/em> rather than a profile script, so nothing about the proxy survives into the image. A proxy reachable from the build host is usually not reachable from wherever the image is deployed, and the URL frequently carries credentials.&lt;/p>
&lt;h2 id="choosing-a-root-filesystem">Choosing A Root Filesystem
&lt;/h2>&lt;p>&lt;code>ROOT_FS&lt;/code> takes &lt;code>btrfs&lt;/code>, &lt;code>ext4&lt;/code> or &lt;code>xfs&lt;/code>, each with its own mkfs and mount option strings:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;code>ROOT_FS&lt;/code>&lt;/th>
&lt;th>&lt;code>ROOT_MKFS_OPTS&lt;/code>&lt;/th>
&lt;th>&lt;code>ROOT_MOUNT_OPTS&lt;/code>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>btrfs&lt;/code>&lt;/td>
&lt;td>&lt;code>-L alpine-root -K&lt;/code>&lt;/td>
&lt;td>&lt;code>rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ext4&lt;/code>&lt;/td>
&lt;td>&lt;code>-L alpine-root -m 1 -E nodiscard&lt;/code>&lt;/td>
&lt;td>&lt;code>rw,noatime,commit=60&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>xfs&lt;/code>&lt;/td>
&lt;td>&lt;code>-L alpine-root&lt;/code>&lt;/td>
&lt;td>&lt;code>rw,noatime,logbsize=256k&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>-K&lt;/code> and &lt;code>-E nodiscard&lt;/code> skip the discard pass at mkfs time, which is pointless on a sparse file and only produces confusing errors.&lt;/p>
&lt;p>Btrfs with zstd stays the default for the same reason as in the old post — on a small disk, transparent compression is worth a lot.&lt;/p>
&lt;h3 id="tuning-mkfs-for-the-storage-underneath">Tuning mkfs For The Storage Underneath
&lt;/h3>&lt;p>The point of exposing &lt;code>ROOT_MKFS_OPTS&lt;/code> is that a cloud disk is rarely a plain disk. &lt;code>config.example.sh&lt;/code> carries these examples.&lt;/p>
&lt;p>Aligning to a 16 KiB ZFS zvol (&lt;code>volblocksize=16k&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ext4: the block size cannot exceed the kernel page size (4 KiB on x86_64),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># so -b 16384 produces a filesystem that will not mount. Align with&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># stride/stripe_width instead: stride = 16K / 4K = 4.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MKFS_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-L alpine-root -m 1 -E nodiscard,stride=4,stripe_width=4&amp;#34;&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="c1"># or, if you really do want 16 KiB allocation clusters:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MKFS_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-L alpine-root -m 1 -O bigalloc -C 16384&amp;#34;&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="c1"># xfs: takes the stripe unit directly.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MKFS_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-L alpine-root -d su=16k,sw=1&amp;#34;&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="c1"># btrfs: nodesize is already 16K; set the sector size explicitly if the host&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># page size differs from the target&amp;#39;s.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MKFS_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-L alpine-root -K -s 4096&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Aligning to RAID6 with 6 data disks and a 128 KiB chunk:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ext4: stride = chunk / block = 128K / 4K = 32&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># stripe_width = stride * data disks = 32 * 4 = 128&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MKFS_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-L alpine-root -m 1 -E nodiscard,stride=32,stripe_width=128&amp;#34;&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="c1"># xfs: su = chunk, sw = number of data disks&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MKFS_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-L alpine-root -d su=128k,sw=4&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="tuning-mount-options">Tuning Mount Options
&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;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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Favour throughput over sync latency. You lose more recent writes on an&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># unclean shutdown, which is fine for a rebuildable instance and not fine&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># for a database.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MOUNT_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;rw,noatime,commit=60,data=writeback&amp;#34;&lt;/span> &lt;span class="c1"># ext4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MOUNT_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;rw,noatime,logbsize=256k,allocsize=1m&amp;#34;&lt;/span> &lt;span class="c1"># xfs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MOUNT_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;rw,noatime,commit=120,compress=zstd:1,ssd,discard=async,space_cache=v2&amp;#34;&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="c1"># Favour density on a tiny disk. zstd:6 costs CPU on write; decompression&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># stays cheap at any level.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MOUNT_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;rw,noatime,compress=zstd:6,ssd,discard=async,space_cache=v2&amp;#34;&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="c1"># Drop discard=async and rely on the weekly fstrim job instead, if your&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># provider&amp;#39;s thin pool behaves badly with continuous discards.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT_MOUNT_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;rw,noatime,compress=zstd:3,ssd,space_cache=v2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>These options are used to mount the image &lt;em>during the build&lt;/em> as well as being written to &lt;code>/etc/fstab&lt;/code>, so a typo fails the build instead of the first boot. There is exactly one deliberate difference between the two, and it is the subject of &lt;a class="link" href="#compression-needs-forcing-at-build-time" >Compression Needs Forcing At Build Time&lt;/a> below.&lt;/p>
&lt;h3 id="the-mount-option-gotcha">The Mount Option Gotcha
&lt;/h3>&lt;p>This one cost me a while, and it is the reason the boot test in the bundle checks mount options rather than trusting them.&lt;/p>
&lt;p>&lt;code>ROOT_MOUNT_OPTS&lt;/code> in &lt;code>/etc/fstab&lt;/code> is &lt;strong>not enough&lt;/strong>. The root filesystem is mounted by the initramfs, long before &lt;code>/etc/fstab&lt;/code> exists, and the &lt;code>mount -o remount,rw /&lt;/code> that OpenRC does afterwards cannot change every option. Most are remountable, so &lt;code>noatime&lt;/code> and &lt;code>commit=60&lt;/code> looked fine. XFS, however, fixes the log buffer size at initial mount:&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"># what I asked for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ROOT_MOUNT_OPTS=&amp;#34;rw,noatime,logbsize=256k&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># what the running machine actually had
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rw,noatime,inode64,logbufs=8,logbsize=32k,noquota
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Silently downgraded to the 32k default, with nothing in &lt;code>dmesg&lt;/code> about it. Mounting the same image on the build host with the same options honoured &lt;code>logbsize=256k&lt;/code>, which made it look like a kernel difference rather than what it was.&lt;/p>
&lt;p>The fix is to put the options on the kernel command line too, so that the &lt;em>initial&lt;/em> mount is the one I want:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOTFLAGS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$FSTAB_ROOT_OPTS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;,&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> grep -vx -e rw -e ro &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;,&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> sed &lt;span class="s1">&amp;#39;s/,*$//&amp;#39;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span> -n &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$ROOTFLAGS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nv">CMDLINE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$CMDLINE&lt;/span>&lt;span class="s2"> rootflags=&lt;/span>&lt;span class="nv">$ROOTFLAGS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>rw&lt;/code> and &lt;code>ro&lt;/code> are dropped because the kernel handles those separately and GRUB already passes &lt;code>ro&lt;/code>. With Btrfs you end up with two &lt;code>rootflags=&lt;/code> on the cmdline, because &lt;code>grub-mkconfig&lt;/code>&amp;rsquo;s &lt;code>10_linux&lt;/code> detects the subvolume and emits its own; ours is appended after GRUB&amp;rsquo;s and the initramfs takes the last one.&lt;/p>
&lt;p>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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">rw,noatime,inode64,logbufs=8,logbsize=256k,noquota
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="btrfs-subvolumes">Btrfs Subvolumes
&lt;/h3>&lt;p>&lt;code>BTRFS_SUBVOL=&amp;quot;@&amp;quot;&lt;/code> by default. It costs three lines at build time, and retrofitting a root subvolume later means moving every file, so it is worth doing even on a machine too small to keep many snapshots. Compression is also set with &lt;code>btrfs property set&lt;/code>, so it holds regardless of mount options. &lt;code>BTRFS_SUBVOL=&amp;quot;&amp;quot;&lt;/code> gives the top-level layout from the old post. GRUB does not care either way, because &lt;code>/boot&lt;/code> is FAT.&lt;/p>
&lt;h3 id="compression-needs-forcing-at-build-time">Compression Needs Forcing At Build Time
&lt;/h3>&lt;p>&lt;code>compress=zstd:3&lt;/code> does not compress everything. Btrfs decides per file, from the beginning of the file, whether compressing is worth it — and on ELF binaries it decides wrong. In a default build, 45 MiB out of 83 MiB was stored uncompressed, including &lt;code>libcrypto.so.3&lt;/code> (3.3 MiB), every &lt;code>grub-*&lt;/code> tool, &lt;code>busybox&lt;/code> and &lt;code>ld-musl&lt;/code>. All of those compress to roughly half. &lt;code>btrfs property set&lt;/code> does not help here either: a file carrying only the inode flag still goes through the same heuristic.&lt;/p>
&lt;p>&lt;code>compress-force&lt;/code> skips the decision. The build mounts the root with it, while &lt;code>/etc/fstab&lt;/code> and &lt;code>rootflags=&lt;/code> keep plain &lt;code>compress=&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BUILD_ROOT_OPTS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">printf&lt;/span> &lt;span class="s1">&amp;#39;%s&amp;#39;&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$ROOT_MOUNT_OPTS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sed &lt;span class="s1">&amp;#39;s/^compress=/compress-force=/; s/,compress=/,compress-force=/&amp;#39;&lt;/span>&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>That is the one place where what the build mounts deliberately differs from what the image ships. It takes 62 MiB of data on disk down to 56 MiB, the raw image from 108 MiB to 102 MiB, and &lt;code>df&lt;/code> on the booted machine from 74.5 MiB used to 67.9 MiB; &lt;code>BTRFS_FORCE_COMPRESS=no&lt;/code> restores the old behaviour. The build prints what it achieved —&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">root data 55M on disk for 86M of files (64%)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>— because the answer depends on what the image installs, and the failure mode is otherwise silent: the mount options, and &lt;code>findmnt&lt;/code>, look exactly the same whether the files got compressed or not.&lt;/p>
&lt;p>The heuristic is left alone for the &lt;em>running&lt;/em> system on purpose. It exists to avoid burning CPU on data that will not compress, and this image contains about 10 MiB of exactly that: Alpine ships kernel modules as &lt;code>.ko.gz&lt;/code>, and &lt;code>xfs.ko.gz&lt;/code>, &lt;code>btrfs.ko.gz&lt;/code> and friends stay uncompressed whether you force it or not. Forcing only recovers the files the heuristic misjudged.&lt;/p>
&lt;p>Running &lt;code>btrfs filesystem defragment -r -czstd&lt;/code> over the finished tree is the other way to get here, and it is the worse one. It lands at 58 MiB rather than 56 MiB, the sparse image balloons to 168 MiB before &lt;code>fstrim&lt;/code> claws it back, and despite the name it does not defragment anything — compressed extents cap at 128 KiB, so the extent count went &lt;em>up&lt;/em>, 1996 → 2082.&lt;/p>
&lt;p>One caveat if you ship a compressed artifact rather than the raw image: compressing inside the image makes the outer compressor&amp;rsquo;s job harder, and the outer compressor is better at it. Forcing shrank the raw image by 5.6% and grew every compressed output — &lt;code>raw.zst&lt;/code> 74 → 77 MiB, &lt;code>raw.xz&lt;/code> 72 → 76 MiB. If upload size is the thing you actually care about, &lt;code>BTRFS_FORCE_COMPRESS=no&lt;/code> is the right setting, and the build says as much when you ask for both.&lt;/p>
&lt;h2 id="sizing-boot">Sizing &lt;code>/boot&lt;/code>
&lt;/h2>&lt;p>&lt;code>BOOT_SIZE=64M&lt;/code>. Measured usage with one kernel is 36 MiB:&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">13 M vmlinuz-virt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">9.4M initramfs-virt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">8.3M grub/ (i386-pc and x86_64-efi modules)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6.2M System.map-6.18.48-0-virt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">156K EFI/BOOT/BOOTX64.EFI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">149K config-6.18.48-0-virt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>That leaves headroom for one kernel and no more. Raise it to &lt;code>128M&lt;/code> if you want to keep a second kernel around — &lt;code>linux-lts&lt;/code> alongside &lt;code>linux-virt&lt;/code>, say — or if you want the previous kernel to survive an &lt;code>apk upgrade&lt;/code> so there is something to fall back to. The build prints &lt;code>/boot&lt;/code> usage at the end and warns when it lands above 80%.&lt;/p>
&lt;h2 id="output-formats">Output Formats
&lt;/h2>&lt;p>&lt;code>OUTPUT_FORMATS&lt;/code> is a space-separated list; each entry produces one file next to the raw image.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Entry&lt;/th>
&lt;th>Produced by&lt;/th>
&lt;th style="text-align: right">Size&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>raw&lt;/code>&lt;/td>
&lt;td>the build itself&lt;/td>
&lt;td style="text-align: right">102 MiB&lt;/td>
&lt;td>sparse; 512 MiB apparent&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>qcow2&lt;/code>&lt;/td>
&lt;td>&lt;code>qemu-img convert -c -O qcow2&lt;/code>&lt;/td>
&lt;td style="text-align: right">81 MiB&lt;/td>
&lt;td>compressed qcow2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>raw.zst&lt;/code>&lt;/td>
&lt;td>&lt;code>zstd -19 -T0&lt;/code>&lt;/td>
&lt;td style="text-align: right">77 MiB&lt;/td>
&lt;td>best ratio for the time; widely accepted&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>raw.gz&lt;/code>&lt;/td>
&lt;td>&lt;code>gzip -9&lt;/code>&lt;/td>
&lt;td style="text-align: right">80 MiB&lt;/td>
&lt;td>widest provider support&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>raw.xz&lt;/code>&lt;/td>
&lt;td>&lt;code>xz -9 -T0&lt;/code>&lt;/td>
&lt;td style="text-align: right">76 MiB&lt;/td>
&lt;td>smallest, slowest&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The sizes are from one build of the same default btrfs image, so they are comparable to each other rather than absolute. Note how little the four compressed formats differ — 76 to 81 MiB, under 7% between the best and the worst: the root filesystem is &lt;em>already&lt;/em> zstd-compressed, so the outer compressor is mostly squeezing free space. &lt;code>raw.gz&lt;/code> is a perfectly reasonable default in exchange for its compatibility.&lt;/p>
&lt;p>That is also why &lt;code>BTRFS_FORCE_COMPRESS&lt;/code> cuts the other way here. Every one of these numbers except &lt;code>qcow2&lt;/code> is &lt;em>larger&lt;/em> than it would be with forcing off — &lt;code>raw.xz&lt;/code> most of all, 76 MiB against 72 MiB — because data that btrfs already compressed at zstd:3 in 128 KiB blocks is data &lt;code>xz -9&lt;/code> cannot compress again. Forcing wins on &lt;code>raw&lt;/code> and loses on everything else, so the build prints a reminder when you ask for both.&lt;/p>
&lt;p>Drop &lt;code>raw&lt;/code> from the list to keep only the compressed artifacts. For other hypervisors, convert the raw image yourself:&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">qemu-img convert -O vmdk out.img out.vmdk &lt;span class="c1"># VMware&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">qemu-img convert -O vpc out.img out.vhd &lt;span class="c1"># Hyper-V / Azure&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">qemu-img convert -O vdi out.img out.vdi &lt;span class="c1"># VirtualBox&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="what-must-not-survive-cloning">What Must Not Survive Cloning
&lt;/h2>&lt;p>A disk image is a template that gets cloned N times, so anything unique baked into it stops being unique. This is the part a hand-built VM image usually gets wrong, and it is why I wanted a script in the first place — I am not going to remember all of this at 1am on a provider&amp;rsquo;s web console.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Thing&lt;/th>
&lt;th>Why it matters&lt;/th>
&lt;th>What the script does&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>/etc/ssh/ssh_host_*&lt;/code>&lt;/td>
&lt;td>Every VM built from the image shares one host key. Anyone holding the image can impersonate all of them, and clients get key-mismatch warnings after the first deploy.&lt;/td>
&lt;td>Delete. Regenerated on first boot.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/var/lib/seedrng/seed.credential&lt;/code> (3.17+; &lt;code>/var/lib/random-seed&lt;/code> before)&lt;/td>
&lt;td>The saved RNG seed is restored early at boot. An identical seed on every clone means the entropy pool starts identical — including for the host keys in the row above.&lt;/td>
&lt;td>Delete.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/etc/machine-id&lt;/code>&lt;/td>
&lt;td>Used to derive DHCP DUIDs and app-level instance identity, so clones can collide on DHCP leases.&lt;/td>
&lt;td>Truncate to &lt;strong>zero bytes&lt;/strong>, not delete: an empty file is the documented &amp;ldquo;generate on next boot&amp;rdquo; signal, while a missing one makes some tools fail instead.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/etc/resolv.conf&lt;/code>&lt;/td>
&lt;td>Would otherwise ship the build host&amp;rsquo;s nameservers.&lt;/td>
&lt;td>Rewritten from &lt;code>$DNS&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/var/cache/apk/*&lt;/code>, &lt;code>/var/log/*&lt;/code>, shell history&lt;/td>
&lt;td>Dead weight and build-host leakage.&lt;/td>
&lt;td>Cleared.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Filesystem UUIDs &lt;em>do&lt;/em> stay identical across clones. Regenerating them on first boot means rewriting &lt;code>fstab&lt;/code> and &lt;code>grub.cfg&lt;/code> from a running system, which is more fragile than the problem it solves — it only bites if you attach two clones to the same host.&lt;/p>
&lt;p>The &lt;code>80-firstboot&lt;/code> hook is the other half of this. It runs in the &lt;code>boot&lt;/code> runlevel, before &lt;code>networking&lt;/code> and &lt;code>sshd&lt;/code>, and generates what was stripped:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">head -c &lt;span class="m">16&lt;/span> /dev/urandom &lt;span class="p">|&lt;/span> od -An -tx1 &lt;span class="p">|&lt;/span> tr -d &lt;span class="s1">&amp;#39; \n&amp;#39;&lt;/span> &amp;gt;/etc/machine-id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh-keygen -A
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>It also runs &lt;code>after hostname&lt;/code>, so the host keys are commented &lt;code>root@alpine&lt;/code> rather than &lt;code>root@(none)&lt;/code>. Cosmetic, but the alternative bothered me.&lt;/p>
&lt;p>Finally, the build stamps &lt;code>/etc/image-release&lt;/code> with the Alpine version, architecture, build date, builder git commit, and the resolved filesystem and hook configuration. Six months later, working out which build a running VM came from is otherwise guesswork.&lt;/p>
&lt;h2 id="staying-able-to-get-in">Staying Able To Get In
&lt;/h2>&lt;p>The single most valuable property of a cloud image is that you can get into it when networking is broken, because the provider&amp;rsquo;s serial console is then your only channel.&lt;/p>
&lt;ul>
&lt;li>&lt;code>console=ttyS0,115200&lt;/code> on x86_64, &lt;code>console=ttyAMA0,115200&lt;/code> on aarch64, with the getty in &lt;code>/etc/inittab&lt;/code> &lt;strong>and&lt;/strong> the tty listed in &lt;code>/etc/securetty&lt;/code>. Without the &lt;code>securetty&lt;/code> entry, root login on that console is refused and looks exactly like a wrong password.&lt;/li>
&lt;li>&lt;code>GRUB_TERMINAL=&amp;quot;console serial&amp;quot;&lt;/code> plus &lt;code>GRUB_SERIAL_COMMAND&lt;/code>, so the &lt;em>bootloader&lt;/em> is reachable over serial too. That is what lets you fix a bad kernel cmdline or boot an older kernel remotely.&lt;/li>
&lt;li>&lt;strong>No &lt;code>quiet&lt;/code>.&lt;/strong> Alpine&amp;rsquo;s &lt;code>setup-disk&lt;/code> defaults to &lt;code>KERNELOPTS=quiet&lt;/code>; on a machine whose only debugging channel is the serial console, hiding the boot log is the wrong trade.&lt;/li>
&lt;li>&lt;code>GRUB_TIMEOUT=1&lt;/code> rather than &lt;code>0&lt;/code>, so there is a window to interrupt.&lt;/li>
&lt;/ul>
&lt;p>&lt;code>grub.cfg&lt;/code> is generated by &lt;code>grub-mkconfig&lt;/code> rather than hand-written, which matters more than it looks: Alpine&amp;rsquo;s grub package carries &lt;code>triggers=&amp;quot;grub.trigger=/boot&amp;quot;&lt;/code>, so a later &lt;code>apk upgrade linux-virt&lt;/code> regenerates it and the image stays bootable with no intervention. A static config would just be silently overwritten by that same trigger. There is a hand-written fallback for the case where &lt;code>grub-probe&lt;/code> cannot cope with the loop device, but I have not needed it.&lt;/p>
&lt;h2 id="hooks">Hooks
&lt;/h2>&lt;p>One ordered list, in &lt;code>HOOKS&lt;/code>. Remove a name to disable it; drop a file into &lt;code>hooks/&lt;/code> and add its name to extend. Each hook is a standalone &lt;code>sh&lt;/code> script run inside the chroot with the configuration exported into its environment.&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">HOOKS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;10-network 20-ssh 30-chrony 40-zram 50-logtruncate 60-sysctl 70-growroot 80-firstboot&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Hook&lt;/th>
&lt;th>Does&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>10-network&lt;/code>&lt;/td>
&lt;td>&lt;code>/etc/network/interfaces&lt;/code>, DHCP or static, optional real DHCPv6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>20-ssh&lt;/code>&lt;/td>
&lt;td>&lt;code>PermitRootLogin&lt;/code>, port, keepalives, &lt;code>authorized_keys&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>30-chrony&lt;/code>&lt;/td>
&lt;td>chrony with &lt;code>makestep 1.0 -1&lt;/code> and a configurable pool&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>40-zram&lt;/code>&lt;/td>
&lt;td>zram swap sized from RAM at boot, optionally &lt;code>/tmp&lt;/code> too&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>50-logtruncate&lt;/code>&lt;/td>
&lt;td>the hourly log cap from the old post, plus a daily apk cache clean&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>60-sysctl&lt;/code>&lt;/td>
&lt;td>zram VM tunables, BBR and fq&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>70-growroot&lt;/code>&lt;/td>
&lt;td>one-shot service: &lt;code>growpart&lt;/code>, then grow the filesystem&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>80-firstboot&lt;/code>&lt;/td>
&lt;td>machine-id and SSH host key generation&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Off by default, because they are opinionated rather than essential: &lt;code>90-tools&lt;/code> (bash, coreutils, iproute2, tcpdump, htop, tmux, vim and friends — about 120 MiB), &lt;code>91-ufw&lt;/code>, &lt;code>92-sshguard&lt;/code>, &lt;code>93-podman&lt;/code>, &lt;code>94-cloud-init&lt;/code>, and &lt;code>95-dotfiles&lt;/code> (git, zsh, lsd and a dotfiles repo, with zsh as root&amp;rsquo;s login shell).&lt;/p>
&lt;p>&lt;code>91-ufw&lt;/code>, &lt;code>92-sshguard&lt;/code> and &lt;code>95-dotfiles&lt;/code> are cheap enough to add at the default &lt;code>IMAGE_SIZE&lt;/code> on any filesystem. The heavier three are where the compression default starts paying for itself: &lt;code>90-tools&lt;/code> + &lt;code>93-podman&lt;/code> + &lt;code>94-cloud-init&lt;/code> together fit on btrfs at 512 MiB — 274 MiB allocated — and run the 381 MiB XFS root out of space partway through installing podman. Raise &lt;code>IMAGE_SIZE&lt;/code> if you want them on ext4 or XFS.&lt;/p>
&lt;p>A few implementation notes:&lt;/p>
&lt;p>&lt;strong>&lt;code>20-ssh&lt;/code> rewrites existing lines rather than appending.&lt;/strong> &lt;code>sshd_config&lt;/code> takes the &lt;em>first&lt;/em> occurrence of a keyword, so appending &lt;code>PermitRootLogin prohibit-password&lt;/code> to a file that already contains a commented-out default works, and appending it to one that has an active setting silently does nothing. The hook edits in place.&lt;/p>
&lt;p>&lt;strong>&lt;code>50-logtruncate&lt;/code> truncates rather than renames.&lt;/strong> Keeping one &lt;code>.0&lt;/code> copy and then &lt;code>truncate -s 0&lt;/code> on the original preserves the inode, so daemons holding the file open keep writing to it. This is the script from the old post, unchanged.&lt;/p>
&lt;p>&lt;strong>&lt;code>70-growroot&lt;/code> uses &lt;code>growpart&lt;/code>, not &lt;code>sfdisk&lt;/code>.&lt;/strong> Growing a GPT disk also requires relocating the backup header to the new end of the device, and &lt;code>growpart&lt;/code> keeps the partition&amp;rsquo;s &lt;em>start&lt;/em> sector untouched, so whatever alignment the image was built with survives. After that it is &lt;code>btrfs filesystem resize max /&lt;/code>, &lt;code>resize2fs&lt;/code> or &lt;code>xfs_growfs&lt;/code> depending on &lt;code>ROOT_FS&lt;/code>, then a stamp file and &lt;code>rc-update del growroot default&lt;/code>.&lt;/p>
&lt;p>&lt;strong>&lt;code>91-ufw&lt;/code> exploits the fact that ufw only touches netfilter when enabled.&lt;/strong> All the rules are recorded inside the chroot with &lt;code>ufw&lt;/code> itself, and then &lt;code>ENABLED=yes&lt;/code> is set in &lt;code>ufw.conf&lt;/code> as the last step. No netfilter calls happen during the build.&lt;/p>
&lt;p>&lt;strong>&lt;code>95-dotfiles&lt;/code> is the only hook that needs the network for something other than the Alpine mirror.&lt;/strong> It shallow-clones &lt;code>DOTFILES_REPO&lt;/code>, runs the repo&amp;rsquo;s own &lt;code>bootstrap.sh&lt;/code>, and switches root&amp;rsquo;s login shell. Two parts of that were more interesting than expected.&lt;/p>
&lt;p>Changing the login shell, first: &lt;code>chsh&lt;/code> is not in a minimal Alpine — it lives in &lt;code>shadow&lt;/code>, which nothing else here wants — so the hook rewrites field 7 of root&amp;rsquo;s &lt;code>/etc/passwd&lt;/code> line directly. Writing the new file elsewhere and &lt;code>cat&lt;/code>-ing it back keeps the original inode, mode and owner, which a &lt;code>mv&lt;/code> would not:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">awk -F: -v &lt;span class="nv">OFS&lt;/span>&lt;span class="o">=&lt;/span>: -v &lt;span class="nv">shell&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$login_shell&lt;/span>&lt;span class="s2">&amp;#34;&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> &lt;span class="s1">&amp;#39;$1 == &amp;#34;root&amp;#34; { $7 = shell } { print }&amp;#39;&lt;/span> /etc/passwd &amp;gt;/tmp/passwd.new
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat /tmp/passwd.new &amp;gt;/etc/passwd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Second, my dotfiles use &lt;a class="link" href="https://github.com/romkatv/zsh4humans" target="_blank" rel="noopener"
>zsh4humans&lt;/a>, which installs itself — plugins, plus prebuilt &lt;code>fzf&lt;/code> and &lt;code>gitstatusd&lt;/code> binaries — the first time an interactive zsh starts. Doing that during the build instead buys two things: the shell works on a machine with no internet, and a download failure fails the build rather than the first login.&lt;/p>
&lt;p>It costs about 67 MiB of files — 26 MiB of packages, 5 MiB of checkout, 28 MiB of z4h cache — which is 21 MiB of actual disk on the compressed btrfs default. &lt;code>99-selftest&lt;/code> checks it the way that matters: it starts an interactive zsh on the booted machine and confirms the repo&amp;rsquo;s &lt;code>.zshrc&lt;/code> really was sourced.&lt;/p>
&lt;p>Being the only hook that talks to GitHub also makes it the one most likely to fail a build. Set &lt;code>http_proxy&lt;/code> and &lt;code>https_proxy&lt;/code> when the path to GitHub is reliably bad rather than occasionally bad.&lt;/p>
&lt;p>&lt;strong>Weekly &lt;code>fstrim&lt;/code> is installed regardless of &lt;code>ROOT_FS&lt;/code>.&lt;/strong> Although a btrfs root already trims itself through &lt;code>discard=async&lt;/code>, the job is not all about the root filesystem. A data disk attached to the instance later is quite likely to be ext4 or XFS, and nothing else in the image would ever trim it. For those, periodic trim is the currently recommended approach over &lt;code>-o discard&lt;/code>.&lt;/p>
&lt;p>The job also cannot be a one-liner, because a minimal Alpine image does not have util-linux:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">fstrim -a 2&amp;gt;/dev/null &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">exit&lt;/span> &lt;span class="m">0&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">awk &lt;span class="s1">&amp;#39;$1 ~ /^\/dev\// &amp;amp;&amp;amp; !seen[$2]++ { print $2 }&amp;#39;&lt;/span> /proc/mounts &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span> &lt;span class="nv">IFS&lt;/span>&lt;span class="o">=&lt;/span> &lt;span class="nb">read&lt;/span> -r mp&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fstrim &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$mp&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> 2&amp;gt;/dev/null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>fstrim -a&lt;/code> walks every mounted filesystem and de-duplicates devices, but that is util-linux&amp;rsquo;s &lt;code>fstrim&lt;/code>. Busybox&amp;rsquo;s applet takes exactly one mount point and has no &lt;code>-a&lt;/code> at all, so on the image as built the &lt;code>-a&lt;/code> form fails and the loop does the work. My original &lt;code>fstrim -a || fstrim /&lt;/code> looked like it handled that and did not: the fallback trimmed only the root, which is precisely the filesystem that needed it least.&lt;/p>
&lt;h3 id="writing-your-own">Writing Your Own
&lt;/h3>&lt;p>Drop a script in &lt;code>hooks/&lt;/code>, add its name to &lt;code>HOOKS&lt;/code>. The whole &lt;code>hooks/&lt;/code> directory is copied into the chroot at &lt;code>/tmp/mkalpine&lt;/code>, so a hook that needs to install a longer file can keep it in &lt;code>hooks/files/&lt;/code> and copy it from &lt;code>/tmp/mkalpine/files/&lt;/code> rather than embedding it in a heredoc:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="nb">set&lt;/span> -eu
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apk add --quiet --no-progress my-thing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">install -m &lt;span class="m">0644&lt;/span> /tmp/mkalpine/files/my-thing.conf /etc/my-thing.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-update add my-thing default
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The number prefixes are a label, not a mechanism. This is not &lt;code>run-parts&lt;/code>: nothing sorts the directory, and the run order is simply the order of the &lt;code>HOOKS&lt;/code> list, so &lt;code>HOOKS=&amp;quot;10-network my-thing 80-firstboot&amp;quot;&lt;/code> does exactly what it looks like. Your hook can be called anything, go anywhere in the list, and it does not matter that the shipped extras have crowded the 90s — &lt;code>99-selftest&lt;/code> runs last because it is last in the list, not because of its number.&lt;/p>
&lt;p>Two things about the environment a hook runs in. Everything in &lt;code>config.sh&lt;/code> is exported into it, but nothing else is: the chroot starts from &lt;code>env -i&lt;/code>, so a hook sees &lt;code>PATH&lt;/code>, &lt;code>HOME&lt;/code>, &lt;code>TERM&lt;/code>, the config, and a proxy if one is configured. And it has no controlling terminal and &lt;code>/dev/null&lt;/code> on stdin, for the reasons in &lt;code>95-dotfiles&lt;/code> above, so a hook cannot stop a build to ask a question — a program that tries gets EOF and fails, which is a build that ends with an error rather than one that waits all night.&lt;/p>
&lt;h2 id="verifying-the-image">Verifying The Image
&lt;/h2>&lt;p>An image that builds is not an image that boots, and an image that boots is not an image that is configured the way you asked. So there are two scripts.&lt;/p>
&lt;h3 id="does-it-boot">Does It Boot
&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;/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">./testboot.sh -m uefi alpine.img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./testboot.sh -m bios alpine.img
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This boots the image headless under QEMU with &lt;code>-snapshot&lt;/code>, so the image is never modified, captures the serial console to a file, and waits for a &lt;code>login:&lt;/code> prompt. Reaching the login prompt exercises the whole chain in one go: firmware → GRUB → kernel → initramfs → root mount → OpenRC → getty. On failure it prints the last 40 lines of the console, which is usually enough to see where it stopped. &lt;code>-w&lt;/code> drops the &lt;code>-snapshot&lt;/code> and lets the guest write to the image, for when you want to inspect what a first-boot service did to the disk.&lt;/p>
&lt;p>It finds OVMF and AAVMF across the various paths different distributions use, and uses KVM when &lt;code>/dev/kvm&lt;/code> is writable. With KVM, BIOS reaches the login prompt in about 16 seconds and UEFI in about 26 — OVMF initialization is the difference.&lt;/p>
&lt;h3 id="is-it-configured-correctly">Is It Configured Correctly
&lt;/h3>&lt;p>Add the &lt;code>99-selftest&lt;/code> hook and pass &lt;code>-d&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;/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">HOOKS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOOKS&lt;/span>&lt;span class="s2"> 99-selftest&amp;#34;&lt;/span> sudo ./mkalpine.sh -f alpine.img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./testboot.sh -d alpine.img
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The hook installs a service that runs last in the &lt;code>default&lt;/code> runlevel and checks the running system against the configuration it was built from. The expectations are baked in at build time, which is the part that makes it useful: a hook that silently did nothing shows up as a failure rather than as a plausible-looking dump.&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;/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">===== SELFTEST BEGIN =====
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-- identity
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> info machine-id=2767df9cefbfd2d2af2d0b8acdfb4aa5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> info hostkey=SHA256:U242JZNiEf+Qo3swIK+MoW4m2bdNb2eCMG5W6VJAwPM (ED25519)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok hostname is alpine
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok machine-id is 32 hex digits
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok timezone is UTC
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-- filesystems
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> info root options=rw,noatime,inode64,logbufs=8,logbsize=256k,noquota
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok root is xfs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok /boot is vfat
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok root mounted with logbsize=256k
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -- btrfs subvolume (not configured)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok fstab references root by UUID
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-- boot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> info kernel=6.18.48-0-virt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> info cmdline=BOOT_IMAGE=/vmlinuz-virt root=UUID=e95aee24-... ro
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> modules=sd-mod,usb-storage,xfs rootfstype=xfs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rootflags=noatime,logbsize=256k console=tty0 console=ttyS0,115200
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok cmdline is not quiet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok ttyS0 in securetty
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-- hooks
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok 20-ssh: PermitRootLogin is prohibit-password
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok 20-ssh: listening on 22
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok 40-zram: swap is ~100% of RAM
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok 60-sysctl: congestion control is bbr
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok 70-growroot: removed itself from the default runlevel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok 70-growroot: root fs fills the partition
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok 80-firstboot: host key is not the build host&amp;#39;s
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -- 91-ufw (disabled)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-- hygiene
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok apk cache is empty
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok no build scratch left behind
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok repositories pinned to v3.24
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ok repositories do not track latest-stable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-- sizing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Filesystem Size Used Available Use% Mounted on
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /dev/vda3 381.0M 161.2M 219.8M 42% /
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /dev/vda2 63.0M 36.4M 26.6M 58% /boot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">SELFTEST RESULT: 59 passed, 0 failed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">===== SELFTEST END =====
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>(abridged; the real report is one line per check)&lt;/p>
&lt;p>&lt;code>testboot.sh -d&lt;/code> exits non-zero if any check failed, so it works in a loop.&lt;/p>
&lt;p>The checks live in &lt;code>hooks/files/selftest.initd&lt;/code> as an ordinary shell script rather than a heredoc, and &lt;code>check&lt;/code> takes a command with its arguments instead of a string to &lt;code>eval&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">check &lt;span class="s2">&amp;#34;root is &lt;/span>&lt;span class="nv">$EXPECT_ROOT_FS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> fstype_is / &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$EXPECT_ROOT_FS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">check &lt;span class="s2">&amp;#34;machine-id is populated&amp;#34;&lt;/span> &lt;span class="nb">test&lt;/span> -s /etc/machine-id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">check &lt;span class="s2">&amp;#34;cmdline is not quiet&amp;#34;&lt;/span> not grep -qw quiet /proc/cmdline
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">check &lt;span class="s2">&amp;#34;60-sysctl: vm.swappiness is 180&amp;#34;&lt;/span> output_is &lt;span class="m">180&lt;/span> sysctl -n vm.swappiness
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Anything needing a pipeline gets a named predicate instead. The first version of this used &lt;code>eval&lt;/code> on quoted strings and was unreadable — &lt;code>check &amp;quot;root is btrfs&amp;quot; &amp;quot;awk '\$2 == \&amp;quot;/\&amp;quot; {print \$3}' /proc/mounts | grep -qx btrfs&amp;quot;&lt;/code> — which is a good sign that the abstraction was wrong.&lt;/p>
&lt;h3 id="everything-at-once">Everything At Once
&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">sudo ./testmatrix.sh -o /var/tmp/mx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This builds &lt;code>btrfs&lt;/code>/&lt;code>ext4&lt;/code>/&lt;code>xfs&lt;/code> × &lt;code>gpt&lt;/code>/&lt;code>mbr&lt;/code>, boot-tests all six under both BIOS and UEFI with the selftest enabled, then checks the things that only show up at runtime:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Growth.&lt;/strong> Copy an image, &lt;code>truncate -s 4G&lt;/code>, and boot it — with &lt;code>testboot.sh -w&lt;/code>, so the guest&amp;rsquo;s writes actually land in the file instead of in QEMU&amp;rsquo;s &lt;code>-snapshot&lt;/code> overlay. Then confirm the root partition grew (911,360 → 8,253,407 sectors) and that it still &lt;em>starts&lt;/em> on its original sector, because a &lt;code>growpart&lt;/code> that moved the start would silently discard the build-time alignment. Reading the table back from the un-booted copy would have made this test tautological, which is what it was until I looked closely.&lt;/li>
&lt;li>&lt;strong>Output formats.&lt;/strong> Build with all five, confirm each file exists, and boot the qcow2 directly rather than just checking that &lt;code>qemu-img&lt;/code> produced something.&lt;/li>
&lt;li>&lt;strong>Identity.&lt;/strong> Boot the same image twice and confirm the two boots produce &lt;em>different&lt;/em> SSH host key fingerprints and machine-ids.&lt;/li>
&lt;/ul>
&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">== build and boot matrix ==
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok build btrfs/gpt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok boot btrfs/gpt/uefi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok boot btrfs/gpt/bios
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">== growroot on a larger disk ==
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok grow 4G
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok grow root partition 1959936 -&amp;gt; 8253407 sectors, still at 135168
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">== output formats ==
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok format formats.img.zst (74M)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok boot qcow2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">== identity is per-boot, not per-image ==
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok hygiene two boots produced different SSH host keys
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ok hygiene two boots produced different machine-ids
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">== summary ==
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">29 passed, 0 failed
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Four real bugs in this post were found this way. The XFS &lt;code>logbsize&lt;/code> downgrade above was one, and the tautological growth check was another. The third was in the selftest itself: I had written&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">check &lt;span class="s2">&amp;#34;apk cache is empty&amp;#34;&lt;/span> not ls -A /var/cache/apk
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>which reads correctly and means the opposite, because &lt;code>ls -A&lt;/code> exits 0 on an empty directory. A test suite that catches bugs in its own checks is doing its job.&lt;/p>
&lt;p>The last one showed up when I dropped the default &lt;code>IMAGE_SIZE&lt;/code> from 1 GiB to 512 MiB. All three filesystems still built and booted, but ext4 and XFS started failing &lt;code>70-growroot: root fs fills the partition&lt;/code>, which had been written as &amp;ldquo;&lt;code>df&lt;/code> size is at least 90% of the partition&amp;rdquo;. Nothing was wrong with the images: &lt;code>df&lt;/code> excludes reserved blocks and fixed metadata, and on a 445 MiB root partition that fixed cost is 44 MiB for ext4 and 64 MiB for XFS — 90.0% and 85.6%. The same filesystems on a 957 MiB partition reported 96.5% and 93.3% and passed. A percentage was simply the wrong shape for the check, because the overhead it has to tolerate barely moves with the disk size while the threshold does. The failure it exists to catch is not marginal either — a root that never grew is a 445 MiB filesystem in a 4 GiB partition — so the tolerance is now the larger of 96 MiB and 10%.&lt;/p>
&lt;h2 id="uploading">Uploading
&lt;/h2>&lt;p>Most providers want a compressed raw image or a qcow2:&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">sudo &lt;span class="nv">OUTPUT_FORMATS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;raw.zst&amp;#34;&lt;/span> ./mkalpine.sh -f alpine.img
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If your provider has no image import at all — which is the common case on the cheap tiers — the restore-over-&lt;code>dd&lt;/code> trick from the &lt;a class="link" href="https://charlie0129.github.io/blog/p/alpine-minimal-btrfs-install/#restore-from-the-providers-original-os" >old post&lt;/a> still applies: boot their stock OS, &lt;code>dd&lt;/code> the image onto the disk from a rescue environment or over SSH, and reboot. &lt;code>70-growroot&lt;/code> then handles the fact that their disk is larger than the image.&lt;/p>
&lt;p>That flow needs a console or a rescue system at one point or another. When the provider offers neither — no VNC, no serial, no rescue mode, just the stock OS and SSH, which is what the smallest NAT&amp;rsquo;d tiers look like — use the &lt;a class="link" href="https://charlie0129.github.io/blog/p/alpine-minimal-btrfs-install/#restore-without-a-console-or-rescue-mode" >no-console variant&lt;/a> in the old post instead: &lt;a class="link" href="https://github.com/bin456789/reinstall" target="_blank" rel="noopener"
>&lt;code>reinstall&lt;/code>&lt;/a> &lt;code>dd&lt;/code> mode arranges a RAM-resident Alpine by rewriting the stock bootloader, then writes the image over the whole disk and reboots, all over SSH. It accepts the &lt;code>raw.gz&lt;/code> / &lt;code>raw.zst&lt;/code> / &lt;code>raw.xz&lt;/code> artifacts from &lt;code>OUTPUT_FORMATS&lt;/code> as-is, and it does not modify a Linux image — which, on a headless box, promotes &lt;code>10-network&lt;/code> and &lt;code>20-ssh&lt;/code> from conveniences to the difference between a machine that comes back and a reinstall ticket.&lt;/p>
&lt;h2 id="what-i-kept-from-the-old-post">What I Kept From The Old Post
&lt;/h2>&lt;p>Everything about &lt;em>why&lt;/em>. The old post explains the 64 MiB &lt;code>/boot&lt;/code>, the compressed Btrfs root, the absence of disk swap, and the individual tweaks in far more detail than a config file comment can, and it is still the reference for doing this by hand on a machine you have already booted. This post is the automation of it.&lt;/p></description></item><item><title>Building Audio Insight: An Audio Analyzer That Actually Renders Smoothly</title><link>https://charlie0129.github.io/blog/p/building-audio-insight/</link><pubDate>Sun, 16 Aug 2026 16:42:45 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/building-audio-insight/</guid><description>&lt;p>I like audio analyzers. It answers questions that my ears alone cannot answer quickly. Where is that resonance? Is the low end actually mono? How loud is this over the whole track? Is a limiter catching an occasional peak, or working all the time?&lt;/p>
&lt;p>What I do not like is an analyzer whose interface feels slower than the display it runs on.&lt;/p>
&lt;p>On my M1 Max, Excite Audio VISION 4X appeared to top out at roughly 30 FPS, with inconsistent frame timing, while consuming about one CPU core. iZotope Insight 2 looked smoother, but in my experience it was comparatively resource-heavy and expensive. These were observations from my own setup, not controlled benchmarks that apply to every machine, host, and plugin version. Still, they were enough to make me wonder: how difficult would it be to build the analyzer I wanted?&lt;/p>
&lt;p>That became &lt;a class="link" href="https://github.com/charlie0129/audio-insight" target="_blank" rel="noopener"
>Audio Insight&lt;/a>, an open-source AUv2 and VST3 analyzer for macOS. Its first goal is deliberately narrow: show useful measurements, leave the audio unchanged, keep real-time callback work bounded, and make the interface feel native on a high-refresh-rate display.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard.png"
width="2400"
height="1600"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_65720109d0b02a45.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_bef71f94e6a17226.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_69cd2a1a5c959a9.webp 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_90165cad42ea4598.jpg 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_e5c6a27b39095609.webp 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_a4aec93b81a0f8da.jpg 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_6b3fb61cc0a9dbfe.webp 2048w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_af4a7725d3b77d03.jpg 2048w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_879369758ebcb0fa.webp 2400w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-dashboard_hu_b23cace1a05ee5e4.jpg 2400w"
loading="lazy"
alt="Audio Insight dashboard with all five analyzers"
class="gallery-image"
data-flex-grow="150"
data-flex-basis="360px"
>
&lt;/p>
&lt;p>The project is still early, but it already has a Spectrum, Spectrogram, Peak/RMS meter, stereo vectorscope and correlation meter, and BS.1770 loudness measurements. Four grid-snapped splitters resize the dashboard tiles, the analysis parameters are adjustable, and a built-in metrics panel makes the renderer&amp;rsquo;s behavior visible instead of leaving performance to intuition.&lt;/p>
&lt;p>This post is about how it works, but mostly about the unexpectedly interesting work required to make a meter move smoothly.&lt;/p>
&lt;h2 id="what-an-audio-plugin-actually-does">What an audio plugin actually does
&lt;/h2>&lt;p>People who use plugins often picture them as little applications inside a DAW. That is a useful mental model for the interface, but not for the audio path.&lt;/p>
&lt;p>An AU or VST3 plugin is code loaded by a host (or, in some hosts, a separate hosting service). The host repeatedly gives the plugin a small block of samples by calling its processing function. At 48 kHz with 512-sample blocks, a new block arrives about every 10.7 milliseconds. The plugin has to finish before the hardware needs the result. Missing that deadline can produce a click or dropout.&lt;/p>
&lt;p>Audio Insight is a transparent effect: it observes supported mono or stereo audio and leaves the samples unchanged. Even so, its callback has to follow the same real-time rules as a compressor or synthesizer. It cannot allocate memory, take a lock, wait for another thread, write a log, open a file, call the UI, or ask the GPU to draw something. Any of those operations can take an unpredictable amount of time.&lt;/p>
&lt;p>The resulting design looks 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;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-text" data-lang="text">&lt;span class="line">&lt;span class="cl">host audio callback
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bounded, non-blocking capture
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">per-instance analysis coordinator
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">shared two-worker analysis pool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">immutable measurement snapshots
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">display-linked Metal renderer
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The callback only captures bounded chunks into preallocated storage and updates the few measurements that must inspect every sample. A per-instance coordinator coalesces work, and all instances loaded in the same plugin module share two analysis workers. There is at most one running or queued job per instance, so opening many plugin windows does not create a thread for every visualization.&lt;/p>
&lt;p>The workers publish immutable snapshots. The UI reads the newest complete snapshot whenever it draws; it never waits for analysis to finish. This separation matters. Analysis targets 60 slices per second, but the latest-wins scheduler can skip stale work rather than build a backlog. Meanwhile, a ProMotion display can render at around 120 Hz. The renderer can advance display motion between discrete analysis updates without running twice as many FFTs.&lt;/p>
&lt;p>All five visualizations share one Metal canvas, drawable, command buffer, and render pass. JUCE supplies the plugin shell and cross-format plumbing, while the visual layer is native Metal. Coordinates and layout use logical points, and drawable and text resources follow the current backing scale. The implementation is therefore designed to support both regular-density and Retina displays, including live backing-scale changes.&lt;/p>
&lt;p>Both paths are highly optimized. The shared transform uses &lt;code>juce::dsp::FFT&lt;/code>, which selects Apple&amp;rsquo;s Accelerate/vDSP implementation on macOS, and Spectrum and Spectrogram reuse each calibrated result. On the GPU side, the Spectrogram stores calibrated dB in a circular 16-bit-float (&lt;code>R16Float&lt;/code>) texture: scrolling remaps texture coordinates, while shader controls recolor retained history without another FFT or a whole-texture copy. High display cadence therefore does not multiply the default 60 Hz FFT workload.&lt;/p>
&lt;p>When the editor is closed, there is nothing to display, so capture, analysis, history, display-link callbacks, and Metal submissions stop. Audio still passes through normally. Reopening the editor begins fresh rather than silently spending host resources on invisible history.&lt;/p>
&lt;h2 id="turning-samples-into-pictures">Turning samples into pictures
&lt;/h2>&lt;p>The analyzers share infrastructure, but each one answers a different question. Here is the calculation path in a little more detail.&lt;/p>
&lt;h3 id="spectrum-what-frequencies-exist-now">Spectrum: what frequencies exist now?
&lt;/h3>&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum.png"
width="1964"
height="686"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_2387749c0d8ba064.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_664cee83eb2c0ad.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_fbdd5b971d8cb430.webp 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_deaf9f5d2535fbbb.jpg 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_c52894cca3685b9.webp 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_b740d68172460d99.jpg 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_bdaa1a07fea7d5df.webp 1964w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum_hu_1cda9b55c8a5a78c.jpg 1964w"
loading="lazy"
alt="Audio Insight Spectrum"
class="gallery-image"
data-flex-grow="286"
data-flex-basis="687px"
>
&lt;/p>
&lt;p>The Spectrum takes a short window of recent audio and uses a fast Fourier transform (FFT) to divide it into frequency bins. By default the transform size is \(N=8192\) samples, or about 171 milliseconds at a sample rate \(F_s=48\) kHz. This does &lt;strong>not&lt;/strong> delay the audio by 171 ms; the plugin passes audio through immediately. It means the displayed estimate describes roughly that much recent history.&lt;/p>
&lt;p>The FFT bin centers are separated by \(\Delta f=F_s/N\). With the defaults, that is approximately 5.86 Hz. This number is useful, but it is not the same as saying two tones 5.86 Hz apart can always be resolved: the selected window also determines how widely a tone spreads into nearby bins.&lt;/p>
&lt;p>Before the FFT, samples are multiplied by a periodic five-term flat-top window \(w[n]\). Cutting an arbitrary piece from a continuous waveform creates artificial edges, which spread energy across the spectrum. A window tapers the data to control that leakage. A flat-top window trades some ability to separate nearby tones for better amplitude accuracy, which is a useful default for a measurement tool.&lt;/p>
&lt;p>For channel \(c\), the transform is:&lt;/p>
\[
\begin{aligned}
X_c[k]&amp;=\sum_{n=0}^{N-1}x_c[n]\,w[n]e^{-j2\pi kn/N},\\
f_k&amp;=\frac{kF_s}{N}.
\end{aligned}
\]&lt;p>Audio Insight corrects the window&amp;rsquo;s coherent gain—the amplitude scaling introduced by multiplying by \(w[n]\)—with \(W=\sum_n w[n]\). Real-valued audio has mirrored positive- and negative-frequency FFT bins, but the graph needs only the nonnegative half. In this one-sided view, the DC bin at 0 Hz and the Nyquist bin at \(F_s/2\) use \(1/W\); every bin between them represents both mirrored sides and uses \(2/W\). The calibrated stereo power and level are therefore:&lt;/p>
\[
\begin{aligned}
a_k&amp;=
\begin{cases}
1/W, &amp; k=0\ \text{or}\ k=N/2,\\
2/W, &amp; \text{otherwise},
\end{cases}\\[3pt]
P[k]&amp;=\max_c\left(a_k|X_c[k]|\right)^2,\\
D[k]&amp;=10\log_{10}P[k].
\end{aligned}
\]&lt;p>For mono, the maximum contains only one channel. For stereo, taking the larger channel magnitude avoids first mixing the waveforms to mono, where out-of-phase content could cancel. The calibration makes a bin-centered full-scale sine read 0 dB internally; powers at or below \(10^{-18}\) are displayed at the \(-180\) dB analysis floor.&lt;/p>
&lt;p>Attack and Release then smooth each bin in &lt;strong>linear power&lt;/strong>, not in dB. Given the elapsed time \(\Delta t\) and the selected time constant \(\tau_d\):&lt;/p>
\[
\begin{aligned}
\alpha_d&amp;=
\begin{cases}
0, &amp; d\text{ is Off},\\
e^{-\Delta t/\tau_d}, &amp; d\text{ is enabled},
\end{cases}\\[3pt]
\bar P_t[k]&amp;=\alpha_d\bar P_{t-1}[k]+(1-\alpha_d)P_t[k].
\end{aligned}
\]&lt;p>The direction \(d\) is Attack when \(P_t[k]\geq\bar P_{t-1}[k]\), otherwise Release. An Off direction follows the current FFT immediately. The default Attack is Off, allowing a short burst to appear at once, while the default 250 ms Release lets the trace fall more slowly. Peak hold, when enabled, operates on unsmoothed power instead of \(\bar P\).&lt;/p>
&lt;p>Transforms target a slice rate \(R_s\) using a hop of \(H=\max(1,\operatorname{round}(F_s/R_s))\) new samples. At 48 kHz and 60 slices per second, \(H=800\), so adjacent 8,192-sample windows overlap by about 90.2%. The first result still waits for one complete window, and the latest-wins scheduler may skip stale transforms under load instead of building a backlog.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings.png"
width="2400"
height="1600"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_9787958d4bb832d.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_e4c42b32afd89577.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_b65c00a7ce269ec9.webp 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_4e962807fc7188dc.jpg 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_e2311e7526ab5e66.webp 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_c78d3014e83d6dbb.jpg 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_6528c9c2772ad63b.webp 2048w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_9bfee6b347dac769.jpg 2048w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_796a3c92f524a6a9.webp 2400w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrum-settings_hu_e5c6f6a008c1f78b.jpg 2400w"
loading="lazy"
alt="Spectrum Attack and Release controls"
class="gallery-image"
data-flex-grow="150"
data-flex-basis="360px"
>
&lt;/p>
&lt;p>Spectrum and Spectrogram use the same continuously adjustable frequency scale. For a frequency \(f\) between \(f_0\) and \(f_1\), the scale control \(s\) blends normalized linear and logarithmic coordinates:&lt;/p>
\[
\begin{aligned}
u_{\mathrm{lin}}(f)&amp;=\frac{f-f_0}{f_1-f_0},\\
u_{\log}(f)&amp;=\frac{\ln(f/f_0)}{\ln(f_1/f_0)},\\
u(f,s)&amp;=(1-s)u_{\mathrm{lin}}(f)+s\,u_{\log}(f).
\end{aligned}
\]&lt;p>Here \(f_0=20\) Hz and \(f_1=\min(20\text{ kHz},F_s/2)\). The default is \(s=0.8\). At \(s=0\), equal distances represent equal numbers of hertz. At \(s=1\), equal ratios such as 100→200 Hz and 1→2 kHz occupy equal distances. Values in between preserve more low-frequency detail without compressing the entire treble into a tiny area. Spectrum uses \(x=u\), while Spectrogram uses \(y=1-u\) so high frequencies appear at the top. Axis labels are chosen dynamically: important anchors win first, then extra candidates fill only the space that remains.&lt;/p>
&lt;h3 id="spectrogram-how-did-the-spectrum-change">Spectrogram: how did the spectrum change?
&lt;/h3>&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram.png"
width="1570"
height="758"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_7f3da03990dee5bf.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_e61b0ccc823d3d7d.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_3e3ec7e159f2c15d.webp 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_d8e057932d6094d3.jpg 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_ff773f267ecd87d6.webp 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_2135715b92aae3fd.jpg 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_e23e3c8120487b46.webp 1570w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-spectrogram_hu_1b464000e76048f6.jpg 1570w"
loading="lazy"
alt="Audio Insight Spectrogram"
class="gallery-image"
data-flex-grow="207"
data-flex-basis="497px"
>
&lt;/p>
&lt;p>A Spectrum is one slice through time. A Spectrogram keeps those slices and scrolls them sideways, using color for level. Transients become vertical marks, steady tones become horizontal lines, and harmonics become stacks of related lines.&lt;/p>
&lt;p>Each Spectrogram column starts from the same raw power \(P[k]\) as Spectrum, before Spectrum&amp;rsquo;s Attack/Release averaging. Let \(\mathcal K\) contain only usable bin centers from 20 Hz through \(f_1\), and let \(R_f=\min(1024,|\mathcal K|)\) be the texture&amp;rsquo;s frequency-row count. For a usable bin at \(f_k=kF_s/N\), define \(q_k=u(f_k,s)\). Its row is:&lt;/p>
\[
r(k)=\min\left(R_f-1,\left\lfloor R_f q_k\right\rfloor\right).
\]&lt;p>For a row containing one or more bin centers, \(P_r\) is the greatest \(P[k]\) assigned to that row. Taking the maximum, rather than the average, helps a narrow tonal trace survive when several FFT bins land in one display row. If a row contains no bin center—common at low frequencies with a small FFT—the mapper inverse-maps the row center and linearly interpolates the two surrounding bins in power. It is honest interpolation between available samples, not a claim of extra frequency resolution.&lt;/p>
&lt;p>Power at or below \(10^{-18}\) becomes \(-180\) dB; otherwise the mapper stores \(D_r=10\log_{10}P_r\). These values go into a circular Metal texture with one 16-bit floating-point level per cell (R16Float). The texture stores calibrated dB rather than finished colors. In the shader, let \(F\) be the selected floor, \(C\) the ceiling, and \(\eta\) the Color response:&lt;/p>
\[
\begin{aligned}
v&amp;=\operatorname{clamp}\left(
\frac{D_r-F}{C-F},0,1\right),\\
\gamma&amp;=2^\eta,\\
c_{\mathrm{palette}}&amp;=v^\gamma.
\end{aligned}
\]&lt;p>The value \(c_{\mathrm{palette}}\) selects a point in the chosen palette. Response 0 is linear in dB; negative values reveal quieter detail, while positive values suppress low energy and emphasize stronger traces. Because this work happens in the shader, changing palette, range, or response recolors existing history without rerunning the FFT.&lt;/p>
&lt;p>For a history duration \(T\) and requested slice rate \(R_s\), the texture uses \(\min(8192,\lceil TR_s\rceil)\) columns. The default ten seconds at 60 slices per second therefore needs 600 columns. A write index wraps around the texture, and the renderer changes texture coordinates instead of copying the whole image to scroll it. Missing timestamp intervals become black columns rather than stretching old information across time.&lt;/p>
&lt;h3 id="peak-and-rms-how-strong-is-the-signal">Peak and RMS: how strong is the signal?
&lt;/h3>&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-peak-rms.png"
width="534"
height="686"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-peak-rms_hu_52e90250c2611238.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-peak-rms_hu_5a4dbf3bd0e0c780.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-peak-rms_hu_3a0f949a87b5d6b1.webp 534w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-peak-rms_hu_de0186bebe1eb659.jpg 534w"
loading="lazy"
alt="Audio Insight Peak/RMS meter"
class="gallery-image"
data-flex-grow="77"
data-flex-basis="186px"
>
&lt;/p>
&lt;p>Peak and RMS intentionally describe different things.&lt;/p>
&lt;p>Sample peak examines every sample. Its live value has instantaneous attack and a 20 dB/s release:&lt;/p>
\[
\begin{aligned}
\lambda_p&amp;=10^{-20/(20F_s)},\\
p[n]&amp;=\max\left(|x[n]|,\lambda_p p[n-1]\right).
\end{aligned}
\]&lt;p>In other words, a new larger sample wins immediately; otherwise the old indication decays by the amount corresponding to one sample period. A separate hold marker keeps a new maximum for two seconds, then falls at the same 20 dB/s rate. The OVER indicator latches when \(|x[n]|\geq1\), although the label deliberately does not claim that floating-point audio at 0 dBFS proves waveform clipping.&lt;/p>
&lt;p>RMS estimates sustained signal power. With the 300 ms time constant \(\tau=0.300\) s, Audio Insight updates an exponential mean square for every sample:&lt;/p>
\[
\begin{aligned}
\alpha&amp;=e^{-1/(F_s\tau)},\\
q[n]&amp;=\alpha q[n-1]+(1-\alpha)x[n]^2,\\
\operatorname{RMS}[n]&amp;=\sqrt{q[n]},\\
D_{\mathrm{RMS}}[n]&amp;=20\log_{10}\operatorname{RMS}[n].
\end{aligned}
\]&lt;p>This is an exponential response, not a rectangular box containing exactly the latest 300 ms. It also has no AES17 \(+3.01\) dB calibration offset, so a full-scale sine reads approximately \(-3.01\) dBFS RMS. Peak reveals brief extremes; RMS behaves more like a view of sustained energy. The peak remains a &lt;strong>sample peak&lt;/strong>, not an oversampled true-peak/dBTP measurement, so it does not predict a possibly larger value between stored samples.&lt;/p>
&lt;p>These ballistics run on the bounded real-time capture path and inspect every sample. Their meaning therefore does not change if an analysis worker is briefly late.&lt;/p>
&lt;h3 id="stereo-how-are-left-and-right-related">Stereo: how are left and right related?
&lt;/h3>&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-stereo-correlation.png"
width="576"
height="650"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-stereo-correlation_hu_263c226881c3769f.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-stereo-correlation_hu_75cef76ae6f2d730.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-stereo-correlation_hu_23357ccd719a8e4e.webp 576w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-stereo-correlation_hu_81e9cc669ba79053.jpg 576w"
loading="lazy"
alt="Audio Insight Vectorscope and Correlation meter"
class="gallery-image"
data-flex-grow="88"
data-flex-basis="212px"
>
&lt;/p>
&lt;p>The vectorscope turns each stereo sample pair into a point:&lt;/p>
\[
x_{\mathrm{scope}}=\frac{R-L}{2},
\qquad
y_{\mathrm{scope}}=\frac{L+R}{2}.
\]&lt;p>Audio shared equally by both channels has \(x=0\) and lies on the vertical center axis. Opposite-phase audio has \(y=0\) and spreads horizontally. The coordinates remain tied to full scale rather than being normalized independently on every frame, so a quiet signal is not made to look artificially loud.&lt;/p>
&lt;p>The field keeps the latest 250 ms but bounds its GPU data. For \(W_f=\lceil0.25F_s\rceil\) captured frames, the worker selects one pair every:&lt;/p>
\[
d=\left\lceil\frac{W_f}{4096}\right\rceil
\]&lt;p>frames. At 48 kHz, \(W_f=12000\), \(d=3\), and the cloud contains about 4,000 uniformly spaced points. Their opacity fades with age. This decimation changes only the picture; it does not change the correlation measurement.&lt;/p>
&lt;p>The adjacent correlation value uses every sample, with 300 ms exponentially weighted averages:&lt;/p>
\[
\begin{aligned}
\alpha&amp;=e^{-1/(F_s\cdot0.300)},\\
E_n[z]&amp;=\alpha E_{n-1}[z]+(1-\alpha)z[n].
\end{aligned}
\]\[
\rho[n]=\frac{E_n[LR]}{\sqrt{E_n[L^2]E_n[R^2]}}.
\]&lt;p>The three running values \(E[L^2]\), \(E[R^2]\), and \(E[LR]\) advance in source-sample order on the real-time side, and the implementation clamps the final ratio to \([-1,1]\) against numerical error. A value near \(+1\) means the channels are strongly alike, \(0\) means little linear relationship, and a negative value warns that mono playback may cancel important content. If either averaged channel power is below \(10^{-9}\), equivalent to \(-90\) dBFS RMS, correlation is reported as unavailable rather than dividing by a nearly zero value. A genuinely mono input is labeled MONO and likewise does not receive a synthetic \(+1\) correlation.&lt;/p>
&lt;h3 id="loudness-how-loud-does-it-feel-over-time">Loudness: how loud does it feel over time?
&lt;/h3>&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-loudness.png"
width="384"
height="650"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-loudness_hu_7320b9503e1a0dce.webp 384w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-loudness_hu_ca5aed9118a6d0df.jpg 384w"
loading="lazy"
alt="Audio Insight Loudness meters"
class="gallery-image"
data-flex-grow="59"
data-flex-basis="141px"
>
&lt;/p>
&lt;p>Raw peak level is not perceived loudness. Audio Insight implements BS.1770-5 K-weighting with the Momentary, Short-term, and Integrated semantics commonly used with EBU R128. K-weighting is a pair of filters: a high-frequency shelf models the head&amp;rsquo;s acoustic effect, and a high-pass stage reduces the contribution of very low frequencies. The code derives their coefficients for the current sample rate.&lt;/p>
&lt;p>If \(y_c[n]\) is the K-weighted output of channel \(c\), Audio Insight forms the per-sample energy and a window mean:&lt;/p>
\[
\begin{aligned}
e[n]&amp;=\sum_c y_c[n]^2,\\
z_W&amp;=\frac{1}{N_W}\sum_{n\in W}e[n].
\end{aligned}
\]&lt;p>For the supported mono and stereo layouts, every actual channel has unit weight. Mono therefore contributes once; it is never duplicated into synthetic left and right channels. Surround layouts and their channel weights are outside the current scope. Window energy becomes LUFS (Loudness Units relative to Full Scale) using the BS.1770 offset:&lt;/p>
\[
L_W=-0.691+10\log_{10}z_W.
\]&lt;ul>
&lt;li>Momentary loudness covers 400 ms.&lt;/li>
&lt;li>Short-term loudness covers 3 seconds.&lt;/li>
&lt;li>Integrated loudness uses 400 ms blocks completed every 100 ms—75% overlap—from the latest Reset within the current uninterrupted, visible analysis interval.&lt;/li>
&lt;/ul>
&lt;p>Momentary and Short-term are simple ungated window measurements. Integrated loudness applies gates: thresholds that exclude blocks from the long-term average. For each 400 ms block \(i\), let its mean-square energy be \(z_i\) and its loudness be \(L_i=-0.691+10\log_{10}z_i\). The absolute-passing set is:&lt;/p>
\[
\begin{aligned}
\mathcal A&amp;=\{i\mid L_i>-70\ \mathrm{LUFS}\},\\
\mu_{\mathcal A}&amp;=\frac{1}{|\mathcal A|}\sum_{i\in\mathcal A}z_i.
\end{aligned}
\]&lt;p>At each Integrated update, one non-iterative relative threshold is calculated 10 LU below the preliminary absolute-gated mean of the history accumulated so far:&lt;/p>
\[
\begin{aligned}
\Gamma_{\mathrm{rel}}&amp;=-0.691+10\log_{10}\mu_{\mathcal A}-10,\\
\mathcal R&amp;=\{i\in\mathcal A\mid L_i>\Gamma_{\mathrm{rel}}\}.
\end{aligned}
\]&lt;p>Finally:&lt;/p>
\[
\begin{aligned}
\bar z_{\mathcal R}&amp;=\frac{1}{|\mathcal R|}\sum_{i\in\mathcal R}z_i,\\
L_I&amp;=-0.691+10\log_{10}\bar z_{\mathcal R}.
\end{aligned}
\]&lt;p>Both comparisons are strict \(>\), and the relative gate is not iterated repeatedly. This two-stage gate prevents silence and very quiet passages from dragging the program average down indefinitely. The tile&amp;rsquo;s Reset command restarts Integrated loudness while ready Momentary/Short-term values and K-weighting continuity remain intact. Editor reactivation, an audio discontinuity, or a format change resets the complete loudness analyzer.&lt;/p>
&lt;p>The empty cases are explicit too. If \(\mathcal A\) contains no blocks, the preliminary mean and relative gate remain unavailable. If \(\mathcal R\) is empty, Integrated loudness remains \(-\infty\). The implementation never divides by an empty set.&lt;/p>
&lt;p>The implementation does not claim complete EBU Mode compliance: it does not yet include LRA or true peak, for example. The label describes its M/S/I measurement semantics, not a certification.&lt;/p>
&lt;p>There is an interesting performance problem hiding in Integrated loudness. Within one uninterrupted visible measurement, the exact answer can cover 24 hours: up to 864,000 blocks. Rescanning every qualifying block every 100 ms would make the cost grow throughout the measurement.&lt;/p>
&lt;p>The implementation uses a preallocated sorted index called a B+ tree. It contains finite block energies above the absolute gate and keeps aggregate counts and sums in its branches; all completed blocks still count toward the 24-hour limit. A new relative-gate boundary can be answered by finding one boundary leaf and combining a bounded number of branch totals. Capacity for the worst case occupies about 7.25 MiB on arm64, and the structure never allocates while processing.&lt;/p>
&lt;h2 id="smooth-is-a-timing-property-not-an-fps-number">Smooth is a timing property, not an FPS number
&lt;/h2>&lt;p>The renderer uses &lt;code>CAMetalDisplayLink&lt;/code>, which supplies a drawable in step with a display. While visible, Audio Insight requests the active display&amp;rsquo;s exact reported maximum refresh rate, with a 60 Hz fallback. That request is best effort—Core Animation and the compositor still control actual presentation—so measured presentation timestamps are the truth.&lt;/p>
&lt;p>This distinction became important repeatedly. A counter can say 120 callbacks per second while the screen still changes only 60 times. An average can say 120 FPS while an occasional doubled interval makes scrolling visibly hitch. Smoothness is about the complete chain from callback to presentation and about the distribution of frame intervals, not just one large number.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/images/audio-insight-soundsource.png"
loading="lazy"
alt="Audio Insight running inside SoundSource"
>
&lt;/p>
&lt;h3 id="the-plugin-that-crashed-its-host">The plugin that crashed its host
&lt;/h3>&lt;p>The first AU build appeared for a moment in SoundSource and then disappeared. The host reported only that its Audio Unit hosting service had crashed.&lt;/p>
&lt;p>The detailed log led to an assertion in timed drawable presentation. A normal Metal application may call an API such as timed &lt;code>present&lt;/code>, but a drawable delivered by &lt;code>CAMetalDisplayLink&lt;/code> has different presentation ownership. Combining the two caused the hosting process to assert. The correct sequence is to commit the command buffer and call plain &lt;code>present()&lt;/code> on that drawable, while using the display link&amp;rsquo;s target timestamp only for telemetry and scheduling.&lt;/p>
&lt;p>This is one reason plugin development needs testing in real hosts. SoundSource exposed an API misuse that a successful build or unit test had not.&lt;/p>
&lt;h3 id="why-120-display-callbacks-produced-60-frames">Why 120 display callbacks produced 60 frames
&lt;/h3>&lt;p>After the crash was fixed, the display link was firing close to 120 times per second, but only about 60 frames were submitted. The built-in metrics captured the pattern:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Counter&lt;/th>
&lt;th style="text-align: right">Before the fix&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Display-link callbacks&lt;/td>
&lt;td style="text-align: right">6,453&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Metal submissions&lt;/td>
&lt;td style="text-align: right">3,230&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPU-backpressure drops&lt;/td>
&lt;td style="text-align: right">3,223&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sampled display-link callback rate&lt;/td>
&lt;td style="text-align: right">~111/s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sampled Metal submission rate&lt;/td>
&lt;td style="text-align: right">~59.6/s&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The cumulative counters cover the full telemetry epoch; the two rates are a sample from its final roughly 0.25 seconds. Almost exactly every other display-link callback was being rejected by the in-flight buffer pool.&lt;/p>
&lt;p>The surprising part was that the GPU was not necessarily too slow. Reusable vertex buffers were retained until the drawable was actually presented. The compositor may hold a drawable for several refresh periods even after GPU execution has completed, so all reusable buffers became occupied and the next callback had nowhere to write.&lt;/p>
&lt;p>The fix was to separate two lifetimes. GPU command completion now releases the reusable buffers immediately. A small, independent object survives only to correlate the later presentation timestamp. The renderer no longer holds large working resources hostage to compositor timing.&lt;/p>
&lt;p>In a later point-in-time M1 Max capture from another development build, with the Metrics panel visible, the drawable was 2,400×1,496 pixels at 2× backing scale. The run recorded 1,188 display-link callbacks, 1,188 submissions, and zero GPU-backpressure drops. Across the most recent 240 presented intervals, the average was 8.438 ms, or 118.52 Hz; 237 intervals were the normal 8.333 ms and three doubled to 16.667 ms. Telemetry also counted 11 skipped presentations over the run. That capture used the then-selected 16,384-point FFT; today&amp;rsquo;s default is 8,192.&lt;/p>
&lt;p>The capture demonstrates that the buffer-lifetime bottleneck and its GPU-backpressure drops were gone. It is evidence of approximately display-rate presentation in that run, not a perfect-pacing claim or a controlled comparison with another plugin.&lt;/p>
&lt;h3 id="making-60-hz-data-scroll-on-a-120-hz-display">Making 60 Hz data scroll on a 120 Hz display
&lt;/h3>&lt;p>The Spectrogram exposed a second kind of stutter. New analysis columns arrive at 60 Hz. If the image moves forward only when a complete column arrives, it necessarily steps every other frame on a 120 Hz display.&lt;/p>
&lt;p>The solution was not to double the FFT workload. The renderer advances a fractional scroll head from the target presentation clock while keeping the actual dB cells discrete. A one-slice cushion absorbs ordinary analysis scheduling jitter. If a texture upload is briefly busy, the renderer postpones that upload while continuing to draw the rest of the dashboard.&lt;/p>
&lt;p>The result is much smoother motion from the same 60-slices-per-second target. This also explains why raising thread priority would have been the wrong first response: the main issue was the relationship between two clocks, not a shortage of real-time privileges.&lt;/p>
&lt;h3 id="the-random-resets-that-were-not-random">The random resets that were not random
&lt;/h3>&lt;p>During longer sessions, all graphs would occasionally reset. The recovery was intentional—when audio history has a real gap, temporal analyzers must not pretend the samples on either side were adjacent—but the handoff overflowed far too easily even while host audio was continuous. Sequence tracking then correctly detected the resulting loss.&lt;/p>
&lt;p>The original capture queue had 16 logical slots and consumed one for each host callback. Its time capacity therefore depended on the host&amp;rsquo;s block size. A metrics capture reached all 16 ready slots, discarded 20 queued chunks to make room for newer audio, and recorded three consumer discontinuities followed by three Loudness resets.&lt;/p>
&lt;p>The redesigned queue packs audio across callback boundaries into 128 slots of 256 frames, retaining 32,768 frames regardless of host callback size. That is about 683 ms at 48 kHz, 341 ms at 96 kHz, or 171 ms at 192 kHz. The capacity and overflow behavior are covered by implementation tests; longer post-redesign host runs remain part of validation. A sufficiently long stall can still overflow it. When that happens, latest data wins and temporal analyzers reset, because joining unrelated pieces of audio would produce convincing but false measurements.&lt;/p>
&lt;h2 id="building-observability-into-the-plugin">Building observability into the plugin
&lt;/h2>&lt;p>Apple&amp;rsquo;s Metal HUD is useful for applications that enable it before creating their first Metal device. A plugin usually arrives after its host has already done that, so it cannot reliably switch the HUD on from a settings button. I replaced that idea with a built-in performance panel available in Release builds.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics.png"
width="2400"
height="1600"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_3e35564a57531db1.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_17a99fe9b1840b47.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_a50d2e90557737cb.webp 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_56e8ba0c2aa2fcf5.jpg 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_c16f0ce7449dffc6.webp 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_7ee062fc54661c12.jpg 1536w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_91a2ab2ced51d103.webp 2048w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_5058b4d74de2b309.jpg 2048w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_8f5e390e440110af.webp 2400w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics_hu_f2af51aec60caaa4.jpg 2400w"
loading="lazy"
alt="Frame pacing and latency composition in the Metrics panel"
class="gallery-image"
data-flex-grow="150"
data-flex-basis="360px"
>
&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics-details.png"
width="1030"
height="1492"
srcset="https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics-details_hu_8fc88fe5b45184b.webp 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics-details_hu_6855df2f78d2e266.jpg 480w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics-details_hu_7bb34ff9e83af0ff.webp 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics-details_hu_ece4367265690785.jpg 1024w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics-details_hu_ce43a78db3f44ba9.webp 1030w, https://charlie0129.github.io/blog/p/building-audio-insight/images/audio-insight-metrics-details_hu_392c9d2f73db95ad.jpg 1030w"
loading="lazy"
alt="Metric details"
class="gallery-image"
data-flex-grow="69"
data-flex-basis="165px"
>
&lt;/p>
&lt;p>It reports exact frame pacing over the latest 240 presentation intervals, derived from 241 timestamps; CPU, submit, GPU, and compositor latency composition; display-link scheduling; audio-callback histograms; queue occupancy and discontinuities; analyzer freshness; and raw copyable metrics for offline inspection. The stacked latency bar covers the pipeline from a display-link callback to presentation. It is not a breakdown of an 8.33 ms frame budget: several frames can overlap in flight, so its total can exceed one refresh interval without reducing presentation cadence.&lt;/p>
&lt;p>Graphs move at vblank, headline numbers refresh at no more than 10 Hz, and the full text table refreshes at 4 Hz. That keeps the visual feedback immediate without rebuilding lots of text 120 times per second. Instrumentation turned several vague reports—“it looks a bit laggy,” “it seems to reset”—into specific, actionable failures.&lt;/p>
&lt;h2 id="an-agent-assisted-human-tested-development-loop">An agent-assisted, human-tested development loop
&lt;/h2>&lt;p>I used coding agents to implement much of Audio Insight. That did not remove the need for a tight feedback loop; it made the loop more important.&lt;/p>
&lt;p>The agents could design the threading model, inspect crash logs, add instrumentation, and reason from raw captures. They could build the plugin, but they could not reliably judge how motion felt inside my particular SoundSource setup. At runnable milestones I installed the AU, watched it on the M1 Max, adjusted settings, and returned observations, screenshots, logs, or copied metrics. Those reports led directly to the presentation-lifetime fix, the fractional Spectrogram scroll, dynamic axis labeling, and the queue redesign.&lt;/p>
&lt;p>For visual and real-time software, “the code is correct” and “the product feels right” are different claims. An instrumented implementation plus a person looking at the actual display proved far more useful than guessing at either one in isolation.&lt;/p>
&lt;h2 id="what-is-open-source-today">What is open source today
&lt;/h2>&lt;p>The current code identifies itself as Audio Insight 0.1.0. It targets macOS 15 on arm64 and builds AUv2 and VST3. It uses C++20, CMake, a pinned JUCE submodule for the plugin shell, CPU FFT analysis accelerated by Apple&amp;rsquo;s vDSP on macOS, and a native Metal renderer. Project-owned code is licensed under AGPL-3.0-or-later; JUCE retains its own upstream AGPL terms.&lt;/p>
&lt;p>The current release policy is pragmatic for a small open-source project: builds use ad hoc signing, and Developer ID signing and notarization are out of scope. Users can build from source. For a downloaded bundle, the documented flow is to verify the published checksum, extract it, clear quarantine only on the intended bundle, apply an ad hoc signature, and verify that signature.&lt;/p>
&lt;p>Older macOS versions, Intel/Universal builds, Windows, and AUv3 are architectural possibilities rather than current support promises. Logic compatibility, broader VST3 host coverage, multi-instance stress testing, and several formal performance gates also remain work in progress.&lt;/p>
&lt;p>The source, build instructions, and current limitations are all in the &lt;a class="link" href="https://github.com/charlie0129/audio-insight" target="_blank" rel="noopener"
>Audio Insight repository&lt;/a>. If you use analyzers but have never looked inside one, I hope the code makes the path from samples to pixels a little less mysterious. And if the Spectrogram glides across a 120 Hz display without drawing attention to the renderer, that is exactly the point.&lt;/p></description></item><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>Minimal Alpine Linux on a 1 GB Btrfs Root Disk</title><link>https://charlie0129.github.io/blog/p/alpine-minimal-btrfs-install/</link><pubDate>Thu, 25 Jun 2026 15:00:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/alpine-minimal-btrfs-install/</guid><description>&lt;p>I sometimes need a tiny cloud instance that only forwards traffic. CPU and memory can be small, and the disk is mostly wasted space. On one Alibaba Cloud &lt;code>ecs.t6-c4m1.large&lt;/code> instance, a 1 GB root disk on a long contract can push the cost down to about $0.40/month, but a normal Linux install leaves too little usable space.&lt;/p>
&lt;p>The trick is not complicated:&lt;/p>
&lt;ul>
&lt;li>Use Alpine Linux, because the base system is small.&lt;/li>
&lt;li>Keep &lt;code>/boot&lt;/code> tiny. A 512 MB boot partition is absurd on a 1 GB disk.&lt;/li>
&lt;li>Put &lt;code>/&lt;/code> on Btrfs with transparent compression. Application files, package metadata, and logs usually compress well.&lt;/li>
&lt;li>Do not allocate disk swap. Use zram later if the machine needs swap-like behavior.&lt;/li>
&lt;/ul>
&lt;p>This post is the install recipe I use to build a minimal BIOS/MBR Alpine image, then optionally convert it to QCOW2 for reuse.&lt;/p>
&lt;blockquote>
&lt;p>If you would rather not do this by hand, I later automated the whole thing: &lt;a class="link" href="https://charlie0129.github.io/blog/p/alpine-image-builder/" >Building Alpine Linux Disk Images Without a VM&lt;/a> is a script that builds the same class of image as a file, with no VM and no interactive steps, and boots under both BIOS and UEFI. This post is still the explanation of &lt;em>why&lt;/em> the pieces are the way they are.&lt;/p>&lt;/blockquote>
&lt;h2 id="assumptions">Assumptions
&lt;/h2>&lt;p>This guide targets a very specific VM shape:&lt;/p>
&lt;ul>
&lt;li>Legacy BIOS boot with MBR and Syslinux/Extlinux.&lt;/li>
&lt;li>One small ext4 &lt;code>/boot&lt;/code> partition and one Btrfs root partition.&lt;/li>
&lt;li>No disk encryption, no LVM, no UEFI, no separate &lt;code>/var&lt;/code>.&lt;/li>
&lt;li>The whole disk can be destroyed.&lt;/li>
&lt;/ul>
&lt;p>If your VM boots with UEFI, use an EFI system partition and GRUB instead. If your disk is NVMe, adapt the partition names to &lt;code>/dev/nvme0n1p1&lt;/code> and &lt;code>/dev/nvme0n1p2&lt;/code>.&lt;/p>
&lt;p>The final layout is:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Partition&lt;/th>
&lt;th style="text-align: right">Size&lt;/th>
&lt;th>Filesystem&lt;/th>
&lt;th>Mountpoint&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>/dev/vda1&lt;/code>&lt;/td>
&lt;td style="text-align: right">64 MB&lt;/td>
&lt;td>ext4&lt;/td>
&lt;td>&lt;code>/boot&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/dev/vda2&lt;/code>&lt;/td>
&lt;td style="text-align: right">Rest of disk (~500MB is fine)&lt;/td>
&lt;td>Btrfs&lt;/td>
&lt;td>&lt;code>/&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>64 MB is enough for this single-kernel Alpine image. The boot partition usage should be around 30M with Alpine 3.23 virt kernel (6.18). If you plan to keep multiple kernels or use a larger bootloader setup, use 128 MB instead.&lt;/p>
&lt;p>Before you start installing, create a VM with a ~512MB disk (yes, really). CPU and RAM can be small, because the install process is not resource-intensive. After the install, you can resize the disk to 1 GB or larger.&lt;/p>
&lt;p>Always go with a small disk first because the disk image can be expanded easily, but shrinking a disk is not trivial.&lt;/p>
&lt;h2 id="partition-the-disk">Partition The Disk
&lt;/h2>&lt;p>Set the disk variables so the commands below are harder to mistype:&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">DISK&lt;/span>&lt;span class="o">=&lt;/span>/dev/vda
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BOOT&lt;/span>&lt;span class="o">=&lt;/span>/dev/vda1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ROOT&lt;/span>&lt;span class="o">=&lt;/span>/dev/vda2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Now create a DOS/MBR partition table with a tiny boot partition:&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">fdisk &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$DISK&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Inside &lt;code>fdisk&lt;/code>, enter:&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;/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">o # new empty DOS partition table
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">n # new partition
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">p # primary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1 # partition number
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2048 # first sector, 1 MiB aligned
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+64M # size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a # toggle bootable flag
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">n # new partition
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">p # primary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2 # partition number
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">133120 # first sector immediately after the 64 MB partition
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> # press Enter for the default end sector
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">p # verify the table
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">w # write changes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The important details are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>/dev/vda1&lt;/code> should be bootable.&lt;/li>
&lt;li>&lt;code>/dev/vda1&lt;/code> should start at sector &lt;code>2048&lt;/code>.&lt;/li>
&lt;li>&lt;code>/dev/vda2&lt;/code> should start at sector &lt;code>133120&lt;/code>.&lt;/li>
&lt;li>The second partition should use the rest of the disk.&lt;/li>
&lt;/ul>
&lt;p>Check that the kernel sees the new partitions:&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 -l /dev/vda*
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If &lt;code>/dev/vda1&lt;/code> and &lt;code>/dev/vda2&lt;/code> do not appear, reboot the live ISO. Alpine&amp;rsquo;s manual disk setup docs also recommend rebooting after manual partition creation when needed.&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;h2 id="start-alpine-setup">Start Alpine Setup
&lt;/h2>&lt;p>Download the latest Alpine ISO from &lt;a class="link" href="https://alpinelinux.org/downloads/" target="_blank" rel="noopener"
>https://alpinelinux.org/downloads/&lt;/a>. I usually use the &lt;code>x86_64&lt;/code> architecture and the &lt;code>virtual&lt;/code> variant. As this will be used in virtualized environments, the &lt;code>virt&lt;/code> kernel is smaller.&lt;/p>
&lt;p>Boot the Alpine ISO and log in as &lt;code>root&lt;/code>. The live environment has no root password by default.&lt;/p>
&lt;p>Run the normal installer first:&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&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">setup-alpine
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Answer the usual questions for keyboard, hostname, network, DNS, root password, timezone, mirror, SSH, and NTP. I usually choose &lt;code>chrony&lt;/code> for NTP.&lt;/p>
&lt;p>When it asks which disk to use, type:&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">none
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This keeps the base configuration but skips automatic partitioning and formatting. For the later diskless-mode prompts, also choose &lt;code>none&lt;/code> for configs. You can keep the default apk cache dir (&lt;code>/var/cache/apk&lt;/code>).&lt;/p>
&lt;h2 id="format-and-mount">Format And Mount
&lt;/h2>&lt;p>Install the filesystem tools in the live environment:&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">apk add btrfs-progs e2fsprogs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Format &lt;code>/boot&lt;/code> as ext4 and &lt;code>/&lt;/code> as Btrfs:&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">mkfs.ext4 -m &lt;span class="m">0&lt;/span> -L boot &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$BOOT&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkfs.btrfs -f -L alpine-root &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$ROOT&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Mount root with compression enabled:&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"># adjust the compression level as desired. zstd:1 is fast and has reasonable compression. zstd:3 is slower but compresses better. Any higher level is usually overkill and comes with diminishing returns.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mount -t btrfs -o rw,relatime,compress&lt;span class="o">=&lt;/span>zstd:3,ssd,discard&lt;span class="o">=&lt;/span>async,space_cache&lt;span class="o">=&lt;/span>v2 &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$ROOT&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> /mnt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">btrfs property &lt;span class="nb">set&lt;/span> /mnt compression zstd:3
&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/boot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mount -t ext4 &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$BOOT&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> /mnt/boot
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>I keep &lt;code>/boot&lt;/code> on ext4 because it is small and predictable with this Syslinux/MBR setup. On a tiny image, boring boot is good boot.&lt;/p>
&lt;h2 id="install-alpine">Install Alpine
&lt;/h2>&lt;p>Install Alpine into the mounted filesystem:&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">setup-disk -v -m sys -s &lt;span class="m">0&lt;/span> /mnt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The options mean:&lt;/p>
&lt;ul>
&lt;li>&lt;code>-m sys&lt;/code>: install a traditional persistent system to disk.&lt;/li>
&lt;li>&lt;code>-s 0&lt;/code>: do not create disk swap.&lt;/li>
&lt;li>&lt;code>/mnt&lt;/code>: use the partitions already mounted under &lt;code>/mnt&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>This is the standard &lt;code>setup-disk -m sys /mnt&lt;/code> flow, just with a manually prepared root filesystem.&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>&lt;/p>
&lt;h2 id="verify-fstab-and-initramfs">Verify fstab And initramfs
&lt;/h2>&lt;p>Check &lt;code>/mnt/etc/fstab&lt;/code> before rebooting:&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">vi /mnt/etc/fstab
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The root entry should include Btrfs compression. Mine looks 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;/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">UUID=... / btrfs rw,relatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/ 0 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UUID=... /boot ext4 rw,relatime 0 2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Remove any stale &lt;code>cdrom&lt;/code> or &lt;code>usbdisk&lt;/code> entries if the installer generated them.&lt;/p>
&lt;p>Also check that the installed initramfs knows about Btrfs:&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">grep -w btrfs /mnt/etc/mkinitfs/mkinitfs.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If &lt;code>btrfs&lt;/code> is missing from &lt;code>features=&amp;quot;...&amp;quot;&lt;/code>, add it before rebooting and regenerate the initramfs from a chroot. Alpine explicitly calls this out for manual Btrfs root installs.&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>&lt;/p>
&lt;h2 id="fix-the-mbr-bootloader">Fix The MBR Bootloader
&lt;/h2>&lt;p>On these tiny MBR images, I have seen the first-stage bootloader not get written correctly. Reinstalling the Syslinux MBR is cheap insurance:&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">apk add syslinux
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dd &lt;span class="nv">bs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">440&lt;/span> &lt;span class="nv">count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="nv">conv&lt;/span>&lt;span class="o">=&lt;/span>notrunc &lt;span class="k">if&lt;/span>&lt;span class="o">=&lt;/span>/usr/share/syslinux/mbr.bin &lt;span class="nv">of&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$DISK&lt;/span>&lt;span class="s2">&amp;#34;&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>gptmbr.bin&lt;/code> instead only if you are using GPT. For the DOS partition table above, &lt;code>mbr.bin&lt;/code> is the right file.&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup>&lt;/p>
&lt;p>Now unmount and reboot:&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">&lt;span class="nb">cd&lt;/span> /
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sync
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">umount /mnt/boot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">umount /mnt
&lt;/span>&lt;/span>&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>After the first boot, log in and check the actual space 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">df -h /
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">btrfs filesystem usage /
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="convert-the-disk-to-qcow2">Convert The Disk To QCOW2
&lt;/h2>&lt;p>Once the image is configured the way you want, shut it down and convert the raw disk from 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-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">qemu-img convert -f raw -c -p -O qcow2 /dev/mapper/ex950-vm--300--disk--0 alpine-minimal.qcow2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Replace the source device with your VM disk. Do this from a stopped VM or a consistent snapshot, not from a running writable system.&lt;/p>
&lt;p>You can now reuse the QCOW2 as a base image for other VMs or cloud instances.&lt;/p>
&lt;h2 id="restore-from-the-providers-original-os">Restore From The Provider&amp;rsquo;s Original OS
&lt;/h2>&lt;p>Some cloud providers do not let you upload or boot a custom disk image. If you still have VNC/serial-console access, another way is to boot the provider&amp;rsquo;s original Linux image, download your Alpine QCOW2 there, convert it to raw, then reboot into the original OS initramfs and overwrite the whole disk from there.&lt;/p>
&lt;p>The important reason for the two-stage flow is tooling: the original OS probably has &lt;code>curl&lt;/code> and &lt;code>qemu-img&lt;/code>, while the initramfs usually does not. The initramfs is only used for the final &lt;code>dd&lt;/code>, because at that point the real root filesystem is not mounted and can be safely overwritten.&lt;/p>
&lt;p>This is destructive. Double-check the target disk and the old root partition before running &lt;code>dd&lt;/code>.&lt;/p>
&lt;p>First, in the provider&amp;rsquo;s original OS. This filesystem needs enough free space to hold both the downloaded QCOW2 and the converted raw image:&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">&lt;span class="c1"># Download the Alpine minimal image in qcow2 format and convert it to raw.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Do this in the original OS because the initramfs probably does not have&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># curl or qemu-img.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -L &lt;span class="s2">&amp;#34;&amp;lt;qcow2 link&amp;gt;&amp;#34;&lt;/span> -o /os.qcow2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">qemu-img convert -f qcow2 -O raw /os.qcow2 /os.raw
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then use the provider console or VNC to reboot into initramfs. On a GRUB-based original OS, select the normal boot entry, press &lt;code>e&lt;/code>, find the Linux kernel command line, append:&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">break=premount
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then press &lt;code>F10&lt;/code> or &lt;code>Ctrl-x&lt;/code> to boot. This should drop you into an initramfs shell before the root filesystem is mounted.&lt;/p>
&lt;p>From the initramfs shell:&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">&lt;span class="c1"># Find the old root partition and the target disk.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Example:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># old root partition: /dev/sda2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># target disk: /dev/sda&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat /proc/partitions
&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 /tmp/rootfs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> /tmp
&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"># Mount the old root filesystem read-only to copy the prepared image.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Replace /dev/sda2 with the root partition from the original OS, and&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># replace ext4 if the provider image uses another filesystem.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mount -t ext4 -o ro /dev/sda2 rootfs
&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"># Copy the raw image to initramfs memory so the old root can be unmounted&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># before overwriting the disk.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This requires enough RAM for /os.raw. If the VM is too small, attach a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># temporary rescue disk or use a rescue system that can stream the image&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># from the network instead.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dd &lt;span class="k">if&lt;/span>&lt;span class="o">=&lt;/span>rootfs/os.raw &lt;span class="nv">of&lt;/span>&lt;span class="o">=&lt;/span>os.raw
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">umount rootfs
&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"># This overwrites the whole disk. Replace /dev/sda with the target disk.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dd &lt;span class="k">if&lt;/span>&lt;span class="o">=&lt;/span>os.raw &lt;span class="nv">of&lt;/span>&lt;span class="o">=&lt;/span>/dev/sda
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&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>After booting into Alpine, apply provider-specific network settings. For a static IPv4 setup:&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">cat &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39; &amp;gt; /etc/network/interfaces
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">auto lo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">iface lo inet loopback
&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">auto eth0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">iface eth0 inet static
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> address X.X.X.X/24
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> gateway X.X.X.X
&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">cat &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39; &amp;gt; /etc/resolv.conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">nameserver 1.1.1.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">nameserver 1.0.0.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">setup-hostname XXXX
&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-service networking restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If the provider uses DHCP, keep the interface as DHCP instead and let Alpine request the address normally.&lt;/p>
&lt;h2 id="restore-without-a-console-or-rescue-mode">Restore Without A Console Or Rescue Mode
&lt;/h2>&lt;p>The cheapest tiers take the rest of it away too: no custom-image import, no VNC, no serial console, no rescue mode. The stock OS and SSH are all you get. The initramfs trick above is unreachable there, because its one console step — interrupting GRUB — has no SSH equivalent: an initramfs shell has no network stack and no sshd.&lt;/p>
&lt;p>What still works is having a script arrange that environment for you. &lt;a class="link" href="https://github.com/bin456789/reinstall" target="_blank" rel="noopener"
>bin456789/reinstall&lt;/a> rewrites the stock bootloader to boot a minimal Alpine that runs entirely in RAM, and from there downloads your image and &lt;code>dd&lt;/code>s it over the whole disk before rebooting into it. No console is involved at any point. You watch the progress over SSH, and if the download or the write fails, the RAM system keeps its sshd up, so a failure is a retry by hand rather than a reinstall ticket.&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">&lt;span class="c1"># From the provider&amp;#39;s stock OS, as root. This is destructive: it will&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># wipe the whole disk, other partitions included.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Mainland-China boxes often cannot reach raw.githubusercontent.com; the&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># project mirrors the script at cnb.cool for exactly that case.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -O https://raw.githubusercontent.com/bin456789/reinstall/main/reinstall.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># curl -O https://cnb.cool/bin456789/reinstall/-/git/raw/main/reinstall.sh&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="c1"># Prompts for a username/password, used to log in over SSH and watch.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># .gz / .xz / .zst compressed raw images are accepted directly.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bash reinstall.sh dd --img&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://example.com/alpine.img.gz&amp;#34;&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="c1"># Still reversible until here: `bash reinstall.sh reset` restores the&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># original bootloader config.&lt;/span>
&lt;/span>&lt;/span>&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>Before it does anything, the script asks for a username and a password, and it is worth being clear about what they are not: the new system&amp;rsquo;s login. They exist only inside the RAM installer environment — they are what you would type to SSH back in while the &lt;code>dd&lt;/code> runs, to watch progress or clean up after a failure. A Linux image gets nothing injected; the script&amp;rsquo;s own end-of-run summary says as much, printing &lt;code>Password: [Depends on image]&lt;/code> under &amp;ldquo;After Install&amp;rdquo;. So press Enter twice — &lt;code>root&lt;/code>, then a random password the summary displays for copying — and let whatever the image baked in be the machine&amp;rsquo;s real credentials. &lt;code>--ssh-key&lt;/code> replaces that password with one of your public keys for the installer environment, if typing nothing at all is the goal.&lt;/p>
&lt;p>When you do log in mid-install, expect a host-key mismatch: the RAM environment presents its own host keys, and so does the finished image, so the client will complain about the stock OS&amp;rsquo;s key on the way in and about both of them after the final reboot. That is the process working, not something broken — clear the old entry with &lt;code>ssh-keygen -R&lt;/code> or accept the new key each time. On a NAT&amp;rsquo;d box the installer&amp;rsquo;s sshd still listens on port 22 internally, so the provider&amp;rsquo;s forwarded port keeps working throughout.&lt;/p>
&lt;p>Four things to get right, all of them consequences of there being no console:&lt;/p>
&lt;p>&lt;strong>The image URL must be reachable from the VPS.&lt;/strong> The download runs inside the RAM environment, over the same network the stock OS uses. For a box in mainland China that usually rules out GitHub-hosted files; one of my own servers or local object storage works better.&lt;/p>
&lt;p>&lt;strong>The image has to work as-is.&lt;/strong> &lt;code>dd&lt;/code> mode does not modify a Linux image, so the network settings, the SSH keys, and the sshd port must be baked into the image &lt;em>before&lt;/em> flashing — the &amp;ldquo;apply provider-specific network settings&amp;rdquo; step above has to happen inside the image, not on the machine afterwards. On a NAT&amp;rsquo;d box, where the provider forwards one public port to (say) internal port 22, changing the sshd port in the image orphans that forward and the machine. If the image comes from &lt;a class="link" href="https://charlie0129.github.io/blog/p/alpine-image-builder/" >the builder&lt;/a>, the &lt;code>10-network&lt;/code> and &lt;code>20-ssh&lt;/code> hooks are where this goes.&lt;/p>
&lt;p>&lt;strong>Check whether the stock OS keeps its root on LVM before rebooting.&lt;/strong> The script stages &lt;code>reinstall-vmlinuz&lt;/code> and &lt;code>reinstall-initrd&lt;/code> in the stock OS&amp;rsquo;s root directory and lets GRUB find them with &lt;code>search --file&lt;/code>. GRUB&amp;rsquo;s LVM support is incomplete — thin-provisioned volumes are unsupported outright, and some perfectly ordinary LVM roots also defeat it for reasons that are not understood (&lt;a class="link" href="https://github.com/bin456789/reinstall/issues/355" target="_blank" rel="noopener"
>issue #355&lt;/a>). When that happens, GRUB stops at &lt;code>file '/reinstall-vmlinuz' not found&lt;/code> and the RAM environment is never reached. On a machine with a console that is a visible error; on this kind of machine the box reboots and simply never speaks again. One &lt;code>lsblk&lt;/code> settles it beforehand — if &lt;code>/&lt;/code> sits on an &lt;code>lvm&lt;/code> device, copy both files to &lt;code>/boot&lt;/code>, which Debian-style LVM installs keep on a plain partition that GRUB reads reliably:&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">lsblk -o NAME,TYPE,FSTYPE,MOUNTPOINTS &lt;span class="c1"># is / on an lvm device?&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cp /reinstall-vmlinuz /reinstall-initrd /boot/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The copies are harmless if they were not needed: &lt;code>search --file&lt;/code> scans every filesystem GRUB can read, so it finds the &lt;code>/boot&lt;/code> copies whenever the LVM ones are invisible. The workaround is the tool author&amp;rsquo;s own, from the issue thread. If &lt;code>/boot&lt;/code> is only a directory inside the LVM root, there is no plain partition to stage onto, and I would not run this flow on such a box without a console.&lt;/p>
&lt;p>&lt;strong>Audit before the final reboot.&lt;/strong> &lt;code>--hold 2&lt;/code> stops after the &lt;code>dd&lt;/code> finishes but before the reboot. SSH back in, mount the freshly written root and boot partitions read-write, and check &lt;code>/etc/network/interfaces&lt;/code>, the authorized keys, and the bootloader config while a mistake is still fixable with a text editor. It is the last moment anything is.&lt;/p>
&lt;p>The RAM environment is small — the project lists 256 MB of RAM as the minimum — so this works on boxes too tiny for anything else. To drive the final &lt;code>dd&lt;/code> by hand instead, &lt;code>bash reinstall.sh alpine --hold 1&lt;/code> boots the same RAM Alpine and stops there, nothing written; it is also a cheap dry run for whether that environment&amp;rsquo;s networking works on the provider&amp;rsquo;s NAT at all. And if the machine still does not come back after the last reboot, the floor is the provider&amp;rsquo;s own reinstall button, back to the stock OS — annoying, not fatal.&lt;/p>
&lt;h2 id="things-i-bake-into-the-image">Things I Bake Into The Image
&lt;/h2>&lt;p>The base install above is intentionally small. The sections below are optional knobs I usually apply before making the reusable image.&lt;/p>
&lt;h3 id="ssh">SSH
&lt;/h3>&lt;p>For a private forwarding box, I allow root SSH and TCP forwarding. Use key auth and firewall rules if this machine is reachable from the public Internet.&lt;/p>
&lt;p>Add or change these lines in &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;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">PermitRootLogin yes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">AllowTcpForwarding 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>Restart SSH:&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">rc-service sshd restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>ClientAliveInterval 60&lt;/code> plus &lt;code>ClientAliveCountMax 3&lt;/code> makes dead SSH forwarding sessions disappear after roughly three minutes instead of waiting for long TCP timeouts.&lt;/p>
&lt;h3 id="community-repository">Community Repository
&lt;/h3>&lt;p>Many useful packages live in &lt;code>community&lt;/code>.&lt;/p>
&lt;p>Either 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;/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">setup-apkrepos -c
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Or edit &lt;code>/etc/apk/repositories&lt;/code> and uncomment the matching community repository:&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"># http://dl-cdn.alpinelinux.org/alpine/&amp;lt;alpine version&amp;gt;/community
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then refresh indexes:&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">apk update
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="ipv6-dhcp">IPv6 DHCP
&lt;/h3>&lt;p>SLAAC may work without any configuration. Some cloud providers need DHCPv6, in which case add an IPv6 stanza to &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;/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 eth0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface eth0 inet dhcp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iface eth0 inet6 dhcp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Install the DHCP and interface tooling if your image does not already have 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;/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 dhcpcd ifupdown-ng
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service networking restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="chrony">Chrony
&lt;/h3>&lt;p>If NTP was not configured during install:&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">apk add chrony
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">vi /etc/chrony/chrony.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">&lt;span class="c1"># Use `ntp.aliyun.com` if you are in mainland China; `pool.ntp.org`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># is often unreliable from there. Elsewhere, the default pool is fine.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Also add `makestep 1.0 -1` so chrony can correct large time offsets&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># on startup, which is common in VMs. You can remove the default&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># `initstepslew ...` line if it exists.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The result should look 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;/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="n">pool&lt;/span> &lt;span class="n">pool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ntp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">org&lt;/span> &lt;span class="n">iburst&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">initstepslew&lt;/span> &lt;span class="mi">10&lt;/span> &lt;span class="n">pool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ntp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">org&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">driftfile&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">chrony&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">chrony&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">drift&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rtcsync&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmdport&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">makestep&lt;/span> &lt;span class="mf">1.0&lt;/span> &lt;span class="o">-&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;p>Enable 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;/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">rc-update add chronyd default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service chronyd restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="log-caps">Log Caps
&lt;/h3>&lt;p>Alpine does not install a full log rotation stack by default. On a 1 GB disk, I prefer a tiny periodic script that keeps one backup and truncates logs in place so daemons can continue writing.&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;/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;&amp;#39;EOF&amp;#39; &amp;gt; /etc/periodic/hourly/logtruncate
&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"># Rotate log files larger than 4M.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"># Keep one .0 backup and truncate the original in place.
&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)) # 4M in bytes
&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"> # Works with both GNU and BusyBox stat.
&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 logtruncate &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">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> # Copy current content to .0 backup, overwriting the old backup.
&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">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> # Preserve the inode so daemons can continue writing.
&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">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> logger -t logtruncate &amp;#34;Rotated $file (backup: $file.0)&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">rc-update add crond default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service crond start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then tell BusyBox syslog not to rotate by itself:&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/^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">rc-service syslog restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="apk-cache-cleanup">APK Cache Cleanup
&lt;/h3>&lt;p>If apk caching is enabled later, clean old packages periodically:&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">cat &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39; &amp;gt; /etc/periodic/daily/apkcacheclean
&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;h3 id="timezone">Timezone
&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;/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 tzdata
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">setup-timezone -z Asia/Shanghai &lt;span class="c1"># replace with your timezone&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="zerotier">ZeroTier
&lt;/h3>&lt;p>Alpine&amp;rsquo;s ZeroTier package can lag behind upstream, so I usually use static binaries from &lt;a class="link" href="https://github.com/charlie0129/zerotier-static" target="_blank" rel="noopener"
>zerotier-static&lt;/a>.&lt;/p>
&lt;p>After placing &lt;code>zerotier-one&lt;/code> at &lt;code>/usr/local/bin/zerotier-one&lt;/code>, create an OpenRC service:&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;/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;&amp;#39;EOF&amp;#39; &amp;gt; /etc/init.d/zerotier-one
&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">depend() {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> after network-online
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> want cgroups
&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_pre() {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> /sbin/modprobe tun
&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">supervisor=supervise-daemon
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">name=zerotier-one
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">command=&amp;#34;/usr/local/bin/zerotier-one&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">command_args=&amp;#34; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;gt;&amp;gt;/var/log/zerotier-one.log 2&amp;gt;&amp;amp;1&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">output_log=/var/log/zerotier-one.log
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">error_log=/var/log/zerotier-one.log
&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">pidfile=&amp;#34;/var/run/zerotier-one.pid&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">respawn_delay=5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">respawn_max=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">chmod +x /etc/init.d/zerotier-one
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-update add zerotier-one default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service zerotier-one start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="zram-swap">ZRAM Swap
&lt;/h3>&lt;p>Zram is more useful than disk swap. Install &lt;code>zram-init&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;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">apk add zram-init
&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"># Adjust as needed. The default config creates swap and /tmp on zram.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># I usually set swap zram to roughly the same size as RAM and use lz4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># for speed. Because zram compresses memory, that does not mean it&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># immediately consumes that much physical RAM.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># To set swap zram to the same size as RAM, remove the default&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># `size0=512M` line and use:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># size0=`LC_ALL=C free -m | awk &amp;#39;/^Mem:/{print int($2)}&amp;#39;`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># To use lz4:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># algo0=lz4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The /tmp zram can be left untouched. If you want a smaller /tmp, use&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># something like:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># size1=`LC_ALL=C free -m | awk &amp;#39;/^Mem:/{print int($2/4)}&amp;#39;`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Remove `blck1=1024` if your default config has it. The minimum block&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># size is 4 KiB, so 1024 bytes is not valid.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If /tmp is already mounted as tmpfs in /etc/fstab, remove that line&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># when /tmp is provided by zram-init.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">vi /etc/conf.d/zram-init
&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 zram-init boot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service zram-init start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>These sysctls make the kernel more willing to use compressed 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;/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;&amp;#39;EOF&amp;#39; &amp;gt; /etc/sysctl.d/01-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;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sysctl -p /etc/sysctl.d/01-zram.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="cloud-init">Cloud-init
&lt;/h3>&lt;p>&lt;code>cloud-init&lt;/code> is convenient if the image will be imported into a cloud provider, but it pulls in Python and is not small.&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 cloud-init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Read the installation notes displayed by apk. It will show which&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># cloud-init services should be enabled for your target environment.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Read the package message after installation and enable the OpenRC services it lists. If you want automatic partition growth on first boot, also install:&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">apk add cloud-utils-growpart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For the smallest image, skip cloud-init and inject network configuration another way.&lt;/p>
&lt;h3 id="expand-root-after-restoring-to-a-larger-disk">Expand Root After Restoring To A Larger Disk
&lt;/h3>&lt;p>If you restore the QCOW2 to a larger disk, grow the root partition and then grow Btrfs.&lt;/p>
&lt;p>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;/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">fdisk /dev/vda
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Inside &lt;code>fdisk&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;/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">p # print; write down the current start sector of vda2, usually 133120
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">d # delete partition
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2 # select vda2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">n # new partition
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">p # primary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2 # partition number 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">133120 # use the exact old start sector
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;ENTER&amp;gt; # default end, use the rest of the disk
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; If asked:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; Partition #2 contains a btrfs signature.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; Do you want to remove the signature? [Y]es/[N]o:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">N # DO NOT remove the signature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">p # verify vda2 starts at the same sector and uses the rest of the disk
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">w # write changes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Ask the kernel to reread the partition table:&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">apk add parted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">partprobe /dev/vda &lt;span class="o">||&lt;/span> reboot
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you had to reboot, continue after the reboot:&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">btrfs filesystem resize max /
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">df -h /
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>I prefer this manual method over blindly using a grow tool because it actually expands and aligns, while some grow tools may not expand the partition to be 4K aligned.&lt;/p>
&lt;h3 id="bbr-congestion-control">BBR Congestion Control
&lt;/h3>&lt;p>For forwarding boxes, I usually enable BBR:&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;&amp;#39;EOF&amp;#39; &amp;gt; /etc/sysctl.d/02-bbr.conf
&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 -p /etc/sysctl.d/02-bbr.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Verify:&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.ipv4.tcp_congestion_control
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="podman">Podman
&lt;/h3>&lt;p>If I need containers on a tiny VM, I prefer Podman over Docker because the entire stack (no containerd, crun instrad of runc, lighter daemon).&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">apk add podman
&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"># Keep the overlay storage driver. It is usually more stable and has&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># better performance for containers, even though the underlying&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># filesystem is Btrfs.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Limit container logs to 1M so they do not fill the disk.&lt;/span>
&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">&lt;span class="c1"># Enable cgroups v2 for better compatibility with modern container runtimes.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-update add cgroups default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service cgroups 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">&lt;span class="c1"># Start containers that have a restart policy such as always or unless-stopped.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-update add podman default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service podman start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you want Docker CLI and Compose compatibility against the Podman socket:&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 add docker-cli docker-cli-compose
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&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>I do not use &lt;code>podman-docker&lt;/code> or &lt;code>podman-compose&lt;/code> here; the Docker CLI is closer to what my existing Compose files expect.&lt;/p>
&lt;h3 id="ufw">UFW
&lt;/h3>&lt;p>If the provider does not give you a firewall, configure one before exposing the VM:&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;/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 ip6tables ufw
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Start ufw later, after the allow rules are in place.&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="c1"># Default policies&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw default deny incoming
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw default allow outgoing
&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"># Allow loopback&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw allow in on lo
&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"># -------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># SSH&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -------------------------&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="c1"># SSH port; change this if you use a non-standard SSH port.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw allow 22/tcp comment &lt;span class="s1">&amp;#39;SSH&amp;#39;&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="c1"># Optional: rate limit SSH brute force.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw limit 22/tcp&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="c1"># -------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Private / intranet ranges&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -------------------------&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="c1"># RFC1918 IPv4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw allow from 10.0.0.0/8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw allow from 172.16.0.0/12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw allow from 192.168.0.0/16
&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"># Optional CGNAT range&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw allow from 100.64.0.0/10&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="c1"># Optional IPv6 ULA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw allow from fc00::/7
&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"># Also allow forwarding to RFC1918 and ULA ranges if this server is a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># gateway or VPN server, or if you use Podman containers with published&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ports. Published container ports usually pass through DNAT and are&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># handled by forward rules.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Note that if you are using Podman containers with published ports,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># these route rules can allow access to all published ports even if the&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># input rules only allow selected ports. If that is not what you want,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># only allow specific forwarded ports here.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw route allow to 10.0.0.0/8&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw route allow to 172.16.0.0/12&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw route allow to 192.168.0.0/16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw route allow to fc00::/7&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="c1"># -------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Public services&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -------------------------&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="c1"># Web&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw allow 80/tcp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw allow 443/tcp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw allow 443/udp&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="c1"># Example app ports&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw allow 3000/tcp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ufw allow 9090/tcp&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="c1"># -------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Logging&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -------------------------&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">ufw logging low
&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"># Enable firewall&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw &lt;span class="nb">enable&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">rc-update add ufw default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service ufw start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If this machine routes VPN, overlay network, or container traffic, add explicit &lt;code>ufw route allow ...&lt;/code> rules for the private ranges you actually need.&lt;/p>
&lt;h3 id="sshguard">sshguard
&lt;/h3>&lt;p>I avoid Fail2ban on this image because Python is too heavy for the target machine. &lt;code>sshguard&lt;/code> is small and good enough.&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">apk add sshguard nftables
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Do not blindly start the &lt;code>nftables&lt;/code> service before checking its default ruleset from a console. On one of my Alpine images, doing that installed a default deny ruleset and locked out remote SSH. I let &lt;code>sshguard&lt;/code> manage its own nftables sets 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;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;/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"># IMPORTANT: Do not run `rc-update add nftables &amp;amp;&amp;amp; rc-service nftables start`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># unless you have checked the ruleset from a console. We will let sshguard&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># manage its nftables sets.&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">mkdir -p /var/lib/sshguard
&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 &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39; &amp;gt; /etc/sshguard.conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"># Full path to backend executable. Required; there is no default.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">BACKEND=&amp;#39;/usr/libexec/sshg-fw-nft-sets&amp;#39;
&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"># Space-separated list of log files to monitor.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">FILES=&amp;#39;/var/log/messages&amp;#39;
&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"># Block attackers when their cumulative attack score exceeds THRESHOLD.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"># Most attacks have a score of 10.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">THRESHOLD=20
&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"># Block attackers for initially BLOCK_TIME seconds after exceeding THRESHOLD.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"># Subsequent blocks increase by a factor of 1.5.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">BLOCK_TIME=180
&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"># Remember potential attackers for up to DETECTION_TIME seconds before
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"># resetting their score.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">DETECTION_TIME=3600
&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"># Permanently blacklist attackers when their cumulative score exceeds threshold.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">BLACKLIST_FILE=100:/var/lib/sshguard/blacklist.db
&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"># Size of IPv6 subnet to block. Defaults to a single address.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">IPV6_SUBNET=48
&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"># Size of IPv4 subnet to block. Defaults to a single address.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">IPV4_SUBNET=24
&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">rc-update add sshguard default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rc-service sshguard start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="common-tools">Common Tools
&lt;/h3>&lt;p>This is no longer minimal, but it makes Alpine feel closer to a general-purpose server if you find busybox too limited:&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">apk add bash coreutils findutils tar grep sed gawk diffutils procps util-linux shadow curl wget iproute2 bind-tools gcompat pciutils
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apk add netcat-openbsd socat tcpdump iftop iptraf-ng ethtool traceroute zsh git htop tmux vim less jq iperf3 sysstat rsync file
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>And other tools&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">apk add bat dufs gdu lsd yazi
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="final-notes">Final Notes
&lt;/h2>&lt;p>The most important part of this setup is the partitioning discipline. On a 1 GB disk, a lazy 512 MB &lt;code>/boot&lt;/code>, a swap partition, or unbounded logs will consume the entire machine faster than the application does.&lt;/p>
&lt;p>With a 64 MB &lt;code>/boot&lt;/code>, compressed Btrfs root, no disk swap, and a few periodic cleanup jobs, Alpine stays usable even on tiny cloud disks. It is not luxurious, but it is enough for a forwarding node or a small single-purpose service.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a class="link" href="https://wiki.alpinelinux.org/wiki/Setting_up_disks_manually" target="_blank" rel="noopener"
>Setting up disks manually - Alpine Linux&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a class="link" href="https://docs.alpinelinux.org/user-handbook/0.1a/Installing/setup_alpine.html" target="_blank" rel="noopener"
>setup-alpine - Alpine Linux Documentation&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a class="link" href="https://wiki.alpinelinux.org/w/index.php?title=Install_to_disk" target="_blank" rel="noopener"
>System Disk Mode - Alpine Linux&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>&lt;a class="link" href="https://wiki.alpinelinux.org/wiki/Btrfs" target="_blank" rel="noopener"
>Btrfs - Alpine Linux&lt;/a>&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>&lt;a class="link" href="https://wiki.alpinelinux.org/wiki/Bootloaders" target="_blank" rel="noopener"
>Bootloaders - Alpine Linux&lt;/a>&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Bundling macOS Dynamic Libraries</title><link>https://charlie0129.github.io/blog/p/bundling-macos-dynamic-libraries/</link><pubDate>Mon, 08 Jun 2026 12:25:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/bundling-macos-dynamic-libraries/</guid><description>&lt;p>I recently tried to build QEMU on macOS and make the result portable enough that it could still run after uninstalling the Homebrew packages used during the build, or I can move the whole QEMU directory to another machine. I don&amp;rsquo;t want the Homebrew build because it installs too many dependencies on the system, and I want a more self-contained bundle, not a system-wide installation.&lt;/p>
&lt;p>The short version: &lt;strong>fully static linking on macOS is not really the right target&lt;/strong>. macOS binaries still dynamically link against Apple system libraries such as &lt;code>libSystem&lt;/code>. But for tools like QEMU, what I really wanted was not a “pure static binary”; I wanted a self-contained directory 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;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-text" data-lang="text">&lt;span class="line">&lt;span class="cl">_qemu-11.0.1/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── bin/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── qemu-img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── qemu-system-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">└── lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libgobject-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libintl.8.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libpcre2-8.0.dylib
&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>The binaries in &lt;code>bin/&lt;/code> should load their non-system dependencies from &lt;code>../lib&lt;/code> (note that it&amp;rsquo;s relative, not absolute, so it will work even if the whole directory is moved), instead of from &lt;code>/opt/homebrew&lt;/code>.&lt;/p>
&lt;p>This post records the process, using QEMU as the example.&lt;/p>
&lt;h2 id="the-original-problem">The original problem
&lt;/h2>&lt;p>I built QEMU roughly 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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># You should refer to https://wiki.qemu.org/Hosts/Mac for the latest build instructions. This is just an example.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install libffi gettext glib pkg-config zstd &lt;span class="c1"># may not be the exact set of dependencies you need&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">./configure --prefix&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/bin/_qemu-11.0.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make -j&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>sysctl -n hw.ncpu&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make install
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The build worked. But after uninstalling some Homebrew dependencies, running &lt;code>qemu-img&lt;/code> failed:&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">dyld[82783]: Library not loaded: /opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Referenced from: /Users/charlie/bin/_qemu-11.0.1/bin/qemu-img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reason: tried: &amp;#39;/opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib&amp;#39; (no such file)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zsh: abort qemu-img
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This means the installed QEMU binary still contains an absolute dependency path:&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">/opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>We can confirm that 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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">otool -L &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">/bin/qemu-img&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Example output:&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-text" data-lang="text">&lt;span class="line">&lt;span class="cl">qemu-img:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib (compatibility version 8801.0.0, current version 8801.0.0)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /opt/homebrew/opt/zstd/lib/libzstd.1.dylib (compatibility version 1.0.0, current version 1.5.7)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)
&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>The &lt;code>/usr/lib&lt;/code> and &lt;code>/System/Library&lt;/code> entries are fine. They are macOS system libraries. The &lt;code>/opt/homebrew/...&lt;/code> entries are the ones that make the binary depend on my local Homebrew installation.&lt;/p>
&lt;h2 id="why-not-just-build-qemu-fully-static">Why not just build QEMU fully static?
&lt;/h2>&lt;p>On Linux, the natural thought is “just build a static binary”.&lt;/p>
&lt;p>On macOS, that is not usually how things work. macOS does not support the same style of fully static userland binary that Linux users often expect. You can try to statically link some third-party libraries, but the final program will still dynamically link to Apple system libraries.&lt;/p>
&lt;p>So the practical goal is:&lt;/p>
&lt;p>Bundle third-party &lt;code>.dylib&lt;/code> dependencies next to the program, and rewrite Mach-O load commands so the binary loads those local copies.&lt;/p>
&lt;p>This is similar in spirit to what many &lt;code>.app&lt;/code> bundles do, but here I wanted a plain CLI directory layout.&lt;/p>
&lt;h2 id="the-tools-involved">The tools involved
&lt;/h2>&lt;p>macOS Mach-O binaries can be inspected and patched with these tools:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">otool -L &amp;lt;file&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">install_name_tool -change &amp;lt;old&amp;gt; &amp;lt;new&amp;gt; &amp;lt;file&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">install_name_tool -id &amp;lt;new-id&amp;gt; &amp;lt;dylib&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">install_name_tool -add_rpath &amp;lt;rpath&amp;gt; &amp;lt;file&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>otool -L&lt;/code> shows dynamic library load commands.&lt;/p>
&lt;p>For 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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">otool -L &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">/bin/qemu-img&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>might show:&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-text" data-lang="text">&lt;span class="line">&lt;span class="cl">qemu-img:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib (compatibility version 8801.0.0, current version 8801.0.0)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /opt/homebrew/opt/zstd/lib/libzstd.1.dylib (compatibility version 1.0.0, current version 1.5.7)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)
&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>Those can be rewritten 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;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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">install_name_tool -change &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> /opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s1">&amp;#39;@executable_path/../lib/libglib-2.0.0.dylib&amp;#39;&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> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">/bin/qemu-img&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>@executable_path&lt;/code> means “the directory containing the executable being launched”.&lt;/p>
&lt;p>So it will try to find &lt;code>libglib-2.0.0.dylib&lt;/code> in &lt;code>../lib/&lt;/code> relative to the executable, no longer looking in &lt;code>/opt/homebrew&lt;/code>.&lt;/p>
&lt;h2 id="first-attempt-patch-direct-dependencies-only">First attempt: patch direct dependencies only
&lt;/h2>&lt;p>My first attempt was simple:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">otool -L &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>/bin/* &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="p">|&lt;/span> grep /opt/homebrew &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="p">|&lt;/span> sort &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="p">|&lt;/span> uniq &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="p">|&lt;/span> awk &lt;span class="s1">&amp;#39;{print $1}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This found direct Homebrew dependencies used by QEMU binaries, such as:&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-text" data-lang="text">&lt;span class="line">&lt;span class="cl">/opt/homebrew/opt/glib/lib/libgio-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/opt/homebrew/opt/glib/lib/libgmodule-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/opt/homebrew/opt/glib/lib/libgobject-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/opt/homebrew/opt/zstd/lib/libzstd.1.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then I copied those libraries into &lt;code>$PREFIX/lib&lt;/code> and patched the binaries.&lt;/p>
&lt;p>This fixed some errors, but not all of them.&lt;/p>
&lt;p>Running &lt;code>qemu-img&lt;/code> then failed with a different error:&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">dyld[97126]: Library not loaded: /opt/homebrew/opt/gettext/lib/libintl.8.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Referenced from: /Users/charlie/bin/_qemu-11.0.1/lib/libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>So I realized that it is not enough to patch only the binaries. The copied &lt;code>.dylib&lt;/code> files have their own dependencies too.&lt;/p>
&lt;p>In this 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;/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">qemu-img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; libintl.8.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>libintl.8.dylib&lt;/code> was not directly referenced by &lt;code>qemu-img&lt;/code>. It was a transitive dependency of &lt;code>libglib&lt;/code>.&lt;/p>
&lt;h2 id="the-correct-model">The correct model
&lt;/h2>&lt;p>The dependency graph looks 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;/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">bin/qemu-img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; /opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; /opt/homebrew/opt/zstd/lib/libzstd.1.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lib/libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; /opt/homebrew/opt/gettext/lib/libintl.8.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; /opt/homebrew/opt/pcre2/lib/libpcre2-8.0.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>So the bundling process must be recursive:&lt;/p>
&lt;ol>
&lt;li>Scan Mach-O files in &lt;code>bin/&lt;/code>.&lt;/li>
&lt;li>Find its &lt;code>/opt/homebrew&lt;/code> dependencies.&lt;/li>
&lt;li>Copy those &lt;code>.dylib&lt;/code> files into &lt;code>lib/&lt;/code>.&lt;/li>
&lt;li>Patch the binaries to refer to the bundled copies (&lt;code>../lib&lt;/code>).&lt;/li>
&lt;li>Scan the copied &lt;code>.dylib&lt;/code> files.&lt;/li>
&lt;li>Copy and patch their dependencies too.&lt;/li>
&lt;li>Repeat until no &lt;code>/opt/homebrew&lt;/code> dependencies remain.&lt;/li>
&lt;/ol>
&lt;h2 id="which-paths-should-be-used">Which paths should be used?
&lt;/h2>&lt;p>For executables in &lt;code>bin/&lt;/code>, I use:&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">@executable_path/../lib/libfoo.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For dylibs inside &lt;code>lib/&lt;/code>, I use:&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">@loader_path/libfoo.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>
&lt;p>&lt;code>@executable_path&lt;/code> is relative to the main executable being launched.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>@loader_path&lt;/code> is relative to the Mach-O file that is doing the loading. For dependencies between dylibs, this is what we want:&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">lib/libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; @loader_path/libintl.8.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>Since both files are in the same &lt;code>lib/&lt;/code> directory, this resolves correctly.&lt;/p>
&lt;h2 id="the-final-bundling-script">The final bundling script
&lt;/h2>&lt;p>I ended up writing a small Python script called &lt;code>macho-bundle-deps&lt;/code>. You can find it here: &lt;a class="link" href="https://github.com/charlie0129/dotfiles/blob/f15791054e517f6b5afc892312f1db73f331d475/bin/darwin/macho-bundle-deps" target="_blank" rel="noopener"
>https://github.com/charlie0129/dotfiles/blob/f15791054e517f6b5afc892312f1db73f331d475/bin/darwin/macho-bundle-deps&lt;/a>. This is a permanent link to a specific commit, so you may want to check the latest version in the repository.&lt;/p>
&lt;p>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;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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PREFIX&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/bin/_qemu-11.0.1&amp;#34;&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">macho-bundle-deps &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --lib-dir &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">/lib&amp;#34;&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> --prefix /opt/homebrew &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>/bin
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Or, from inside the QEMU prefix:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/bin/_qemu-11.0.1&amp;#34;&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">macho-bundle-deps --lib-dir lib bin
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The script does the following:&lt;/p>
&lt;ul>
&lt;li>finds Mach-O files from the input paths&lt;/li>
&lt;li>scans their dependencies with &lt;code>otool -L&lt;/code>&lt;/li>
&lt;li>copies matching dependencies into &lt;code>--lib-dir&lt;/code>&lt;/li>
&lt;li>patches executable dependencies to &lt;code>@executable_path/../lib/...&lt;/code>&lt;/li>
&lt;li>patches bundled dylib dependencies to &lt;code>@loader_path/...&lt;/code>&lt;/li>
&lt;li>patches copied dylib IDs with &lt;code>install_name_tool -id&lt;/code>&lt;/li>
&lt;li>recursively processes newly copied dylibs&lt;/li>
&lt;li>reports an error if matching absolute dependency paths remain&lt;/li>
&lt;/ul>
&lt;h2 id="verifying-the-result">Verifying the result
&lt;/h2>&lt;p>After running the bundler, I verify that no Homebrew paths remain:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">otool -L &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>/bin/* &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>/lib/*.dylib &lt;span class="p">|&lt;/span> grep /opt/homebrew
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Now I can uninstall the dependencies from Homebrew, and the bundled QEMU still works:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">brew uninstall libffi gettext glib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">qemu-img --help
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="notes-and-limitations">Notes and limitations
&lt;/h2>&lt;p>This is not the same as producing a fully static binary.&lt;/p>
&lt;p>The result still depends on macOS system libraries, which is normal:&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">/usr/lib/libSystem.B.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/System/Library/Frameworks/...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>It also does not magically make a binary portable across all macOS versions or CPU architectures. A binary built on Apple Silicon is still an arm64 Mach-O binary unless built otherwise.&lt;/p>
&lt;p>This approach is mainly useful for making a local CLI tool directory self-contained with respect to Homebrew dependencies.&lt;/p>
&lt;h2 id="why-copying-symlinks-should-be-avoided">Why copying symlinks should be avoided
&lt;/h2>&lt;p>Homebrew often has dylib symlinks 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">libintl.dylib -&amp;gt; libintl.8.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>When bundling, prefer copying the symlink target, not the symlink itself.&lt;/p>
&lt;p>In shell, that means:&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-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">cp -L source.dylib &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2">/lib/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In Python, &lt;code>shutil.copy2(..., follow_symlinks=True)&lt;/code> does the same thing.&lt;/p>
&lt;p>This avoids ending up with a bundled symlink pointing to a non-existent file.&lt;/p>
&lt;h2 id="final-layout">Final layout
&lt;/h2>&lt;p>After bundling, the QEMU directory looks 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;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;/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">_qemu-11.0.1/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── bin/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── qemu-img
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── qemu-io
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── qemu-nbd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── qemu-system-aarch64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── qemu-system-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">└── lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libffi.8.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libgio-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libglib-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libgmodule-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libgobject-2.0.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libintl.8.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libpcre2-8.0.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── libzstd.1.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Now &lt;code>qemu-img&lt;/code> no longer cares whether Homebrew’s &lt;code>glib&lt;/code>, &lt;code>gettext&lt;/code>, &lt;code>pcre2&lt;/code>, or &lt;code>zstd&lt;/code> packages are installed.&lt;/p>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>The main lesson is that macOS dependency bundling is graph traversal, not a one-shot patch.&lt;/p>
&lt;p>Patching only the top-level binaries is not enough. You also need to patch the dylibs that you copied, and then patch the dylibs that those dylibs depend on.&lt;/p>
&lt;p>The final strategy is:&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">scan -&amp;gt; copy -&amp;gt; patch -&amp;gt; scan copied dylibs -&amp;gt; repeat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For executables:&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">/opt/homebrew/.../libfoo.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; @executable_path/../lib/libfoo.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>For bundled dylibs:&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">/opt/homebrew/.../libbar.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; @loader_path/libbar.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>And for the dylib’s own install name:&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">/opt/homebrew/.../libfoo.dylib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; @loader_path/libfoo.dylib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>(Chinese Only) 华为 AirEngine 5773-21 踩坑记录</title><link>https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/</link><pubDate>Sat, 13 Dec 2025 16:00:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/</guid><description>&lt;h2 id="背景">背景
&lt;/h2>&lt;p>最近老家重建，家里需要重新布置无线网络。考虑到自建别墅面积比较大，单纯使用单个无线路由器覆盖不够，因此多个 AP 肯定是需要的。家里在装修的时候就每个房间吊顶中有留超六类线缆，这种情况对 AP 来说是非常友好的，因此决定使用企业级无线接入点（AP）来组建无线网络。由于华为系列的 AP 在国内使用量较大，闲鱼上非常好捡公司下架下来的二手设备，因此考虑华为系列的 AP。这些企业级 AP 并不需要担心二手问题，他们的质量是消费级产品无法比拟的，稳定性也是非常强。&lt;/p>
&lt;p>目前是 2025 年 12 月，虽然在国内 Wi-Fi 7 （802.11be）的 6 GHz 频段还没批准无法使用（损失不少性能），但是上一下 Wi-Fi 7 追一下新也没什么问题。华为 Wi-Fi 7 AP 是 AirEngine xx7x 产品，我闲鱼花了 500 多，激情下单了一台 AirEngine 5773-21 （有一个 2.5 Gbe PoE 口）。先买一台测测，为之后全屋部署积累经验，如果好用，再给每个房间买一台（后续更新）。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap.jpg"
width="4032"
height="3024"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_b14f786812e45791.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_3795bb5d434c54b0.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_c37647f0c15c6bfa.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_659d44c48271545c.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_9696bbcf2a4c39f0.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_2718ba35d7e51169.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_36fb61f00091e8d4.webp 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_2e5881c9f9792c.jpg 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_73dbca4a0db777e9.webp 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_7b88ec1defe6fdad.jpg 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_69d650df99db1704.webp 4032w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap_hu_4e74f57c21f23815.jpg 4032w"
loading="lazy"
alt="AirEngine 5773-21"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>回来一查，这设备保修到 2028 年 3 月，现在才 2025 年 12 月，还有快 3 年的保修期，很新啊。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage.png"
width="2350"
height="290"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_16571f7808de217e.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_6d25512b31bbbe5a.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_5871f286a08dda3c.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_9eae67b12ffa303c.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_c4777a0cc3d924e5.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_c17a9e9a7484cb5d.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_8da3523ba2246189.webp 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_281f5bc066139400.jpg 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_676bcd69687ad1a0.webp 2350w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/coverage_hu_79438dc774df4d75.jpg 2350w"
loading="lazy"
alt="AirEngine 5773-21 保修"
class="gallery-image"
data-flex-grow="810"
data-flex-basis="1944px"
>
&lt;/p>
&lt;p>我目前的打算是不购买单独的 AC 控制器（AC 用于统一管理其他 AP ），因为我没有几十上百个 AP 需要管理。对于只需管理十几台 AP 来说，AC 控制器的成本和复杂度都不划算。因此打算使用华为 AP 自带的 Leader AP 模式组网（按道理华为的 AirEngine 系列大部分都是支持的，但是对于我买的 AirEngine 5773-21 来说，这里是个大坑，后面会讲）。这种模式下，Leader AP 可以担任 AC 的角色，统一管理其他 FIT AP ，每个 Leader AP 具体能管理几台 FIT AP 可以上华为 Info-Finder 上查询（基本上都大于 16 台）。&lt;/p>
&lt;p>（组网方案后续更新，包括多 AP 的组网方案， VLAN 配置 IOT 设备隔离等）&lt;/p>
&lt;h2 id="首次启动">首次启动
&lt;/h2>&lt;p>插上网线（注意，如果使用 PoE 的话需要满足 802.3at ，否则可能降速，或者使用 DC 供电）&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in.jpg"
width="4032"
height="3024"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_967afc15f6fb39a0.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_7f537c013a7c9346.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_aa971d6c1a58150a.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_639b1745ec70b782.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_ec8d627d850ac998.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_9fdaa233479e885a.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_79c5eac46e7cea7c.webp 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_f326ec59d3d0b998.jpg 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_c60915b602be3238.webp 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_bda31642a42e317b.jpg 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_83f57340592ca1d2.webp 4032w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/ap-plugged-in_hu_66b4792641cfb88f.jpg 4032w"
loading="lazy"
alt="AP 插电"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>注意，开机过程中状态灯常亮，等到状态灯快速闪烁说明开机完成等待配置。&lt;/p>
&lt;p>现在华为的 AP 默认会用 DHCP 获取 IP 地址，因此只需根据 AP 的 MAC 地址（在 AP 背后会写）在 DHCP 服务器上找到对应的 IP 地址，就可以知道 AP 目前的 IP 地址了&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp.png"
width="1890"
height="166"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_d8241ea7fd04c8dd.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_997cdf3557a21165.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_e2cef2b70d0ac5b1.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_1f18538921ecada1.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_429b6c90817b4f2f.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_de3dab1d0515d26f.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_f1a8027132842368.webp 1890w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/dhcp_hu_4550bfe3cc46d5c2.jpg 1890w"
loading="lazy"
alt="DHCP"
class="gallery-image"
data-flex-grow="1138"
data-flex-basis="2732px"
>
&lt;/p>
&lt;p>然后浏览器打开该 IP 即可登录 Web 管理界面（并不）。&lt;/p>
&lt;h2 id="踩坑">踩坑
&lt;/h2>&lt;p>然后浏览器打开该 IP 即可登录 Web 管理界面（吗？？？？），我发现并不能。&lt;/p>
&lt;p>按道理华为 Wi-Fi 6 系列（比如 AirEngine 5761 ）都是可以直接登录 Web 管理页面的，Wi-Fi 7 更新不应该不行，而且官方 AirEngine 5773-21 的&lt;a class="link" href="https://e.huawei.com/cn/material/enterprise/5d5164cf207149b0b210988b1bb7996d" target="_blank" rel="noopener"
>彩页&lt;/a>中也说可以用做 FAT AP / Leader AP ，怎么回事？&lt;/p>
&lt;p>我后面用 SSH 登录设备（后面会说怎么登录），没有说当前是 FIT 还是 FAT 模式（老的 AP 会在 SSH 登录的时候就提示）。而且这个命令行看的我一脸懵，老的 Wi-Fi 6 AP 都是直接 &lt;code>system-view&lt;/code> 就能配置，然后命令也是传统的那一套。这新的 AP 一上来就是个 &lt;code>MDCLI&amp;gt;&lt;/code> 提示符（一看就是新搞的东西，里面的命令也完全变了，我根本没时间去学习这些新命令。（后面才知道，必须升级系统后，用 &lt;code>switch cli&lt;/code> 从 MC-CLI 切换到传统 CLI 模式，才能用老的命令行方式配置，但是建议还是用新的 MD-CLI ，因为传统 CLI 里面基本没啥功能了）&lt;/p>
&lt;p>看了一圈文档，说可以输入 &lt;code>edit-config&lt;/code> 修改配置，然后发现提示没有权限。WTF？我管理员还能没权限？后面才知道，原来没有权限其实代表当前 AP 在 FIT 模式下（这报错给用户带来多少困扰），必须切换到 FAT 模式才有权限修改配置。那么问题来了，我都没权限做任何配置，我怎么切换到 FAT 模式（况且我也没找到切换 FAT 模式的命令）？&lt;/p>
&lt;p>后面折腾一圈才发现（各种根据文档中的蛛丝马迹去猜），是我当前固件版本太老了（&lt;code>V600R023C10&lt;/code>），华为并没有在早期固件中实现 FAT AP / Leader AP 功能（设备都发布了，功能还没做好是吧），必须升级到 &lt;code>V600R024C10&lt;/code> 及以上版本才有该功能，写文章时最新版本 是 &lt;code>V600R025C00&lt;/code> 。OK，问题找到，接下来就是升级固件了。&lt;/p>
&lt;p>但是华为是出了名的不给资料，比如我想下载新版本固件，就算我注册了账号、也绑定了 AP 的序列号、也给了华为公司名称 + 设备序列号
也审核 &lt;strong>通过&lt;/strong> 了 AirEngine 5773-21 的资料和软件下载权限，但是你还是下不到 AirEngine 5773-21 的固件，华为官网上根本 &lt;strong>没有&lt;/strong> 提供任何一个版本固件的下载链接（截至 2025 年 12 月），你只能下到补丁（这还是从其他 AP 找的补丁，刚好能用到 AirEngine 5773-21 上）。&lt;/p>
&lt;p>最后我只能在闲鱼花钱上找有权限的人代下的固件，版本号 &lt;code>V600R024C10&lt;/code> ，也就是第一版支持 FAT AP / Leader AP 功能的固件。本来我想下载当前最新的 &lt;code>V600R025C00&lt;/code> ，但是我没找到人能下载这个版本，遂放弃， &lt;code>V600R024C10&lt;/code> 也不是不能用。&lt;/p>
&lt;h2 id="首次设备设置">首次设备设置
&lt;/h2>&lt;blockquote>
&lt;p>可以按住设备旁的 Default 按钮数十秒来重置设备。观察指示灯，变成常亮表示成功。重置成功后会重启，开机过程中状态灯常亮，等到状态灯快速闪烁说明开机完成等待配置。&lt;/p>&lt;/blockquote>
&lt;p>你需要 SSH 上去 &lt;code>ssh admin@&amp;lt;AP_IP_ADDRESS&amp;gt;&lt;/code> ，默认密码是 &lt;code>admin@huawei.com&lt;/code> 。首次登录会提示你修改密码，按照提示修改后会登出，下次使用新密码再登录即可。&lt;/p>
&lt;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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; display system/system-info/software-name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34; # 当前版本
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>我目前的版本是 &lt;code>V600R023C10SPC200&lt;/code> ，也就是 &lt;code>V600R023C10&lt;/code> （后面的 &lt;code>SPC200&lt;/code> 是补丁），需要升级到 &lt;code>V600R024C10&lt;/code> 及以上版本才有 FAT AP / Leader AP 功能。&lt;/p>
&lt;h2 id="固件升级">固件升级
&lt;/h2>&lt;p>首先需要找台机器开启 FTP 服务端，设置好用户名密码，打开读写权限（macOS App Store 中有个 QuickFTP 还是很好用的），到时候 AP 需要通过 FTP 上传和下载固件。&lt;/p>
&lt;p>然后你需要有一个 &lt;code>V600R024C10&lt;/code> 及以上版本的固件文件，放到 FTP 的根目录下面。&lt;/p>
&lt;p>需要使用的固件名称通常长这样，两者都行：&lt;/p>
&lt;ul>
&lt;li>&lt;code>AirEngineX773_V600R024C10.cc&lt;/code> （不带补丁的基础固件）&lt;/li>
&lt;li>&lt;code>AirEngineX773_V600R024C10SPC100.cc&lt;/code> （带补丁的完整固件）&lt;/li>
&lt;/ul>
&lt;p>其中：&lt;/p>
&lt;ul>
&lt;li>&lt;code>AirEngineX773&lt;/code>：设备型号，比如 AirEngine 5773&lt;/li>
&lt;li>&lt;code>V600R024C10&lt;/code>：版本号，R 或者 C 之后的数字越大，版本越新。&lt;/li>
&lt;li>&lt;code>SPC100&lt;/code>：补丁号，可选。注意，不要下成 &lt;code>SPH&lt;/code> 的热补丁了，补丁通常长这样 &lt;code>AirEngineX773_V600R024C10SPH150.pat&lt;/code>（有 &lt;code>SPH&lt;/code> 字样，且结尾是 &lt;code>.pat&lt;/code> ），需要完整的固件（&lt;code>.cc&lt;/code> 结尾）。&lt;/li>
&lt;/ul>
&lt;h3 id="备份当前固件">备份当前固件
&lt;/h3>&lt;p>SSH 登录设备操作&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;/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">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; command-type put
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; local-file-name AirEngineX773_V600R023C10SPC200.cc # 文件名与当前 AP 版本一致，查询：display system/system-info/software-name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; remote-file-name AirEngineX773_V600R023C10SPC200.cc # 与上面保持一致即可
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; server-ipv4-address 192.168.213.54 # FTP 服务器 IP 地址
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; server-port 2121 # FTP 服务器端口
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; user-name xxxx # FTP 用户名
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; password # FTP 密码
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Enter password:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Confirm password:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(x)admin@HUAWEI]/ftpc-transfer-file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; emit # 提交上传任务
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;huawei-ftpc:transfer-id&amp;#34;: 3 # 可以用这个 ID 查看任务状态
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; display ftpc/transfer-tasks # 检查是否备份成功
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;transfer-task&amp;#34;: [
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;transfer-id&amp;#34;: 3,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;command-type&amp;#34;: &amp;#34;put&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;server-address&amp;#34;: &amp;#34;192.168.213.54&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;server-port&amp;#34;: 2121,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;local-file-name&amp;#34;: &amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;remote-file-name&amp;#34;: &amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;status&amp;#34;: &amp;#34;succeeded&amp;#34;, # 表示成功
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;percentage&amp;#34;: 100
&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>&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>你应该可以在 FTP 服务器上看到备份下来的固件文件了。&lt;/p>
&lt;h3 id="下载新固件">下载新固件
&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;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;/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="p">[&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&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="p">[(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">software&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">directory&lt;/span> &lt;span class="n">AirEngineX773_V600R024C10SPC100&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cc&lt;/span> &lt;span class="c1"># 新固件文件名，注意要和 FTP 服务器上一致&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">server&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ip&lt;/span> &lt;span class="mf">192.168&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">213.54&lt;/span> &lt;span class="c1"># FTP 服务器 IP 地址&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">server&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">port&lt;/span> &lt;span class="mi">2121&lt;/span> &lt;span class="c1"># FTP 服务器端口&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">protocol&lt;/span> &lt;span class="n">ftp&lt;/span> &lt;span class="c1"># 传输协议&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="n">xxxx&lt;/span> &lt;span class="c1"># FTP 用户名&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">password&lt;/span> &lt;span class="c1"># FTP 密码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Enter&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Confirm&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="p">:&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">emit&lt;/span> &lt;span class="c1"># 提交下载任务&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upgrade&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">display&lt;/span> &lt;span class="n">software&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">download&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">/&lt;/span> &lt;span class="c1"># 查看下载状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;file-name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;AirEngineX773_V600R024C10SPC100.cc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;succeeded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 表示下载成功&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;percentage&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&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="p">[&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">display&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">operation&lt;/span>&lt;span class="o">/&lt;/span> &lt;span class="c1"># 查看文件列表，确认新固件在设备上&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;dir&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;file-name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;AirEngineX773_V600R024C10SPC100.cc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 新固件文件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;dir-name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;backup:/&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;attribute&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;-rw-&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;modify-time&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2024-11-12T12:05:04Z&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">52394852&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="设置新固件为启动固件">设置新固件为启动固件
&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;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;/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">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; startup-by-mode name AirEngineX773_V600R024C10SPC100.cc # 设置启动固件，注意文件名要和上面一致
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; display cfg/startup-infos
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;startup-info&amp;#34;: [
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;position&amp;#34;: &amp;#34;0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;configed-system-software&amp;#34;: &amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;current-system-software&amp;#34;: &amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;next-system-software&amp;#34;: &amp;#34;AirEngineX773_V600R024C10SPC100.cc&amp;#34;, # 确认新固件已设置为下次启动固件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;current-cfg-file&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;next-cfg-file&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;current-patch-file&amp;#34;: &amp;#34;NULL&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;next-patch-file&amp;#34;: &amp;#34;NULL&amp;#34;
&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>&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>注意，要是你之前安装了 patch ，可能会导致新系统安装不上，例如&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; display cfg/startup-infos
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;startup-info&amp;#34;: [
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;position&amp;#34;: &amp;#34;0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;configed-system-software&amp;#34;: &amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34;, # 是的，华为拼错单词了
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;current-system-software&amp;#34;: &amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;next-system-software&amp;#34;: &amp;#34;AirEngineX773_V600R023C10SPC200.cc&amp;#34;, # 发现还是老的版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;current-cfg-file&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;next-cfg-file&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;current-patch-file&amp;#34;: &amp;#34;AirEngineX773_V600R023SPH151.pat&amp;#34;, # 已安装 patch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;next-patch-file&amp;#34;: &amp;#34;AirEngineX773_V600R023SPH151.pat&amp;#34;
&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>&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>这时候你需要把 patch 给清掉：&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">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; delete-patch delete-type all
&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>然后再设置启动固件即可。&lt;/p>
&lt;h3 id="重启设备">重启设备
&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;/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">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; reboot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Warning: This operation will reboot the device.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Are you sure you want to continue? [Y(yes)/N(no)]:y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>等设备起来，浏览器访问设备 IP 地址，应该就可以看到 Web 管理界面了。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui.png"
width="3054"
height="1962"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_2dc4cf129c52d0de.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_3ffb09a143dfe7c.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_ac969082e605071e.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_5d4367033d6aa459.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_79b592d4c6005975.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_d1a2678628fd1de8.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_a759fa727b2062c5.webp 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_d504819ff47877e5.jpg 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_b7ee2d3e163c8fa8.webp 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_5d35b15e278adda8.jpg 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_17ead509063531c7.webp 3054w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fit-webui_hu_bea6f46969aebeeb.jpg 3054w"
loading="lazy"
alt="FIT 模式 Web 管理界面"
class="gallery-image"
data-flex-grow="155"
data-flex-basis="373px"
>
&lt;/p>
&lt;p>选择右上角 FIT 按钮，改成 FAT 模式即可使用 Leader AP 功能，重启后即可变成 Leader AP ，可以管理其他 FIT AP 了。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui.png"
width="3682"
height="2442"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_6fc4b20daee84ad3.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_1d6c9be7c7495cd0.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_728abe395b9542b8.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_50aceb1373a9216b.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_57c461a47cff8acd.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_92eee0f76b4d2a69.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_9a13584e537e0ec0.webp 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_a96509553461e7bd.jpg 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_5c505c5c3fd4443.webp 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_9ae3db4bee0b50c6.jpg 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_644252fa3fbe707e.webp 3682w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/fat-webui_hu_aeb84a12dfa0776c.jpg 3682w"
loading="lazy"
alt="FAT 模式 Web 管理界面"
class="gallery-image"
data-flex-grow="150"
data-flex-basis="361px"
>
&lt;/p>
&lt;h2 id="安装补丁patch">安装补丁（Patch）
&lt;/h2>&lt;p>补丁是在基础版本上修复 bug 或者增加小功能的。补丁一般靠自己注册完设备就能在官网下到，下载补丁的时候，注意产品型号不需要选择 AirEngine 5773-21 ，因为太新了，华为官网上甚至没有这个型号。直接选择全部，只要版本对的上即可，比如我当前基础版本是 &lt;code>V600R024C10&lt;/code> ，那么补丁就需要选择 &lt;code>V600R024C10SPH181&lt;/code> 这种补丁，表示在 &lt;code>V600R024C10&lt;/code> 基础版本上打的热补丁 &lt;code>SPH181&lt;/code> ，数字越大表示补丁越新。补丁文件名通常长这样 &lt;code>AirEngineX773_V600R024C10SPH181.pat&lt;/code> ，注意是 &lt;code>.pat&lt;/code> 结尾。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch.png"
width="2958"
height="1202"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_12ba1e5edc78d891.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_ec90140dadee2d70.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_f853aa122e7b7c85.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_2af7fd32952da821.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_4568abf538e7a47c.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_7ab9a788df6717fb.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_3325f09ae9c671be.webp 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_75e7a3a906927408.jpg 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_2584ee31d5913ea3.webp 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_7f805f5d3e4ee90b.jpg 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_f4eef20388af5b87.webp 2958w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/download-patch_hu_34ea8adc52150cb2.jpg 2958w"
loading="lazy"
alt="下载补丁"
class="gallery-image"
data-flex-grow="246"
data-flex-basis="590px"
>
&lt;/p>
&lt;p>下载后，放到 FTP 服务器根目录下，然后 SSH 登录设备，执行以下命令安装补丁：&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;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&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="p">[&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&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="p">[(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">command&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">type&lt;/span> &lt;span class="n">get&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">local&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="n">AirEngineX773_V600R024C10SPH181&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pat&lt;/span> &lt;span class="c1"># 补丁文件名，注意和 FTP 服务器上一致&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">remote&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="n">AirEngineX773_V600R024C10SPH181&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pat&lt;/span> &lt;span class="c1"># 与上面保持一致即可&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">server&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ipv4&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">address&lt;/span> &lt;span class="mf">192.168&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">213.54&lt;/span> &lt;span class="c1"># FTP 服务器 IP 地址&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">server&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">port&lt;/span> &lt;span class="mi">2121&lt;/span> &lt;span class="c1"># FTP 服务器端口&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="n">xxx&lt;/span> &lt;span class="c1"># FTP 用户名&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">password&lt;/span> &lt;span class="c1"># FTP 密码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Enter&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Confirm&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="p">:&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="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ftpc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">emit&lt;/span> &lt;span class="c1"># 提交下载任务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;huawei-ftpc:transfer-id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&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="p">[&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">display&lt;/span> &lt;span class="n">ftpc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">transfer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="o">/&lt;/span> &lt;span class="c1"># 查看任务状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;transfer-task&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;transfer-id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;command-type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;get&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;server-address&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;192.168.213.54&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;server-port&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2121&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;local-file-name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;AirEngineX773_V600R024C10SPH181.pat&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;remote-file-name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;AirEngineX773_V600R024C10SPH181.pat&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;succeeded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 表示成功&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;percentage&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&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="p">[&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="nb">load&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">patch&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="n">AirEngineX773_V600R024C10SPH181&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pat&lt;/span> &lt;span class="nb">load&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">type&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="c1"># 安装补丁，注意文件名与需要安装的补丁一致&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="p">[&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">display&lt;/span> &lt;span class="n">patch&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">operation&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">schedules&lt;/span> &lt;span class="c1"># 查看安装进度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;operation-schedule&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;phase&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;load-patch&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;successful&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;schedule&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span> &lt;span class="c1"># 等待进度 100%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;phase&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;delete-patch&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;not-started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;schedule&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;phase&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;startup-next-patch&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;not-started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;schedule&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;phase&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;reset-startup-patch&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;not-started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;schedule&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&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="p">[&lt;/span>&lt;span class="n">admin&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">HUAWEI&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MDCLI&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">display&lt;/span> &lt;span class="n">patch&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">patch&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">infos&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;patch-info&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;AirEngineX773_V600R024C10SPH181.pat&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 验证补丁安装成功&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;V600R024C10SPH181&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;state&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;running&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;runtime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2025-12-13T21:39:48+08:00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;path&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;operations&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;operation&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;position&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;position-type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;MPU&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;upgrade-mode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;reset-board&amp;#34;&lt;/span> &lt;span class="c1"># 某些补丁需要重启设备才能生效&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>你也可以在 Web 管理界面上查看补丁是否安装成功。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui.png"
width="3022"
height="358"
srcset="https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_8ddac104a9193297.webp 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_c4ab380c254e0716.jpg 480w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_4f92d18e8bc4b92a.webp 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_1728784cd89885af.jpg 1024w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_d7d40e13c260db24.webp 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_e1e784ff870a4e1d.jpg 1536w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_9eaab813304a2cf1.webp 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_b99443bb15eb4386.jpg 2048w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_fbeb485cb3d1e5a3.webp 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_86721418d6a3bbe6.jpg 2560w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_a5859c1dd4aabe21.webp 3022w, https://charlie0129.github.io/blog/p/huawei-airengine-5773-gotchas/images/patch-webui_hu_b1424983fba2cf30.jpg 3022w"
loading="lazy"
alt="补丁安装成功"
class="gallery-image"
data-flex-grow="844"
data-flex-basis="2025px"
>
&lt;/p>
&lt;h2 id="bonus-启用-ipv6">Bonus: 启用 IPv6
&lt;/h2>&lt;p>我发现 AP 下面的设备获取不到 IPv6 地址（我内网中 IPv6 配置都是正确的），基本上肯定是在 AP 导致的 IPv6 地址无法下发。&lt;/p>
&lt;p>后面了解华为 AP 默认不开 IPv6 报文转发。原因是：华为认为 IPv4 是主流，在 IPv4 网络中，如果存在较多 IPv6 协议报文，会影响无线网络性能，也会损耗设备的 CPU 处理能力。因此在纯 IPv4 网络中，可以通过不处理 IPv6 无线报文来提高 IPv4 网络性能。&lt;/p>
&lt;p>不过，我就是要用 IPv6 ，因此要去把 WLAN 处理 IPv6 报文的功能打开（ Web UI 没有，要命令行开），SSH 登录设备，执行以下命令：&lt;/p>
&lt;blockquote>
&lt;p>官方文档里面其实有这个，但是我找半天没找到，在：“参考-MD-CLI配置参考-WLAN配置-WLAN用户管理” 里面。不是，你把 IPv6 相关配置放在“用户管理”里面？这跟用户管理有啥关系？&lt;/p>&lt;/blockquote>
&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; edit-config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[(gl)admin@HUAWEI]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; wlan-sta-access
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[(gl)admin@HUAWEI]/wlan-sta-access
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; sta-ipv6-switch true
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[*(gl)admin@HUAWEI]/wlan-sta-access
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MDCLI&amp;gt; commit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>之后你的设备应该就能正确收发 IPv6 报文了。&lt;/p>
&lt;h2 id="结语">结语
&lt;/h2>&lt;p>这告诉我们， AirEngine 5773-21 这种 Wi-Fi 7 新设备，还是太新了，所有的坑都得自己踩。与 Wi-Fi 6 的设备比如 AirEngine 5761 相比网上大把教程还是差太远了。不过也算是积累了一些经验，后续再买多台 AP 的时候就不会再踩这些坑了。&lt;/p>
&lt;p>这次单台 AP 就放北京用了，年底回老家再配置多 AP 组网方案。&lt;/p></description></item><item><title>Set Up ZeroTier Moon on Non-Standard Ports</title><link>https://charlie0129.github.io/blog/p/set-up-zerotier-moon-on-non-standard-ports/</link><pubDate>Thu, 27 Nov 2025 10:20:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/set-up-zerotier-moon-on-non-standard-ports/</guid><description>&lt;p>To install ZeroTier, one would typically allow port 9993/UDP on their firewall. However, in certain scenarios, you may need to run a ZeroTier moon on non-standard ports due to network restrictions (e.g. behind NAT) or conflicts with other services. This guide will walk you through the steps to set up a ZeroTier moon on non-standard ports.&lt;/p>
&lt;h2 id="setup-relays-moon-nodes-on-non-standard-ports">Setup Relays (Moon Nodes) on Non-Standard Ports
&lt;/h2>&lt;p>Install ZeroTier like usual:&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">curl -s https://install.zerotier.com &lt;span class="p">|&lt;/span> sudo bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># and join a network (if your moon also acts as a client)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zerotier-cli join &amp;lt;network_id&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Setup moon:&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">zerotier-idtool initmoon /var/lib/zerotier-one/identity.public &amp;gt;&amp;gt;/var/lib/zerotier-one/moon.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Here is the important part: edit the &lt;code>moon.json&lt;/code> file to specify the desired non-standard ports. Open the file with your preferred text editor:&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">vim /var/lib/zerotier-one/moon.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Modify the &lt;code>stableEndpoints&lt;/code> section to include your public IP address along with the desired non-standard port. If you are behind a NAT, use your router&amp;rsquo;s public IP address and forward the same port from your server to your router. The format should be &lt;code>IP_ADDRESS/PORT&lt;/code>.&lt;/p>
&lt;p>For example, if you want to use port &lt;code>14999&lt;/code>, change the line to:&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">&amp;#34;stableEndpoints&amp;#34;: [&amp;#34;xxx.xxx.xxx.xxx/14999&amp;#34;]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Generate the moon configuration (this is what clients will use to connect to your moon):&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">zerotier-idtool genmoon /var/lib/zerotier-one/moon.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You should have one file that looks like &lt;code>*.moon&lt;/code> in your current dir. Move the generated moon file to the ZeroTier directory:&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">mkdir -p /var/lib/zerotier-one/moons.d/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mv *.moon /var/lib/zerotier-one/moons.d/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If your moon also acts as a client, change the client configuration to use the non-standard port. Edit &lt;code>/var/lib/zerotier-one/local.conf&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;settings&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;primaryPort&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">14999&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Restart the ZeroTier service to apply the changes:&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 zerotier-one
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="setup-clients-leaf-nodes">Setup Clients (Leaf Nodes)
&lt;/h2>&lt;p>On the client side, you have to change the default port as well. Yes, the client&amp;rsquo;s default port (9993) must match the moon&amp;rsquo;s port. Otherwise, they won&amp;rsquo;t be able to communicate in my tests.&lt;/p>
&lt;p>After you installed ZeroTier and joined the network, edit &lt;code>/var/lib/zerotier-one/local.conf&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;settings&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;primaryPort&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">14999&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Copy &lt;code>/var/lib/zerotier-one/moons.d/*.moon&lt;/code> from the moon server to the client machine&amp;rsquo;s &lt;code>/var/lib/zerotier-one/moons.d/&lt;/code> directory, so the client knows about the moon:&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">scp &lt;span class="s2">&amp;#34;user@moon-server:/var/lib/zerotier-one/moons.d/*.moon&amp;#34;&lt;/span> /var/lib/zerotier-one/moons.d/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Restart the ZeroTier service on the client:&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 zerotier-one
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="verification">Verification
&lt;/h2>&lt;p>You can verify that the moon is functioning correctly by checking &lt;code>zerotier-cli&lt;/code> command on the client to see if it can connect to the moon.&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">zerotier-cli peers
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You should see an entry for your moon with the correct non-standard port, similar to the example below (xxx.xxx.xxx.xxx/14999):&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">zerotier-cli peers
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">200 peers
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;ztaddr&amp;gt; &amp;lt;ver&amp;gt; &amp;lt;role&amp;gt; &amp;lt;lat&amp;gt; &amp;lt;link&amp;gt; &amp;lt;lastTX&amp;gt; &amp;lt;lastRX&amp;gt; &amp;lt;path&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">35c192ce9b 1.15.3 LEAF 287 DIRECT 11575 11575 2001:19f0:6001:2c59:beef:3d:6767:df71/21006
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3cdfac522d 1.16.0 MOON 66 DIRECT 3660 3660 xxx.xxx.xxx.xxx/14999
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">778cde7190 - PLANET 287 DIRECT 44090 43802 2605:9880:400:c3:254:f2bc:a1f7:19/9993
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cafe04eba9 - PLANET 287 DIRECT 44090 43802 84.17.53.155/9993
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cafe80ed74 - PLANET 261 DIRECT 269315 43837 2a02:6ea0:c87f::1/9993
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cafefd6717 - PLANET 246 DIRECT 299345 43845 2a02:6ea0:d368::9993/9993
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If your &lt;code>&amp;lt;lat&amp;gt;&lt;/code> is &lt;code>-1&lt;/code>, it means the client cannot reach the moon. Double-check your configuration and ensure that the specified ports are open and correctly forwarded if behind a NAT.&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>(Chinese-Only) 一个Linux用户的绿联NAS配置指南</title><link>https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/</link><pubDate>Sun, 27 Jul 2025 16:45:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/</guid><description>&lt;h2 id="启用-ssh">启用 SSH
&lt;/h2>&lt;p>没SSH就别想着用Terminal了，赶紧去开了。&lt;/p>
&lt;p>去绿联NAS的Web管理界面，控制面板 -&amp;gt; 终端机 -&amp;gt; SSH ，记得关闭自动关闭功能。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh.png"
width="1798"
height="1342"
srcset="https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_5b3a338ea3c2c53d.webp 480w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_7a1720dd3c850f6e.jpg 480w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_46af6012db3f5abf.webp 1024w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_c0438416237935ce.jpg 1024w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_764b3ee01c6f12f7.webp 1536w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_e7bfaebf190c46c1.jpg 1536w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_6ac0dadaea3417.webp 1798w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/ssh_hu_cfb02f11702bc96b.jpg 1798w"
loading="lazy"
alt="ssh"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="321px"
>
&lt;/p>
&lt;h2 id="配置-root-用户-ssh-登录">配置 root 用户 SSH 登录
&lt;/h2>&lt;p>用你的NAS用户名SSH到NAS上&lt;code>ssh yourname@nas-ip&lt;/code>&lt;/p>
&lt;p>你需要先有一个SSH密钥对，如果你还没有，可以用 &lt;code>ssh-keygen -t rsa&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;/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"># 切root&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo su
&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"># 打开 root SSH 登录（仅允许密钥登录）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 注意，别去改 /etc/ssh/sshd_config ，因为绿联NAS的系统会保护这个文件，改了重启就失效了。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;PermitRootLogin prohibit-password&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/ssh/sshd_config.d/A0-root-login.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">&lt;span class="c1"># 应用修改&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl restart ssh
&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"># 将你的公钥添加到 /root/.ssh/authorized_keys 中&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p /root/.ssh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod &lt;span class="m">700&lt;/span> /root/.ssh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;your-public-key&amp;#39;&lt;/span> &amp;gt;&amp;gt; /root/.ssh/authorized_keys
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod &lt;span class="m">600&lt;/span> /root/.ssh/authorized_keys
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>配置你本机的 &lt;code>~/.ssh/config&lt;/code> 文件使用对应的密钥登录NAS：&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">Host nas
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HostName nas-ip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> User root
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> IdentityFile ~/.ssh/&amp;lt;private-key&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="安装包管理器-opkg">安装包管理器 Opkg
&lt;/h2>&lt;p>那么有人会问，你为什么要装包管理器，你怎么不用自带的 apt？UGOS Pro不是基于Debian的么？其实最主要的原因是避免干扰内置预装的package。我举个例子，你想安装git，好，那你得apt update吧，update完拉过来了最新的package index，然后刚好这个新的git依赖新版的libcurl，你装的时候给他一起升级了，但是绿联自带了不少预装的package，这些package可能是根据老版本libcurl编译的。正常的debian你upgrade一下就完事了，但是UGOS作为一个定制的Debian，不建议你这么做（其实要是你尝试去upgrade，你会发现不让你upgrade，绿联已经做了保护了，虽然你可以绕过就是）。&lt;/p>
&lt;p>OK，我们解释清楚了为什么不用apt。那么用什么呢？我的建议是一个轻量级给嵌入式设备用的包管理器，用OpenWRT的可能很熟悉了，就是opkg。你想重量级一点，也可以用Homebrew/Linuxbrew（不过你得先装git才能装Homebrew，但是git又没有，现在还没包管理器也没得装git，循环依赖了属于是），也可以用Nix（我觉得Nix太重了，除非你真的需要Nix的特性，不然就算了）。&lt;/p>
&lt;p>这里用 Opkg 做演示。你需要用root用户SSH到NAS上&lt;code>ssh root@nas-ip&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;/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"># 下载 opkg 安装脚本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wget https://mirrors.nju.edu.cn/entware/x64-k3.2/installer/generic.sh -O opkg-install.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 换源 1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s|http://bin.entware.net|https://mirrors.nju.edu.cn/entware|g&amp;#39;&lt;/span> opkg-install.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 换源 2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s|-O /opt/etc/opkg.conf|-O /opt/etc/opkg.conf \&amp;amp;\&amp;amp; sed -i &amp;#34;s,http://bin.entware.net,https://mirrors.nju.edu.cn/entware,g&amp;#34; /opt/etc/opkg.conf|g&amp;#39;&lt;/span> opkg-install.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 执行 opkg 安装脚本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/bin/sh opkg-install.sh
&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"># 临时设置 PATH ，使得 opkg 命令可用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>/opt/bin:/opt/sbin:/opt/usr/bin:&lt;span class="nv">$PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 设置 PATH ，使得 opkg 命令可用。如果你用 zsh，记得改成 ~/.zshrc 。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或者如果你用我的 dotfiles，现在可以先不做这一步，你可以看后续我配置 zsh 的时候怎么做的。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=/opt/bin:/opt/sbin:/opt/usr/bin:$PATH&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.bashrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="安装常用package">安装常用Package
&lt;/h2>&lt;p>这时有包管理器了，而且是与NAS系统隔离的，你可以随意安装了。以下是一些常用的包：&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"># 注意 vim 需要安装两个包，vim-full 和 vim-runtime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">opkg install vim-full vim-runtime
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 终于能装 git 了，注意需要 git-http&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">opkg install git git-http
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 其他常见工具&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">opkg install htop sysstat zsh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="配置-zsh">配置 ZSH
&lt;/h2>&lt;p>如果你想要一个更好的终端体验，建议使用 ZSH 和比较好的配置。以下是使用我的 dotfiles 来配置 ZSH 的步骤：&lt;/p>
&lt;blockquote>
&lt;p>注意，你需要在你以后会经常使用的用户中配置 ZSH 。
我这里都是用的 root 用户，我对 Linux 比较熟悉了所以我并不怕搞坏东西。
如果你不熟悉 Linux，建议切换回自己用户配置 ZSH 。
如果找不到 git ，记得参考 opkg 安装中最后一步重新设置 PATH 。&lt;/p>&lt;/blockquote>
&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"># 先设置代理，等会要从 GitHub 上下载文件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">https_proxy&lt;/span>&lt;span class="o">=&lt;/span>http://&amp;lt;your-proxy-ip&amp;gt;:&amp;lt;your-proxy-port&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 克隆我的 dotfiles 仓库&lt;/span>
&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">git clone 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">zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 询问 Do you need to use a proxy [y/n] 的时候选择 n （因为前面 set 过 proxy 了）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 询问 Change login shell of root to /opt/bin/zsh 的时候选择 y （不然呢）&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>将 Opkg 的 bin 和 sbin 目录添加到 ZSH 的 PATH 中：&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">vim ~/dotfiles/env/custom.sh
&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;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"># This list is inserted before PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PATH_BEFORE&lt;/span>&lt;span class="o">=(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># custom bin in this repo, i.e. bin/custom&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$HOME&lt;/span>/dotfiles/bin/custom
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Opkg bin and sbin directories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /opt/bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /opt/sbin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /opt/usr/bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&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>效果&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zsh.png"
width="1492"
height="1106"
srcset="https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zsh_hu_2bf218c09156f946.webp 480w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zsh_hu_b74e010a3aab6115.jpg 480w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zsh_hu_1a8d7b34d296b753.webp 1024w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zsh_hu_ecb9491cedcecb0f.jpg 1024w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zsh_hu_65de481189b51b2e.webp 1492w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zsh_hu_b06cafb5d3ec1bf7.jpg 1492w"
loading="lazy"
alt="zsh"
class="gallery-image"
data-flex-grow="134"
data-flex-basis="323px"
>
&lt;/p>
&lt;h2 id="优化-zram">优化 Zram
&lt;/h2>&lt;blockquote>
&lt;p>需要使用 root 用户&lt;/p>&lt;/blockquote>
&lt;p>绿联默认的 zram 不够激进，关了它。我们要用 zstd 算法和一半的内存来做zram，榨干内存。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff.png"
width="1798"
height="1342"
srcset="https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_a7006aae15a47453.webp 480w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_698e2d8f31a6dfbd.jpg 480w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_ff1ee2aa8b0ae20f.webp 1024w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_65199f43810a186e.jpg 1024w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_fd86fbfce8e0bf84.webp 1536w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_6c56c6d22fd5c94f.jpg 1536w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_412fbfd393b6764.webp 1798w, https://charlie0129.github.io/blog/p/ugreen-nas-terminal-setup/images/zramoff_hu_b93eb5ccbcf55b9e.jpg 1798w"
loading="lazy"
alt="zramoff"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="321px"
>
&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"># 先设置代理，等会要从 GitHub 上下载文件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">https_proxy&lt;/span>&lt;span class="o">=&lt;/span>http://&amp;lt;your-proxy-ip&amp;gt;:&amp;lt;your-proxy-port&amp;gt;
&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/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>配置使用 zstd 算法（因为 Debian 13 里面默认 zram 不给 lzo-rle 算法了，lz4 的压缩率又不行，所以用 zstd ）：&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/_zram_algorithm=.*/_zram_algorithm=&amp;#34;zstd&amp;#34;/g&amp;#39;&lt;/span> /etc/default/zram-swap
&lt;/span>&lt;/span>&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>为 zram 配置合适的 sysctl ，较大的 &lt;code>swappiness&lt;/code> 值可以不活跃的页面更快地被交换出去，让内存留给更有用的页面。因为 sysctl.conf 里面绿联已经设置了 vm.swappiness 如果放在 &lt;code>/etc/sysctl.d/&lt;/code> 里面会被覆盖掉，所以直接放在 sysctl.conf 里面。&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="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;# ZRAM BEGIN&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;vm.swappiness = 180&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;vm.watermark_boost_factor = 0&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;vm.watermark_scale_factor = 125&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;vm.page-cluster = 0&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;# ZRAM END&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/sysctl.conf
&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>能看到内存大小 1.5 倍的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;/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> swapon
&lt;/span>&lt;/span>&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 93.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="安装-docker">安装 Docker
&lt;/h2>&lt;p>先去应用中心安装 Docker 。默认绿联的Docker 配置比较烂，而且还会在根目录下留下一个 &lt;code>/daemon.json&lt;/code> 的文件夹（WTF？），一看就是绿联的安装脚本写错了。&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="c1"># 删除错误的 daemon.json 文件夹&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rmdir /daemon.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>将以下json写入 &lt;code>/etc/docker/daemon.json&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-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;log-opts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;max-size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1m&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;experimental&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;metrics-addr&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.0.0.0:8132&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;data-root&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;your-data-root&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;registry-mirrors&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;https://xxxxxx.com/dockerhub/&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>log-opts&lt;/code>：限制日志大小，防止日志占满磁盘。&lt;/li>
&lt;li>&lt;code>experimental&lt;/code>：启用实验性功能。&lt;/li>
&lt;li>&lt;code>metrics-addr&lt;/code>：设置可观测端口。&lt;/li>
&lt;li>&lt;code>data-root&lt;/code>：设置 Docker 数据目录，建议查看你之前的 &lt;code>/etc/docker/daemon.json&lt;/code> 中的配置，不要动。&lt;/li>
&lt;li>&lt;code>registry-mirrors&lt;/code>：设置 Docker 镜像加速器，建议使用国内的镜像源。&lt;/li>
&lt;/ul>
&lt;h2 id="可观测配置">可观测配置
&lt;/h2>&lt;ul>
&lt;li>Grafana: 用于可视化和监控。&lt;/li>
&lt;li>Victoria Metrics: 用于指标存储与查询。&lt;/li>
&lt;li>Alloy: 用于容器日志采集。&lt;/li>
&lt;li>Cadvisor: 用于容器指标采集。&lt;/li>
&lt;li>Loki: 用于日志存储与查询。&lt;/li>
&lt;li>Node Exporter: 用于主机指标采集。&lt;/li>
&lt;li>Smartctl Exporter: 用于硬盘SMART指标采集。&lt;/li>
&lt;li>Intel PCM: 用于CPU性能监控。&lt;/li>
&lt;/ul>
&lt;p>效果：TODO&lt;/p>
&lt;p>参考 Docker Compose （镜像版本可以适当升级，记得更换volume中数据存储位置）：&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;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;span class="lnt">139
&lt;/span>&lt;span class="lnt">140
&lt;/span>&lt;span class="lnt">141
&lt;/span>&lt;span class="lnt">142
&lt;/span>&lt;span class="lnt">143
&lt;/span>&lt;span class="lnt">144
&lt;/span>&lt;span class="lnt">145
&lt;/span>&lt;span class="lnt">146
&lt;/span>&lt;span class="lnt">147
&lt;/span>&lt;span class="lnt">148
&lt;/span>&lt;span class="lnt">149
&lt;/span>&lt;span class="lnt">150
&lt;/span>&lt;span class="lnt">151
&lt;/span>&lt;span class="lnt">152
&lt;/span>&lt;span class="lnt">153
&lt;/span>&lt;span class="lnt">154
&lt;/span>&lt;span class="lnt">155
&lt;/span>&lt;span class="lnt">156
&lt;/span>&lt;span class="lnt">157
&lt;/span>&lt;span class="lnt">158
&lt;/span>&lt;span class="lnt">159
&lt;/span>&lt;span class="lnt">160
&lt;/span>&lt;span class="lnt">161
&lt;/span>&lt;span class="lnt">162
&lt;/span>&lt;span class="lnt">163
&lt;/span>&lt;span class="lnt">164
&lt;/span>&lt;span class="lnt">165
&lt;/span>&lt;span class="lnt">166
&lt;/span>&lt;span class="lnt">167
&lt;/span>&lt;span class="lnt">168
&lt;/span>&lt;span class="lnt">169
&lt;/span>&lt;span class="lnt">170
&lt;/span>&lt;span class="lnt">171
&lt;/span>&lt;span class="lnt">172
&lt;/span>&lt;span class="lnt">173
&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">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">o11y&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">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">grafana&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">grafana&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">grafana/grafana:12.1.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">1G&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">networks&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">o11y&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="c"># 我使用了 Traefik 作为反向代理，所以不需要暴露端口&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="c"># 如果你没有使用 Traefik，可以取消注释以下端口映射&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="c"># ports:&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="c"># - 3000:3000&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">healthcheck&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">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wget --no-verbose --tries=1 --spider http://localhost:3000/api/health&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">/volume2/docker/data/grafana:/var/lib/grafana&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">TZ&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Asia/Shanghai&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">GF_SERVER_ENABLE_GZIP&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="c"># 如果你使用 Traefik 作为反向代理，可以添加以下标签&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">labels&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;traefik.enable=true&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="s2">&amp;#34;traefik.http.routers.grafana.entrypoints=web&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="s2">&amp;#34;traefik.http.routers.grafana.rule=Host(`grafana.example.com`)&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="s2">&amp;#34;traefik.http.routers.grafana.service=grafana-secure&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="c"># - &amp;#34;traefik.http.routers.grafana.middlewares=grafana-https-redirect&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="c"># - &amp;#34;traefik.http.middlewares.grafana-https-redirect.redirectscheme.scheme=https&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="s2">&amp;#34;traefik.http.routers.grafana-secure.tls=true&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="s2">&amp;#34;traefik.http.routers.grafana-secure.tls.certresolver=cloudflare&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="s2">&amp;#34;traefik.http.routers.grafana-secure.entrypoints=websecure&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="s2">&amp;#34;traefik.http.routers.grafana-secure.rule=Host(`grafana.example.com`)&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="s2">&amp;#34;traefik.http.services.grafana-secure.loadbalancer.server.port=3000&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">vm&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">vm&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/victoria-metrics:v1.122.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">4G&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">networks&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">o11y&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="c"># ports:&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="c"># - 8428: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="nt">healthcheck&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">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wget --no-verbose --tries=1 --spider http://localhost:8428/ || exit 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">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">promscrape.config=/etc/victoriametrics/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">storageDataPath=/var/victoriametrics&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">retentionPeriod=20y&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">inmemoryDataFlushInterval=300s&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 减少写盘&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="s1">&amp;#39;host.docker.internal:host-gateway&amp;#39;&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="s1">&amp;#39;d48t:host-gateway&amp;#39;&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">./vm:/etc/victoriametrics&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Scrape 配置&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">/volume2/docker/data/vm:/var/victoriametrics&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">alloy&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">alloy&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">grafana/alloy:v1.10.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">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">networks&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">o11y&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="c"># ports:&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="c"># - 12345:12345&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">run&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">disable-reporting&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">server.http.listen-addr=0.0.0.0:12345&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">storage.path=/var/lib/alloy/data&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">/etc/alloy/config.alloy&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">/var/run/docker.sock:/var/run/docker.sock: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="l">/tmp/alloy:/var/lib/alloy/data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 这玩意巨能写，SSD会写爆，给他放内存里去。绿联的 /tmp 是内存盘。&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">./alloy/config.alloy:/etc/alloy/config.alloy&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">cadvisor&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">cadvisor&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">gcr.io/cadvisor/cadvisor:v0.53.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">256M&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">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="c"># ports:&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="c"># - 8080:8080&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">networks&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">o11y&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">/:/rootfs: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="l">/var/run:/var/run: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="l">/sys:/sys: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="l">/var/lib/docker/:/var/lib/docker: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="l">/dev/disk/:/dev/disk: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">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">devices&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">/dev/kmsg&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">store_container_labels=false&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">docker_only=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="l">housekeeping_interval=30s&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">loki&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">loki&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">grafana/loki:3.5.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">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">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">networks&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">o11y&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">ipv4_address&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;172.26.195.254&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 固定 IP，方便 Docker 的 Loki plugin 用（虽然我们这里用的 Alloy 其实并不需要）&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="c"># 如果你没有使用 Traefik 作为反向代理，可以取消注释以下端口映射。&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="c"># ports:&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="c"># - 3100:3100&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">4G&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="s2">&amp;#34;-config.file=/etc/loki/config.yml&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">./loki:/etc/loki: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="l">/volume2/docker/data/loki:/loki&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">node-exporter&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">node-exporter&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.9.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">256M&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="c"># ports:&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="c"># - 9100: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">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=172.17.0.1: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="c"># networks:&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="c"># - o11y&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="s1">&amp;#39;/:/host:ro,rslave&amp;#39;&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">smartctl-exporter&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">smartctl-exporter&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">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">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="c"># ports:&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="c"># - 192.168.91.1:9633:9633&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">networks&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">o11y&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">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">256M&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">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="c"># ports:&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="c"># - 192.168.91.1:9738:9738&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">networks&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">o11y&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">networks&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">o11y&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">o11y&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">driver&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bridge&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">ipam&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">config&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">subnet&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;172.26.195.0/24&amp;#34;&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>参考 Victoria Metrics 的 Scrape 配置：&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;/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="c"># ./vm/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="nt">global&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">scrape_interval&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">15s&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">scrape_configs&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">job_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">victoria-metrics&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">static_configs&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">targets&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">vm: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="c"># 如果你有 Traefik&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="c"># - job_name: traefik&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="c"># static_configs:&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="c"># - targets:&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="c"># - traefik:8082&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">job_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">node-exporter&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">static_configs&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">targets&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">d48t: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">job_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cadvisor&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">static_configs&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">targets&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">cadvisor:8080&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">job_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">loki&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">static_configs&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">targets&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">loki:3100&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">job_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">grafana&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">static_configs&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">targets&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">grafana:3000&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">job_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">smartctl-exporter&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">static_configs&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">targets&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">smartctl-exporter:9633&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">job_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">static_configs&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">targets&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">pcm:9738&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>参考 Loki 配置：&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="c"># ./loki/config.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="nt">auth_enabled&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&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">server&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">http_listen_port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">3100&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">common&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">instance_addr&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">127.0.0.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">path_prefix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/loki&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">storage&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">filesystem&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">chunks_directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/loki/chunks&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">rules_directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/loki/rules&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">replication_factor&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">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">ring&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">kvstore&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">store&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">inmemory&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">ingester&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">chunk_encoding&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">lz4&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 比默认的snappy压缩率高&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">chunk_target_size&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">8388608&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 8M&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">max_chunk_age&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">48h&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">chunk_idle_period&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">12h&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">limits_config&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">max_query_lookback&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">672h&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 28 days&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">retention_period&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">672h &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 28 days&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">schema_config&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">configs&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">from&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2020-10-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">store&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tsdb&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">object_store&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">filesystem&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">schema&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v13&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">index&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">prefix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">index_&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">period&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">24h&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">ruler&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">alertmanager_url&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">http://localhost:9093&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>参考 Grafana Alloy 配置：&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;/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"># ./alloy/config.alloy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">logging {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> level = &amp;#34;info&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> format = &amp;#34;logfmt&amp;#34;
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">// Discover Docker containers and extract metadata.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">discovery.docker &amp;#34;logs_integrations_docker&amp;#34; {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> host = &amp;#34;unix:///var/run/docker.sock&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> refresh_interval = &amp;#34;5s&amp;#34;
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">// Define a relabeling rule to create a service name from the container name.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">discovery.relabel &amp;#34;logs_integrations_docker&amp;#34; {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> targets = []
&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> rule {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> target_label = &amp;#34;job&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> replacement = &amp;#34;integrations/docker&amp;#34;
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rule {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> target_label = &amp;#34;instance&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> replacement = &amp;#34;d48t&amp;#34; // constants.hostname
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rule {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> source_labels = [&amp;#34;__meta_docker_container_name&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> regex = &amp;#34;/(.*)&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> target_label = &amp;#34;container&amp;#34;
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rule {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> source_labels = [&amp;#34;__meta_docker_container_log_stream&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> target_label = &amp;#34;stream&amp;#34;
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// Configure a loki.source.docker component to collect logs from Docker containers.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">loki.source.docker &amp;#34;logs_integrations_docker&amp;#34; {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> host = &amp;#34;unix:///var/run/docker.sock&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> targets = discovery.docker.logs_integrations_docker.targets
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> relabel_rules = discovery.relabel.logs_integrations_docker.rules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> forward_to = [loki.write.local.receiver]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> refresh_interval = &amp;#34;15s&amp;#34;
&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>&lt;/span>&lt;span class="line">&lt;span class="cl">loki.write &amp;#34;local&amp;#34; {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> endpoint {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> url = &amp;#34;http://loki:3100/loki/api/v1/push&amp;#34;
&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>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>RTX4090 GPUDirect P2P Unlocked!</title><link>https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/</link><pubDate>Fri, 07 Feb 2025 11:18:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/</guid><description>&lt;p>If you want see the test results directly, please jump to the &lt;a class="link" href="#test-the-unlocked-performance" >Test the unlocked performance&lt;/a> section.&lt;/p>
&lt;h2 id="what-is-p2p">What is P2P?
&lt;/h2>&lt;p>From NVIDIA: GPUDirect Peer to Peer enables GPU-to-GPU copies as well as loads and stores directly over the memory fabric (PCIe, NVLink). GPUDirect Peer to Peer is supported natively by the CUDA Driver.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv.png"
width="3600"
height="2025"
srcset="https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_c0dea0e4133f12b3.webp 480w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_78c536aa2adfec40.jpg 480w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_4d3bc510c9644fee.webp 1024w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_b69160a21f35f725.jpg 1024w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_bef5fc61dedcef4f.webp 1536w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_a015670e0b55601d.jpg 1536w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_6728d4acb2c526d3.webp 2048w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_62160f0ea9bc456b.jpg 2048w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_8da13b08935e1240.webp 2560w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_885a0dd0b0e33832.jpg 2560w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_9989f9783603442a.webp 3600w, https://charlie0129.github.io/blog/p/rtx4090-p2p-unlocked/images/gpudirect-p2p-nv_hu_316c8d5ffa418cbb.jpg 3600w"
loading="lazy"
alt="GPUDirect P2P"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;/p>
&lt;p>As you can see, GPUDirect P2P allows for direct memory access between GPUs, bypassing the CPU and system memory. This can lead to significant performance improvements in multi-GPU systems. For example, in a multi-GPU LLM inference system, the data can be directly transferred between GPUs without involving the CPU, reducing latency and improving throughput.&lt;/p>
&lt;h2 id="why-is-p2p-disabled-on-rtx4090">Why is P2P disabled on RTX4090?
&lt;/h2>&lt;p>Easy, NVIDIA wants to make money. They want you to buy the more expensive Tesla GPUs if you want P2P.&lt;/p>
&lt;h2 id="test-machine-configuration">Test machine configuration
&lt;/h2>&lt;ul>
&lt;li>CPU: 2 x AMD EPYC 7542 (32C 64T, 225W TDP, 2.9GHz base, 3.4GHz boost)&lt;/li>
&lt;li>MEM: 2 x 8 x 32GB 3200MT/s DDR4 ECC&lt;/li>
&lt;li>GPU: 8 x NVIDIA GeForce RTX 4090 (24GB GDDR6X, 450W TDP)&lt;/li>
&lt;/ul>
&lt;p>Topology (ideally, the GPUs should be behind a PCIe switch, but our test machine doesn&amp;rsquo;t have one):&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;/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"> GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 NIC0 NIC1 NIC2 NIC3 CPU Affinity NUMA Affinity GPU NUMA ID
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU0 X SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS 24-31,88-95 3 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU1 SYS X SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS 16-23,80-87 2 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU2 SYS SYS X SYS SYS SYS SYS SYS SYS SYS SYS SYS 8-15,72-79 1 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU3 SYS SYS SYS X SYS SYS SYS SYS SYS SYS SYS SYS 0-7,64-71 0 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU4 SYS SYS SYS SYS X SYS SYS SYS SYS SYS SYS SYS 56-63,120-127 7 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU5 SYS SYS SYS SYS SYS X SYS SYS SYS SYS SYS SYS 48-55,112-119 6 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU6 SYS SYS SYS SYS SYS SYS X SYS PHB PHB PHB PHB 40-47,104-111 5 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GPU7 SYS SYS SYS SYS SYS SYS SYS X SYS SYS SYS SYS 32-39,96-103 4 N/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NIC0 SYS SYS SYS SYS SYS SYS PHB SYS X PIX PHB PHB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NIC1 SYS SYS SYS SYS SYS SYS PHB SYS PIX X PHB PHB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NIC2 SYS SYS SYS SYS SYS SYS PHB SYS PHB PHB X PIX
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NIC3 SYS SYS SYS SYS SYS SYS PHB SYS PHB PHB PIX X
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Legend:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> X = Self
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> PXB = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> PIX = Connection traversing at most a single PCIe bridge
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NV# = Connection traversing a bonded set of # NVLinks
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NIC Legend:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NIC0: mlx5_0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NIC1: mlx5_1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NIC2: mlx5_2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NIC3: mlx5_3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="test-the-original-performance">Test the original performance
&lt;/h2>&lt;h3 id="pci-bar">PCI BAR
&lt;/h3>&lt;p>Only 256MB of BAR memory is available. Note that I did enabled Resizable BAR in the BIOS (not my fault).&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> 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;span class="line">&lt;span class="cl">&lt;span class="go"> BAR1 Memory Usage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Total : 256 MiB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Used : 1 MiB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Free : 255 MiB
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="nccl-tests">NCCL Tests
&lt;/h3>&lt;p>Building NCCL Tests&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">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> 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">make -j&lt;span class="k">$(&lt;/span>nproc&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>Runnning:&lt;/p>
&lt;ul>
&lt;li>Around 14.5GB/s of bandwidth.&lt;/li>
&lt;/ul>
&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;/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> ./build/all_reduce_perf -b &lt;span class="m">8&lt;/span> -e 128M -f &lt;span class="m">2&lt;/span> -g &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> nThread &lt;span class="m">1&lt;/span> nGpus &lt;span class="m">8&lt;/span> minBytes &lt;span class="m">8&lt;/span> maxBytes &lt;span class="m">134217728&lt;/span> step: 2&lt;span class="o">(&lt;/span>factor&lt;span class="o">)&lt;/span> warmup iters: &lt;span class="m">5&lt;/span> iters: &lt;span class="m">20&lt;/span> agg iters: &lt;span class="m">1&lt;/span> validation: &lt;span class="m">1&lt;/span> graph: &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Using devices&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">0&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">0&lt;/span> &lt;span class="o">[&lt;/span>0x01&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">1&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">1&lt;/span> &lt;span class="o">[&lt;/span>0x25&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">2&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">2&lt;/span> &lt;span class="o">[&lt;/span>0x41&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">3&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">3&lt;/span> &lt;span class="o">[&lt;/span>0x61&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">4&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">4&lt;/span> &lt;span class="o">[&lt;/span>0x81&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">5&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">5&lt;/span> &lt;span class="o">[&lt;/span>0xa1&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">6&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">6&lt;/span> &lt;span class="o">[&lt;/span>0xc1&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Rank &lt;span class="m">7&lt;/span> Group &lt;span class="m">0&lt;/span> Pid &lt;span class="m">1756513&lt;/span> on &amp;lt;REDACTED&amp;gt; device &lt;span class="m">7&lt;/span> &lt;span class="o">[&lt;/span>0xe1&lt;span class="o">]&lt;/span> NVIDIA GeForce RTX &lt;span class="m">4090&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># out-of-place in-place &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> size count &lt;span class="nb">type&lt;/span> redop root &lt;span class="nb">time&lt;/span> algbw busbw &lt;span class="c1">#wrong time algbw busbw #wrong&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> &lt;span class="o">(&lt;/span>B&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>elements&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>us&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>GB/s&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>GB/s&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>us&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>GB/s&lt;span class="o">)&lt;/span> &lt;span class="o">(&lt;/span>GB/s&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 8 2 float sum -1 38.83 0.00 0.00 0 40.43 0.00 0.00 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 16 4 float sum -1 38.73 0.00 0.00 0 38.59 0.00 0.00 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 32 8 float sum -1 41.85 0.00 0.00 0 42.55 0.00 0.00 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 64 16 float sum -1 42.55 0.00 0.00 0 42.65 0.00 0.00 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 128 32 float sum -1 42.42 0.00 0.01 0 42.88 0.00 0.01 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 256 64 float sum -1 42.44 0.01 0.01 0 42.53 0.01 0.01 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 512 128 float sum -1 42.58 0.01 0.02 0 42.68 0.01 0.02 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1024 256 float sum -1 44.04 0.02 0.04 0 42.39 0.02 0.04 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2048 512 float sum -1 42.91 0.05 0.08 0 42.61 0.05 0.08 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4096 1024 float sum -1 43.18 0.09 0.17 0 42.69 0.10 0.17 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 8192 2048 float sum -1 44.02 0.19 0.33 0 42.96 0.19 0.33 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 16384 4096 float sum -1 43.10 0.38 0.67 0 42.99 0.38 0.67 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 32768 8192 float sum -1 44.18 0.74 1.30 0 43.63 0.75 1.31 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 65536 16384 float sum -1 45.66 1.44 2.51 0 45.28 1.45 2.53 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 131072 32768 float sum -1 67.27 1.95 3.41 0 54.86 2.39 4.18 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 262144 65536 float sum -1 124.8 2.10 3.68 0 121.1 2.17 3.79 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 524288 131072 float sum -1 196.9 2.66 4.66 0 200.5 2.61 4.58 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1048576 262144 float sum -1 222.8 4.71 8.24 0 226.1 4.64 8.12 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2097152 524288 float sum -1 373.4 5.62 9.83 0 358.6 5.85 10.23 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4194304 1048576 float sum -1 580.8 7.22 12.64 0 592.0 7.09 12.40 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 8388608 2097152 float sum -1 1041.8 8.05 14.09 0 1040.2 8.06 14.11 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 16777216 4194304 float sum -1 1972.8 8.50 14.88 0 1979.5 8.48 14.83 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 33554432 8388608 float sum -1 3905.6 8.59 15.04 0 3919.0 8.56 14.98 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 67108864 16777216 float sum -1 7907.1 8.49 14.85 0 7905.3 8.49 14.86 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 134217728 33554432 float sum -1 16253 8.26 14.45 0 16227 8.27 14.47 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> Out of bounds values : &lt;span class="m">0&lt;/span> OK
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> Avg bus bandwidth : 4.85265
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="p2p-bandwidth-and-latency">P2P Bandwidth and Latency
&lt;/h3>&lt;p>Building:&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">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> https://github.com/NVIDIA/cuda-samples.git
&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 -j&lt;span class="k">$(&lt;/span>nproc&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>Running:&lt;/p>
&lt;ul>
&lt;li>About 21GB/s bidirectional bandwidth&lt;/li>
&lt;li>10us+ GPU-GPU latency&lt;/li>
&lt;/ul>
&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;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&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> ./p2pBandwidthLatencyTest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">P2P Connectivity Matrix
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 1 0 0 0 0 0 0 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 0 1 0 0 0 0 0 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 0 0 1 0 0 0 0 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 0 0 0 1 0 0 0 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 0 0 0 0 1 0 0 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 0 0 0 0 0 1 0 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 0 0 0 0 0 0 1 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 0 0 0 0 0 0 0 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Unidirectional P2P=Disabled Bandwidth Matrix (GB/s)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 909.49 18.77 19.56 20.23 18.72 19.29 19.34 19.83
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 19.95 919.12 18.93 20.18 18.83 19.23 19.24 19.80
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 19.87 19.55 919.66 20.27 18.82 19.28 19.29 19.77
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 19.92 19.48 19.56 918.58 18.82 19.23 19.19 19.80
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 19.88 20.58 19.74 20.54 920.15 18.38 18.89 18.71
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 20.04 20.19 19.80 20.37 18.75 921.29 18.88 19.08
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 20.04 20.11 19.71 20.60 18.77 18.58 920.74 18.62
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 20.02 20.21 19.73 20.33 18.37 18.74 18.89 921.29
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Unidirectional P2P=Enabled Bandwidth (P2P Writes) Matrix (GB/s)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 910.02 18.83 19.55 20.27 18.76 19.36 19.26 19.83
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 19.95 920.20 18.92 20.19 18.83 19.26 19.21 19.80
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 19.82 19.61 919.66 20.28 18.86 19.29 19.34 19.75
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 19.91 19.47 19.60 920.20 18.83 19.26 19.16 19.81
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 19.78 20.43 19.75 20.56 920.25 18.60 18.85 18.75
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 20.02 20.24 19.76 20.45 18.80 922.37 18.89 19.15
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 20.06 20.12 19.75 20.59 18.80 18.58 921.25 18.61
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 20.02 20.16 19.80 20.38 18.41 18.72 18.87 920.83
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Bidirectional P2P=Disabled Bandwidth Matrix (GB/s)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 916.15 21.03 21.53 21.54 20.69 21.02 21.10 21.06
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 21.39 920.74 21.59 21.52 20.55 21.02 20.50 21.18
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 21.15 21.48 921.83 21.20 20.58 21.11 20.98 21.09
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 21.38 21.67 21.42 922.65 20.53 21.05 20.65 21.21
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 20.86 20.89 20.86 20.95 923.69 19.99 20.11 19.97
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 20.90 20.97 21.00 21.02 20.22 922.10 20.15 20.30
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 20.94 20.62 20.89 20.61 19.98 20.07 923.11 19.99
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 21.07 21.13 21.10 21.22 20.00 20.29 20.11 923.46
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Bidirectional P2P=Enabled Bandwidth Matrix (GB/s)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 917.23 20.86 21.49 21.56 20.63 20.99 21.05 21.13
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 21.30 922.37 21.61 21.56 20.53 21.08 20.49 21.15
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 21.15 21.47 922.36 21.16 20.50 21.06 20.94 21.10
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 21.39 21.60 21.39 922.92 20.63 21.09 20.69 21.22
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 20.89 20.86 20.81 20.89 923.46 20.12 20.05 20.02
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 20.88 20.98 20.99 20.87 20.20 923.65 20.12 20.39
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 21.05 20.63 20.89 20.67 20.07 20.05 922.92 20.02
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 21.01 21.12 21.06 21.19 20.01 20.31 20.13 923.16
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">P2P=Disabled Latency Matrix (us)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> GPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 1.54 20.26 11.68 11.60 12.65 15.80 12.19 11.75
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 11.60 1.48 11.58 11.66 16.03 12.13 12.55 14.59
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 20.27 11.59 1.43 20.08 13.17 17.14 16.31 11.41
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 11.50 11.59 11.51 1.47 13.11 16.14 14.04 11.61
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 12.53 14.27 12.75 12.65 1.51 12.47 12.49 12.41
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 11.88 11.73 11.65 11.63 12.28 1.42 14.95 15.40
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 12.27 12.63 12.42 12.68 12.25 12.30 1.47 11.89
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 11.46 16.67 11.44 11.47 12.30 13.33 13.57 1.41
&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"> CPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 4.04 12.23 10.01 9.91 10.88 10.78 10.60 11.29
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 9.79 3.15 9.85 10.06 10.77 10.56 10.50 10.48
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 10.04 9.63 3.24 9.94 10.86 10.65 10.70 10.63
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 9.75 9.61 9.85 3.26 10.92 10.82 10.77 10.67
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 10.50 10.26 10.61 10.56 3.57 11.55 11.48 11.26
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 10.52 10.07 10.40 10.48 11.42 3.52 11.32 11.24
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 10.24 10.20 10.44 10.47 11.42 11.29 3.54 11.35
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 10.26 10.09 10.50 10.41 11.47 11.31 11.36 3.53
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">P2P=Enabled Latency (P2P Writes) Matrix (us)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> GPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 1.54 11.67 11.75 20.27 14.49 15.77 16.95 15.91
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 11.67 1.48 11.44 11.59 15.22 12.17 13.20 13.18
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 11.58 20.27 1.43 11.50 14.60 13.73 13.21 11.38
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 11.58 20.06 12.12 1.47 13.16 16.17 14.67 11.67
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 12.88 12.78 13.92 12.58 1.50 12.46 12.59 12.50
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 11.48 11.93 11.78 11.88 19.18 1.42 11.59 11.40
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 11.95 11.70 14.90 14.00 12.42 11.41 1.46 11.63
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 12.96 11.45 11.60 11.53 20.55 11.32 12.94 1.40
&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"> CPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 0 3.21 9.61 9.86 9.79 10.69 10.48 10.55 10.44
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 1 9.67 3.12 9.81 9.81 10.64 10.46 10.66 10.43
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 2 9.89 9.62 3.20 9.98 10.96 10.67 10.75 10.72
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 3 9.80 9.65 9.89 3.20 10.86 10.78 10.78 10.61
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 4 10.41 10.24 10.60 10.50 3.52 11.34 11.49 11.28
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 5 10.27 10.14 11.93 14.06 14.53 4.55 16.99 19.67
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 6 20.68 20.61 20.39 20.63 20.86 14.35 5.21 14.46
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 7 9.89 9.66 9.85 9.85 10.81 10.62 10.68 3.41
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="how-to-unlock-p2p-on-rtx4090">How to unlock P2P on RTX4090?
&lt;/h2>&lt;p>I encountered this legendary &lt;a class="link" href="https://github.com/tinygrad/open-gpu-kernel-modules" target="_blank" rel="noopener"
>modified NVIDIA driver&lt;/a> by tinygrad that unlocks P2P on RTX4090 GPUs which blows my mind. Let&amp;rsquo;s give it a try!&lt;/p>
&lt;h3 id="disable-iommu">Disable IOMMU
&lt;/h3>&lt;p>If the following command returns an empty list, it means that the IOMMU is disabled, otherwise, you need to disable it. Consult your motherboard manual on how to disable IOMMU.&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">ll /sys/class/iommu/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="enable-resizable-bar">Enable Resizable BAR
&lt;/h3>&lt;p>Enable Resizable BAR in the BIOS. It&amp;rsquo;s required for the modified NVIDIA driver to work. Consult your motherboard manual on how to enable Resizable BAR.&lt;/p>
&lt;h3 id="uninstall-the-official-nvidia-driver">Uninstall the official NVIDIA driver
&lt;/h3>&lt;p>We will install our own modified NVIDIA driver, so we need to uninstall the official NVIDIA driver 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;/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-uninstall
&lt;/span>&lt;/span>&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;h3 id="install-the-official-nvidia-driver-without-kernel-modules">Install the official NVIDIA driver without kernel modules
&lt;/h3>&lt;p>Visit the &lt;a class="link" href="https://github.com/tinygrad/open-gpu-kernel-modules" target="_blank" rel="noopener"
>modified NVIDIA driver&lt;/a> by tinygrad. Check out the branches with p2p in the name. There are multiple versions. You should choose one that fits. For example, I am using the &lt;a class="link" href="https://github.com/tinygrad/open-gpu-kernel-modules/tree/550.90.07-p2p" target="_blank" rel="noopener"
>550.90.07-p2p&lt;/a> branch. Remember the version number &lt;code>550.90.07&lt;/code>, you will need it later.&lt;/p>
&lt;p>Download the official driver with the same version number as you chose (&lt;code>550.90.07&lt;/code> in my case) from the &lt;a class="link" href="https://www.nvidia.com/Download/index.aspx" target="_blank" rel="noopener"
>NVIDIA website&lt;/a>. The driver I downloaded is &lt;code>NVIDIA-Linux-x86_64-550.90.07.run&lt;/code>.&lt;/p>
&lt;p>Install the official driver without the kernel modules because we will install our own modified kernel modules 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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">./NVIDIA-Linux-x86_64-550.90.07.run --no-kernel-modules
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="install-the-modified-kernel-modules">Install the modified kernel modules
&lt;/h3>&lt;p>Clone the modified NVIDIA driver by tinygrad and install. Remember to replace the branch name with the one you chose.&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">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> -b 550.90.07-p2p https://github.com/tinygrad/open-gpu-kernel-modules.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> open-gpu-kernel-modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Just in case&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rmmod nvidia_drm nvidia_modeset nvidia_uvm nvidia
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build and install the modified kernel modules&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make modules -j&lt;span class="k">$(&lt;/span>nproc&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make modules_install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">depmod
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">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;h2 id="test-the-unlocked-performance">Test the unlocked performance
&lt;/h2>&lt;h3 id="pci-bar-1">PCI BAR
&lt;/h3>&lt;p>Now the &lt;strong>BAR memory is fully available&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>256MB -&amp;gt; 32GB.&lt;/li>
&lt;/ul>
&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> 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;span class="line">&lt;span class="cl">&lt;span class="go"> BAR1 Memory Usage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Total : 32768 MiB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Used : 24211 MiB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Free : 8557 MiB
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="nccl-tests-1">NCCL Tests
&lt;/h3>&lt;ul>
&lt;li>14.47GB/s -&amp;gt; 20.64GB/s: &lt;strong>42% improvement&lt;/strong>&lt;/li>
&lt;/ul>
&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># nThread 1 nGpus 8 minBytes 8 maxBytes 134217728 step: 2(factor) warmup iters: 5 iters: 20 agg iters: 1 validation: 1 graph: 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"># Using devices
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 0 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 0 [0x01] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 1 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 1 [0x25] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 2 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 2 [0x41] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 3 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 3 [0x61] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 4 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 4 [0x81] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 5 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 5 [0xa1] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 6 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 6 [0xc1] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Rank 7 Group 0 Pid 33895 on &amp;lt;REDACTED&amp;gt; device 7 [0xe1] NVIDIA GeForce RTX 4090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># out-of-place in-place
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># size count type redop root time algbw busbw #wrong time algbw busbw #wrong
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># (B) (elements) (us) (GB/s) (GB/s) (us) (GB/s) (GB/s)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8 2 float sum -1 38.82 0.00 0.00 0 38.16 0.00 0.00 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16 4 float sum -1 38.31 0.00 0.00 0 38.12 0.00 0.00 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 32 8 float sum -1 38.57 0.00 0.00 0 40.87 0.00 0.00 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 64 16 float sum -1 40.71 0.00 0.00 0 40.65 0.00 0.00 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 128 32 float sum -1 40.51 0.00 0.01 0 41.66 0.00 0.01 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 256 64 float sum -1 41.63 0.01 0.01 0 41.38 0.01 0.01 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 512 128 float sum -1 41.67 0.01 0.02 0 42.45 0.01 0.02 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1024 256 float sum -1 41.70 0.02 0.04 0 41.67 0.02 0.04 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2048 512 float sum -1 41.74 0.05 0.09 0 41.74 0.05 0.09 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4096 1024 float sum -1 42.35 0.10 0.17 0 42.39 0.10 0.17 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8192 2048 float sum -1 42.14 0.19 0.34 0 41.88 0.20 0.34 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16384 4096 float sum -1 43.19 0.38 0.66 0 42.73 0.38 0.67 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 32768 8192 float sum -1 42.17 0.78 1.36 0 42.61 0.77 1.35 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 65536 16384 float sum -1 47.58 1.38 2.41 0 46.95 1.40 2.44 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 131072 32768 float sum -1 72.03 1.82 3.18 0 71.50 1.83 3.21 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 262144 65536 float sum -1 119.7 2.19 3.83 0 118.6 2.21 3.87 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 524288 131072 float sum -1 166.6 3.15 5.51 0 159.8 3.28 5.74 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1048576 262144 float sum -1 216.1 4.85 8.49 0 217.2 4.83 8.45 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2097152 524288 float sum -1 321.5 6.52 11.42 0 322.6 6.50 11.37 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4194304 1048576 float sum -1 478.7 8.76 15.33 0 480.1 8.74 15.29 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8388608 2097152 float sum -1 798.6 10.50 18.38 0 818.0 10.26 17.95 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16777216 4194304 float sum -1 1472.8 11.39 19.94 0 1472.1 11.40 19.94 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 33554432 8388608 float sum -1 2870.3 11.69 20.46 0 2866.4 11.71 20.49 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 67108864 16777216 float sum -1 5705.0 11.76 20.59 0 5695.6 11.78 20.62 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 134217728 33554432 float sum -1 11382 11.79 20.64 0 11380 11.79 20.64 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Out of bounds values : 0 OK
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Avg bus bandwidth : 6.11168
&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;h3 id="p2p-bandwidth-and-latency-1">P2P Bandwidth and Latency
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>P2P is now available&lt;/strong>: see the 1s in the P2P Connectivity Matrix&lt;/li>
&lt;li>Bandwidth: 21.39GB/s -&amp;gt; 50.15GB/s: &lt;strong>134% improvement&lt;/strong>&lt;/li>
&lt;li>Latency: 11.67us -&amp;gt; 1.19us: &lt;strong>89% reduction&lt;/strong>&lt;/li>
&lt;/ul>
&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;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&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">P2P Connectivity Matrix
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 1 1 1 1 1 1 1 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Unidirectional P2P=Disabled Bandwidth Matrix (GB/s)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 912.13 18.86 19.89 20.25 18.34 19.64 19.25 19.45
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 20.56 920.74 19.44 20.27 18.46 19.72 19.19 19.40
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 20.36 19.72 922.37 20.28 18.52 19.65 19.22 19.40
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 20.43 19.68 20.04 922.49 18.47 19.59 19.15 19.41
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 20.20 20.15 20.25 20.26 921.83 19.04 18.84 18.36
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 20.45 20.09 20.17 20.46 18.29 922.37 18.73 18.60
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 20.39 20.18 20.24 20.54 18.44 19.09 922.49 18.65
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 20.53 20.14 20.12 20.51 18.17 19.11 18.74 923.46
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Unidirectional P2P=Enabled Bandwidth (P2P Writes) Matrix (GB/s)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 913.74 25.60 25.85 25.85 21.36 22.41 21.74 22.48
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 25.78 938.91 25.79 25.79 20.70 22.39 22.53 21.63
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 25.70 25.95 939.00 25.78 21.42 21.59 22.21 22.16
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 25.60 25.93 25.53 938.42 22.14 22.30 21.70 22.45
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 22.41 21.61 22.35 22.49 940.70 25.69 25.96 25.90
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 21.34 22.38 21.72 22.41 25.77 939.57 25.95 25.87
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 20.57 22.27 22.32 21.60 25.75 26.01 941.73 25.83
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 21.32 21.52 22.39 22.48 25.51 25.83 25.75 938.44
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Bidirectional P2P=Disabled Bandwidth Matrix (GB/s)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 918.85 20.94 21.69 21.67 20.66 21.44 20.98 21.26
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 21.48 921.29 21.57 21.54 20.20 20.65 20.87 20.70
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 21.34 21.63 921.56 21.13 20.66 21.28 20.89 21.21
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 21.40 21.72 21.62 921.83 20.16 20.92 20.89 20.89
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 20.93 20.33 20.87 20.45 922.90 20.07 20.21 19.96
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 21.40 20.86 21.26 20.90 20.23 923.69 20.18 20.33
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 20.93 20.87 20.89 20.90 20.27 20.25 923.67 20.19
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 21.19 20.66 21.03 20.87 20.02 20.28 20.41 923.41
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Bidirectional P2P=Enabled Bandwidth Matrix (GB/s)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D\D 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 916.45 50.10 50.46 50.29 39.90 41.85 40.83 41.68
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 50.15 919.60 50.19 50.22 38.93 41.79 41.87 40.82
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 49.91 50.42 920.74 50.53 40.19 40.94 41.86 41.61
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 50.08 50.48 50.35 919.66 40.22 41.69 40.92 41.78
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 41.60 40.94 41.86 41.93 919.93 49.80 50.36 50.22
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 40.25 41.68 39.96 41.64 49.75 921.83 50.20 50.66
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 39.59 41.58 41.83 40.95 49.89 50.43 921.56 50.59
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 40.36 40.86 41.76 41.82 49.96 50.43 50.28 921.01
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">P2P=Disabled Latency Matrix (us)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> GPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 1.50 11.60 20.17 20.18 13.08 11.27 14.58 19.33
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 11.68 1.38 11.74 20.17 12.60 11.39 14.35 14.59
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 11.33 11.34 1.31 11.50 16.96 20.53 16.53 20.53
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 11.51 11.51 11.51 1.37 17.86 11.37 15.80 20.54
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 11.47 11.79 11.44 12.57 1.38 12.24 12.93 11.73
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 11.48 11.79 11.64 11.64 14.40 1.44 13.46 14.35
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 13.59 15.05 13.35 12.52 12.06 11.67 1.40 12.34
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 19.32 19.95 11.39 11.44 12.87 12.23 14.05 1.38
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 3.28 10.21 10.15 9.83 10.92 10.75 10.88 10.71
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 10.07 3.14 9.81 9.56 10.58 10.41 10.61 10.35
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 10.13 9.68 3.28 9.62 10.71 10.56 10.73 10.48
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 9.81 9.46 9.68 3.13 10.60 10.41 10.52 10.35
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 10.60 10.20 10.40 10.16 3.48 11.19 11.37 11.07
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 10.37 10.02 10.29 10.04 11.10 3.49 11.24 11.00
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 10.51 10.20 10.35 10.11 11.34 11.09 3.47 11.16
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 10.40 10.11 10.30 10.06 11.17 11.07 11.22 3.42
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">P2P=Enabled Latency (P2P Writes) Matrix (us)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> GPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 1.48 1.24 1.26 1.25 1.41 1.41 1.36 1.42
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 1.19 1.38 1.18 1.18 1.29 1.42 1.29 1.26
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 1.21 1.20 1.31 1.19 1.30 1.34 1.44 1.41
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 1.21 1.19 1.17 1.38 1.37 1.41 1.30 1.44
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 1.69 1.69 1.69 1.64 1.38 1.50 1.59 1.56
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 1.64 1.60 1.60 1.62 1.50 1.43 1.56 1.48
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 1.59 1.53 1.57 1.54 1.48 1.44 1.39 1.44
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 1.58 1.58 1.57 1.56 1.48 1.47 1.45 1.37
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CPU 0 1 2 3 4 5 6 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 3.27 2.84 2.76 2.74 2.80 2.76 2.76 2.75
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 2.80 3.13 2.66 2.74 2.70 2.70 2.68 2.69
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 2.83 2.80 3.26 2.85 2.85 2.78 2.77 2.76
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 2.78 2.69 2.70 3.24 2.69 2.70 2.98 2.67
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 3.18 3.11 3.10 3.09 3.55 3.12 3.09 3.08
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 3.12 3.04 3.10 3.05 3.06 3.50 3.08 3.04
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 3.20 3.11 3.18 3.11 3.11 3.16 3.57 3.08
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 3.12 3.06 3.08 3.12 3.08 3.07 3.04 3.58
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="how-it-works-by-tinygrad">How it works? By tinygrad.
&lt;/h2>&lt;blockquote>
&lt;p>I am not an expert in such low-level hacking (but it&amp;rsquo;s soooo interesting and I want to learn). This part is from the &lt;a class="link" href="https://github.com/tinygrad/open-gpu-kernel-modules" target="_blank" rel="noopener"
>modified NVIDIA driver&lt;/a> by tinygrad (Thank you tinygrad). I just put it here for reference.&lt;/p>&lt;/blockquote>
&lt;p>Normally, P2P on NVIDIA cards uses MAILBOXP2P. This is some hardware interface designed to allow GPUs to transfer memory back in the days of small BAR. It is not present or disabled in hardware on the 4090s, and that&amp;rsquo;s why P2P doesn&amp;rsquo;t work. There &lt;a class="link" href="https://forums.developer.nvidia.com/t/standard-nvidia-cuda-tests-fail-with-dual-rtx-4090-linux-box/233202" target="_blank" rel="noopener"
>was a bug in early versions&lt;/a> of the driver that reported that it did work, and it was actually sending stuff on the PCIe bus. However, because the mailbox hardware wasn&amp;rsquo;t present, these copies wouldn&amp;rsquo;t go to the right place. You could even crash the system by doing something like &lt;code>torch.zeros(10000,10000).cuda().to(&amp;quot;cuda:1&amp;quot;)&lt;/code>&lt;/p>
&lt;p>In some 3090s and all 4090s, NVIDIA added large BAR support.&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">tiny@tiny14:~$ lspci -s 01:00.0 -v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">01:00.0 VGA compatible controller: NVIDIA Corporation AD102 [GeForce RTX 4090] (rev a1) (prog-if 00 [VGA controller])
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Subsystem: Micro-Star International Co., Ltd. [MSI] Device 510b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Physical Slot: 49
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Flags: bus master, fast devsel, latency 0, IRQ 377
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Memory at b2000000 (32-bit, non-prefetchable) [size=16M]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Memory at 28800000000 (64-bit, prefetchable) [size=32G]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Memory at 28400000000 (64-bit, prefetchable) [size=32M]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> I/O ports at 3000 [size=128]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Expansion ROM at b3000000 [virtual] [disabled] [size=512K]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Capabilities: &amp;lt;access denied&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Kernel driver in use: nvidia
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Notice how BAR1 is size 32G. In H100, they also added support for a PCIe mode that uses the BAR directly instead of the mailboxes, called BAR1P2P. So, what happens if we try to enable that on a 4090?&lt;/p>
&lt;p>We do this by bypassing the HAL and calling a bunch of the GH100 methods directly. Methods like &lt;code>kbusEnableStaticBar1Mapping_GH100&lt;/code>, which maps the entire VRAM into BAR1. This mostly just works, but we had to disable the use of that region in the &lt;code>MapAperture&lt;/code> function for some reason. Shouldn&amp;rsquo;t matter.&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[ 3491.654009] NVRM: kbusEnableStaticBar1Mapping_GH100: Static bar1 mapped offset 0x0 size 0x5e9200000
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3491.793389] NVRM: kbusEnableStaticBar1Mapping_GH100: Static bar1 mapped offset 0x0 size 0x5e9200000
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Perfect, we now have the VRAM mapped. However, it&amp;rsquo;s not that easy to get P2P. When you run &lt;code>./simpleP2P&lt;/code> from &lt;code>cuda-samples&lt;/code>, you get this error.&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[ 3742.840689] NVRM: kbusCreateP2PMappingForBar1P2P_GH100: added PCIe BAR1 P2P mapping between GPU2 and GPU3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3742.840762] NVRM: kbusCreateP2PMappingForBar1P2P_GH100: added PCIe BAR1 P2P mapping between GPU3 and GPU2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3742.841089] NVRM: nvAssertFailed: Assertion failed: (shifted &amp;gt;&amp;gt; pField-&amp;gt;shift) == value @ field_desc.h:272
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3742.841106] NVRM: nvAssertFailed: Assertion failed: (shifted &amp;amp; pField-&amp;gt;maskPos) == shifted @ field_desc.h:273
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3742.841281] NVRM: nvAssertFailed: Assertion failed: (shifted &amp;gt;&amp;gt; pField-&amp;gt;shift) == value @ field_desc.h:272
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3742.841292] NVRM: nvAssertFailed: Assertion failed: (shifted &amp;amp; pField-&amp;gt;maskPos) == shifted @ field_desc.h:273
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3742.865948] NVRM: GPU at PCI:0000:01:00: GPU-49c7a6c9-e3a8-3b48-f0ba-171520d77dd1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 3742.865956] NVRM: Xid (PCI:0000:01:00): 31, pid=21804, name=simpleP2P, Ch 00000013, intr 00000000. MMU Fault: ENGINE CE3 HUBCLIENT_CE1 faulted @ 0x7f97_94000000. Fault is of type FAULT_INFO_TYPE_UNSUPPORTED_KIND ACCESS_TYPE_VIRT_WRITE
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Failing with an MMU fault. So you dive into this and find that it&amp;rsquo;s using &lt;code>GMMU_APERTURE_PEER&lt;/code> as the mapping type. That doesn&amp;rsquo;t seem supported in the 4090. So let&amp;rsquo;s see what types are supported, &lt;code>GMMU_APERTURE_VIDEO&lt;/code>,&lt;code>GMMU_APERTURE_SYS_NONCOH&lt;/code>, and &lt;code>GMMU_APERTURE_SYS_COH&lt;/code>. We don&amp;rsquo;t care about being coherent with the CPU&amp;rsquo;s L2 cache, but it does have to go out the PCIe bus, so we rewrite &lt;code>GMMU_APERTURE_PEER&lt;/code> to &lt;code>GMMU_APERTURE_SYS_NONCOH&lt;/code>. We also no longer set the peer id that was corrupting the page table.&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">cudaMemcpyPeer / cudaMemcpy between GPU0 and GPU1: 24.21GB/s
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Preparing host buffer and memcpy to GPU0...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Run kernel on GPU1, taking source data from GPU0 and writing to GPU1...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Run kernel on GPU0, taking source data from GPU1 and writing to GPU0...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Copy data back to host from GPU0 and verify results...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Verification error @ element 1: val = 0.000000, ref = 4.000000
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Verification error @ element 2: val = 0.000000, ref = 8.000000
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Progress! &lt;code>./simpleP2P&lt;/code> appears to work, however the copy isn&amp;rsquo;t happening. The address is likely wrong. It turns out they have a separate field for the peer address called &lt;code>fldAddrPeer&lt;/code>, we change that to &lt;code>fldAddrSysmem&lt;/code>. We also print out the addresses and note that the physical BAR address isn&amp;rsquo;t being added properly, they provide a field &lt;code>fabricBaseAddress&lt;/code> for &lt;code>GMMU_APERTURE_PEER&lt;/code>, we reuse it and put the &lt;code>BAR1&lt;/code> base address in there.&lt;/p></description></item><item><title>XFS Tuning for Hardware RAID</title><link>https://charlie0129.github.io/blog/p/xfs-with-hw-raid/</link><pubDate>Tue, 05 Nov 2024 14:40:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/xfs-with-hw-raid/</guid><description>&lt;h2 id="stripe-size">Stripe Size
&lt;/h2>&lt;p>When you create an hardware RAID array, you must encountered the term &lt;strong>stripe size&lt;/strong>.&lt;/p>
&lt;p>For example, an 8-disk RAID 1/0 has a &lt;strong>stripe width&lt;/strong> of 4, with a &lt;strong>stripe element size&lt;/strong> of 64 KB has a &lt;strong>stripe size&lt;/strong> of 256 KB (4 * 64 KB). A 5-disk RAID 5 (4+1) with a 64 KB &lt;strong>stripe element size&lt;/strong> also has a &lt;strong>stripe width&lt;/strong> of 256 KB drive (4 * 64 KB) .&lt;/p>
&lt;p>A &lt;strong>stripe&lt;/strong> is the smallest chunk of data within a RAID array that can be addressed. People often also refer to this as granularity or block size. It can be compared to the blocks (logical block addressing - LBA) on conventional hard drives. Most RAID controllers allow the user to define her or his favorite stripe size, because it alters the performance characteristics of a RAID array. &lt;a class="link" href="https://www.tomshardware.com/reviews/RAID-SCALING-CHARTS,1735-4.html" target="_blank" rel="noopener"
>Reference&lt;/a>&lt;/p>
&lt;p>It is important to let the filesystem you use know the underlying RAID array&amp;rsquo;s &lt;strong>stripe size&lt;/strong>. This is because the filesystem will align its data blocks to the RAID array&amp;rsquo;s &lt;strong>stripe size&lt;/strong>. This will help to avoid read-modify-write operations, which can be very slow.&lt;/p>
&lt;p>For example, when creating a Virtual Disk in a Dell PERC H730P Controller, you can see the &lt;strong>stripe element size&lt;/strong>.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/xfs-with-hw-raid/images/h730p-create-vd.png"
width="1020"
height="764"
srcset="https://charlie0129.github.io/blog/p/xfs-with-hw-raid/images/h730p-create-vd_hu_e932e40b4527a18e.webp 480w, https://charlie0129.github.io/blog/p/xfs-with-hw-raid/images/h730p-create-vd_hu_d36f0409f7a5add7.jpg 480w, https://charlie0129.github.io/blog/p/xfs-with-hw-raid/images/h730p-create-vd_hu_66bb4e0f7899a2f6.webp 1020w, https://charlie0129.github.io/blog/p/xfs-with-hw-raid/images/h730p-create-vd_hu_5c24b674b1db5127.jpg 1020w"
loading="lazy"
alt="image-2024110530534764 PM"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;h2 id="tune-xfs-for-hardware-raid">Tune XFS for Hardware RAID
&lt;/h2>&lt;p>When you format the XFS partition, you can tell XFS about the underlying array info.&lt;/p>
&lt;p>For example, I have 4x8T HDDs in RAID 10 with &lt;strong>stripe element size&lt;/strong> of 64 KB. So I have a &lt;strong>stripe width&lt;/strong> of 2 and a &lt;strong>stripe size&lt;/strong> of 128 KB. The corresponding XFS options is &lt;code>sw=2&lt;/code> and &lt;code>su=64k&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;/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">mkfs.xfs -f -d &lt;span class="nv">su&lt;/span>&lt;span class="o">=&lt;/span>64k,sw&lt;span class="o">=&lt;/span>&lt;span class="m">2&lt;/span> /dev/sdb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="other-resources">Other Resources
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.percona.com/blog/aligning-io-on-a-hard-disk-raid-the-theory/" target="_blank" rel="noopener"
>https://www.percona.com/blog/aligning-io-on-a-hard-disk-raid-the-theory/&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.percona.com/blog/setting-up-xfs-the-simple-edition/" target="_blank" rel="noopener"
>https://www.percona.com/blog/setting-up-xfs-the-simple-edition/&lt;/a>&lt;/li>
&lt;/ul></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><item><title>Fake macOS Version Detected by Homebrew</title><link>https://charlie0129.github.io/blog/p/homebrew-fake-macos-version/</link><pubDate>Tue, 08 Oct 2024 22:08:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/homebrew-fake-macos-version/</guid><description>&lt;h2 id="background">Background
&lt;/h2>&lt;p>One particular issue that I encountered when I tried to install some software using Homebrew on my Mac is that Homebrew deprecates support for older macOS versions just like Apple do. Once a macOS version is deprecated, Homebrew will not provide bottles (precompiled binaries) for that version. This means that you have to compile the software from source, which is extremely time-consuming and sometimes error-prone. Imagine installing &lt;code>shell-check&lt;/code> will require to build &lt;code>ghc&lt;/code> from source, which takes hours, and then use &lt;code>ghc&lt;/code> to build &lt;code>shell-check&lt;/code> from source. This is not a good experience.&lt;/p>
&lt;h2 id="solution">Solution
&lt;/h2>&lt;h3 id="pin-homebrewcore-to-an-older-version">Pin homebrew/core to an older version
&lt;/h3>&lt;p>One way to work around this issue is to use a older version of the &lt;code>homebrew/core&lt;/code> tap, such that the bottles are still available for the macOS version you are using because they are built when your macOS version is supported. The downside is that you will be using outdated software. If this is not an issue for you then congratulations. You can achieve this by running the following command:&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> brew tap homebrew/core
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nb">cd&lt;/span> &lt;span class="k">$(&lt;/span>brew --repo homebrew/core&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> git reset --hard &amp;lt;commit-hash&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Make sure you disable the auto-update of Homebrew so that it does not update the &lt;code>homebrew/core&lt;/code> tap to the latest version. You can do this by running the following command:&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> &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export HOMEBREW_NO_AUTO_UPDATE=1&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="fake-macos-version">Fake macOS version
&lt;/h3>&lt;p>Another way is to fake the macOS version that Homebrew detects. This way, Homebrew will provide bottles for the macOS version you are faking. Most software will work and you will be using the latest software. However, this may not work for all software as some software may have dependencies that are not available for the macOS version you are faking. You can achieve this by running the following command:&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> &lt;span class="nb">export&lt;/span> &lt;span class="nv">HOMEBREW_FAKE_MACOS&lt;/span>&lt;span class="o">=&lt;/span>13.0 &lt;span class="c1"># Choose a macOS version that is supported by Homebrew and close to your macOS version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> brew install xxx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="explanation">Explanation
&lt;/h2>&lt;p>Homebrew actually has an private undocumented API that allows you to fake the macOS version. &lt;a class="link" href="https://github.com/Homebrew/brew/blob/a3d8f4e0e4a22da9990d59cca70bec1e7be726cf/Library/Homebrew/os/mac.rb#L41" target="_blank" rel="noopener"
>https://github.com/Homebrew/brew/blob/a3d8f4e0e4a22da9990d59cca70bec1e7be726cf/Library/Homebrew/os/mac.rb#L41&lt;/a>&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-ruby" data-lang="ruby">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This can be compared to numerics, strings, or symbols&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># using the standard Ruby Comparable methods.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># @api internal&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sig&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">returns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="no">MacOSVersion&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nc">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="nf">full_version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="vi">@full_version&lt;/span> &lt;span class="o">||=&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fake_macos&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="no">ENV&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;HOMEBREW_FAKE_MACOS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kp">nil&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="c1"># for Portable Ruby building&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="no">MacOSVersion&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fake_macos&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="no">MacOSVersion&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="no">VERSION&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">end&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">end&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="example">Example
&lt;/h2>&lt;p>I have macOS Monterey (12.0) running, which is just deprecated by Homebrew. I want to install &lt;code>batt&lt;/code> which is not available as a bottle for macOS Monterey.&lt;/p>
&lt;p>If I run &lt;code>brew install batt&lt;/code>, I will get the following error:&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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> brew install batt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Warning: You are using macOS 12.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">We (and Apple) do not provide support for this old version.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">It is expected behaviour that some formulae will fail to build in this old version.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">It is expected behaviour that Homebrew will be buggy and slow.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Do not create any issues about this on Homebrew&amp;#39;s GitHub repositories.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Do not create any issues even if you think this message is unrelated.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Any opened issues will be immediately closed without response.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Do not ask for help from Homebrew or its maintainers on social media.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">You may ask for help in Homebrew&amp;#39;s discussions but are unlikely to receive a response.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Try to figure out the problem yourself and submit a fix as a pull request.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">We will review it but may or may not accept it.
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>It&amp;rsquo;s Oct 8, 2024 and the latest macOS version is macOS Sequoia (15.0). So the last 3 supported macOS versions are:&lt;/p>
&lt;ul>
&lt;li>macOS Sequoia (15.0)&lt;/li>
&lt;li>macOS Sonoma (14.0)&lt;/li>
&lt;li>macOS Ventura (13.0)&lt;/li>
&lt;/ul>
&lt;p>You can confirm that by checking out &lt;code>batt&lt;/code>&amp;rsquo;s formula:&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> brew edit batt
&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">sha256 cellar: :any_skip_relocation, arm64_sequoia: &amp;#34;61bd7790a82f2269b9a0ce1585c57564d03be4e4ac89b8f0d8843c0073a688e6&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">sha256 cellar: :any_skip_relocation, arm64_sonoma: &amp;#34;198bd7bb9a808f0a9e4cb1a31b7e9c2a72d690ba1d07ebddf78b6d0ce0b6dd03&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">sha256 cellar: :any_skip_relocation, arm64_ventura: &amp;#34;6eff598159b263327b8b562ab32f5e5e7157c20f25cbecfa08b48eda794c4c43&amp;#34;
&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>Showing only the bottles for macOS Sequoia (15.0), macOS Sonoma (14.0), and macOS Ventura (13.0) are available.&lt;/p>
&lt;p>Sadly my macOS version (Monterey 12.0) is not in the list, but macOS Ventura (13.0) is the closest to macOS Monterey (12.0). So I can fake the macOS version to macOS Ventura (13.0) by running the following command:&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="gp">$&lt;/span> &lt;span class="nb">export&lt;/span> &lt;span class="nv">HOMEBREW_FAKE_MACOS&lt;/span>&lt;span class="o">=&lt;/span>13.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> brew config
&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">macOS: 13.0-arm64 # YES! I&amp;#39;m now faking macOS Ventura (13.0)
&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 class="gp">$&lt;/span> brew install batt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">==&amp;gt; Downloading https://ghcr.io/v2/homebrew/core/batt/manifests/0.3.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Already downloaded: /Users/charlie/Library/Caches/Homebrew/downloads/86da7d77f0bacb44475e0280eef162148e3b51df8ea44ff9bc16a5a7bba6d39f--batt-0.3.1.bottle_manifest.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">==&amp;gt; Fetching batt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">==&amp;gt; Downloading https://ghcr.io/v2/homebrew/core/batt/blobs/sha256:6eff598159b263327b8b562ab32f5e5e7157c20f25cbecfa08b48eda794c4c43
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Already downloaded: /Users/charlie/Library/Caches/Homebrew/downloads/24fc002de2ee11096a544dd84ff1033262a5cb627c2b824c915a552bb6a588dd--batt--0.3.1.arm64_ventura.bottle.tar.gz
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>As you can see, Homebrew is downloading the bottle for macOS Ventura (13.0) successfully! (Of course, the log is showing that I have already downloaded earlier, but you get the idea.)&lt;/p>
&lt;p>&lt;code>batt&lt;/code> itself does not rely on any APIs that are not available in my current macOS version, so it works perfectly fine.&lt;/p></description></item><item><title>What happens when your Kubernetes master has low memory or slow disk?</title><link>https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/</link><pubDate>Thu, 15 Aug 2024 14:54:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/</guid><description>&lt;h2 id="background">Background
&lt;/h2>&lt;p>We want to test some sandbox application on our new cluster. My colleague reported that the performance is not as expected and wanted me to take a look.&lt;/p>
&lt;p>The sandbox application works like this: it starts a python container, runs some scripts, and exits. They executes scripts provided by the user, so the cluster is expected to create and destroy a lot of containers in a short period of time.&lt;/p>
&lt;h2 id="assumptions">Assumptions
&lt;/h2>&lt;p>Considering the characteristics of the application, the bottleneck could be the following:&lt;/p>
&lt;ul>
&lt;li>CPU/Memory of the worker nodes, or the container runtime&lt;/li>
&lt;li>Performance of the master node (apiserver, etcd, and etc)&lt;/li>
&lt;/ul>
&lt;p>Since we have relatively beefy worker nodes, each with 2*Xeon Platinum 8353v (72C144T) and 512GB of memory, the bottleneck is likely the master node (8C 16GB memory).&lt;/p>
&lt;p>Let&amp;rsquo;s verify this assumption.&lt;/p>
&lt;h2 id="observations">Observations
&lt;/h2>&lt;p>I ran k6 to load test the cluster.&lt;/p>
&lt;p>Pods started to pending&amp;hellip;&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/pending-pods.png"
width="324"
height="514"
srcset="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/pending-pods_hu_e0659d193f6ba0c7.webp 324w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/pending-pods_hu_ba8aa5c4b632e40c.jpg 324w"
loading="lazy"
alt="Pending Pods"
class="gallery-image"
data-flex-grow="63"
data-flex-basis="151px"
>
&lt;/p>
&lt;p>Hmm, it smelled like a etcd issue (the newly-created Pods is not able to be written to the database). I looked at the etcd dashboard. Sure enough, db backend is having some trouble writing the files.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics.png"
width="2618"
height="1066"
srcset="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_80d6d18cafca0ce4.webp 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_c269990831e72efd.jpg 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_91c5ccc92849dc2c.webp 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_cf32d92f25ea41c1.jpg 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_9127fd9de5aa228a.webp 1536w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_a0885a5cfc438bd8.jpg 1536w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_8c1d60c12d405fae.webp 2048w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_cad034763199ee91.jpg 2048w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_7189bf7d1cc6fc48.webp 2560w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_8085901669bce26f.jpg 2560w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_ef9cffa4f1c7ec0d.webp 2618w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/etcd-metrics_hu_b63db6228ee138a1.jpg 2618w"
loading="lazy"
alt="etcd metrics"
class="gallery-image"
data-flex-grow="245"
data-flex-basis="589px"
>
&lt;/p>
&lt;p>I grabed the the disk stats: the disks on the master node is almost fully utilized! A better disk (SSD) is really needed.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time.png"
width="2624"
height="1324"
srcset="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_896f8b53e1d20b40.webp 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_852332f2a2d80d7b.jpg 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_ea8ef60e62c81adf.webp 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_e06faae575985720.jpg 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_f42b0df92f42c16.webp 1536w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_77e90f88e10b09b2.jpg 1536w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_f3ce497608a58da8.webp 2048w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_11e03a19ecaada22.jpg 2048w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_75ecda59dd5360c7.webp 2560w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_afdd7e0c319c18b.jpg 2560w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_14566d795e0e6d8a.webp 2624w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/disk-io-time_hu_cc392e1e774a24d8.jpg 2624w"
loading="lazy"
alt="Disk IO time"
class="gallery-image"
data-flex-grow="198"
data-flex-basis="475px"
>
&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/iostat.png"
width="1280"
height="185"
srcset="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/iostat_hu_2d5369d4b44180a7.webp 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/iostat_hu_35ff1e9fe8b6ee07.jpg 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/iostat_hu_a384d1e2ec522a4b.webp 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/iostat_hu_3fff69ee7108264.jpg 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/iostat_hu_a395520b5f31a537.webp 1280w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/iostat_hu_4a88be4cca2c1c44.jpg 1280w"
loading="lazy"
alt="iostat"
class="gallery-image"
data-flex-grow="691"
data-flex-basis="1660px"
>
&lt;/p>
&lt;h2 id="side-note">Side note
&lt;/h2>&lt;p>We also experienced unexpected issue when I applied a cilium config, the whole cluster goes down (Thanks god. This is not a production cluster, or this will be a total disaster). At first, I assumed the cilium config that I written is wrong and I reverted, hoping the cluster will be fixed. Nope, all nodes are down. However, some interesting behavior is noticed: all worker nodes are in a ready - notready loop.&lt;/p>
&lt;p>Upon further investigation, I noticed the apiserver is constantly being OOM killed.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-oomkilled.jpg"
width="1038"
height="232"
srcset="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-oomkilled_hu_e91b0622f442775d.webp 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-oomkilled_hu_80c2e6f8dbe3044c.jpg 480w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-oomkilled_hu_1f54d78c59f2929.webp 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-oomkilled_hu_f65127f002ff7e84.jpg 1024w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-oomkilled_hu_c90160b6ead6e703.webp 1038w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-oomkilled_hu_e43b58e01016429a.jpg 1038w"
loading="lazy"
alt="apiserver OOM killed"
class="gallery-image"
data-flex-grow="447"
data-flex-basis="1073px"
>
&lt;/p>
&lt;p>The apiserver is using more memory than the node has.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-ram-usage.jpg"
width="426"
height="454"
srcset="https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-ram-usage_hu_a36ad326df17beb3.webp 426w, https://charlie0129.github.io/blog/p/what-happens-when-k8s-master-have-low-memory-or-slow-disk/images/apiserver-ram-usage_hu_d3fe3e00e88e29b4.jpg 426w"
loading="lazy"
alt="apiserver RAM usage"
class="gallery-image"
data-flex-grow="93"
data-flex-basis="225px"
>
&lt;/p>
&lt;p>It seems something is hitting the apiserver really hard. Under normal circumstances, I will go to the master node to look at the apiserver logs. However, the master node is down (because OOM) and I cannot ssh into it.&lt;/p>
&lt;p>The cluster is stuck in such a loop:&lt;/p>
&lt;ol>
&lt;li>Worker nodes are all starting (cilium starting) and pulling/changed config through apiserver&lt;/li>
&lt;li>apiserver on the master node OOM&amp;rsquo;ed because of bursts of requests&lt;/li>
&lt;li>the apiserver is OOM killed&lt;/li>
&lt;li>worker node cannot report status to the apiserver&lt;/li>
&lt;li>worker node is marked as not ready; cilium pods are terminated because of nodes are not ready&lt;/li>
&lt;li>go to step 1&lt;/li>
&lt;/ol>
&lt;p>Since the cluster cannot properly start in such a scenario, I have to use some dirty fix to start the cluster: just after the apiserver is started, and before the apiserver eats all memory, such that the master node is still alive, quickly SSH into the master node and kill the apiserver process. By doing this a few times, we can span the burst of requests over a longer period of time, avoiding overloading the apiserver (causing it to be killed).&lt;/p>
&lt;h2 id="solution">Solution
&lt;/h2>&lt;p>The solution is simple: replace the master nodes with faster ones (much more memory, NVME SSDs).&lt;/p></description></item><item><title>Rename RDMA NIC Interface Names</title><link>https://charlie0129.github.io/blog/p/rename-rnics/</link><pubDate>Fri, 09 Aug 2024 14:28:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/rename-rnics/</guid><description>&lt;h2 id="background">Background
&lt;/h2>&lt;p>The RDMA NIC names (InfiniBand HCAs in this case) of our nodes are inconsistent. We want to rename them to a consistent naming scheme.&lt;/p>
&lt;p>One node:&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;/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">root@tj01-h20-node139:/# ibstatus
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp171s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x70
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp187s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x6f
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp203s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x29
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp219s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x6a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp41s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x6c
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp59s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x2b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp75s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x80
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;ibp93s0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x6e
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;rocep22s0f0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 25 Gb/sec (1X EDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: Ethernet
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Another node:&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;/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">root@tj01-h20-node140:/# ibstatus
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_2&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x68
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_3&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x71
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_4&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x27
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_5&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x69
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_6&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x6d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_7&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x6b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_8&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x2a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_9&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x2c
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Infiniband device &amp;#39;mlx5_bond_0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> base lid: 0x0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sm lid: 0x0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rate: 25 Gb/sec (1X EDR)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> link_layer: Ethernet
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Note that one node has the names &lt;code>ibp*&lt;/code> and the other has &lt;code>mlx5_*&lt;/code>. We want to make node 1 follow a same naming scheme as the other nodes.&lt;/p>
&lt;p>To be fair &lt;code>ibp*&lt;/code> is actually consistent (named using PCI location). &lt;code>mlx5_*&lt;/code> is not consistent, which depends on the card initialization order and can change after a reboot.&lt;/p>
&lt;p>Anyway, since all other nodes are using &lt;code>mlx5_*&lt;/code> naming scheme, we want to rename all of them to &lt;code>mlx5_*&lt;/code> for consistency with the other nodes.&lt;/p>
&lt;h2 id="solution">Solution
&lt;/h2>&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">cp cp /lib/udev/rules.d/60-rdma-persistent-naming.rules /etc/udev/rules.d/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This is all you need. Now reboot your server.&lt;/p>
&lt;p>If you are interested in the details, read on.&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;/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"># SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Copyright (c) 2019, Mellanox Technologies. All rights reserved. See COPYING file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Rename modes:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NAME_FALLBACK - Try to name devices in the following order:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># by-pci -&amp;gt; by-guid -&amp;gt; kernel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NAME_KERNEL - leave name as kernel provided&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NAME_PCI - based on PCI/slot/function location&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NAME_GUID - based on system image GUID&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NAME_FIXED - rename the device to the fixed named in the next argument&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The stable names are combination of device type technology and rename mode.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Infiniband - ib*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># RoCE - roce*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># iWARP - iw*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># OPA - opa*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Default (unknown protocol) - rdma*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Example:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># * NAME_PCI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># pci = 0000:00:0c.4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Device type = IB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># mlx5_0 -&amp;gt; ibp0s12f4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># * NAME_GUID&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># GUID = 5254:00c0:fe12:3455&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Device type = RoCE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># mlx5_0 -&amp;gt; rocex525400c0fe123455&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ACTION&lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="s2">&amp;#34;add&amp;#34;&lt;/span>, &lt;span class="nv">SUBSYSTEM&lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="s2">&amp;#34;infiniband&amp;#34;&lt;/span>, &lt;span class="nv">PROGRAM&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;rdma_rename %k NAME_KERNEL&amp;#34;&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="c1"># Example:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># * NAME_FIXED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># fixed name for specific board_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#ACTION==&amp;#34;add&amp;#34;, ATTR{board_id}==&amp;#34;MSF0010110035&amp;#34;, SUBSYSTEM==&amp;#34;infiniband&amp;#34;, PROGRAM=&amp;#34;rdma_rename %k NAME_FIXED myib&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Look at the line contains &lt;code>PROGRAM=&amp;quot;rdma_rename %k NAME_KERNEL&amp;quot;&lt;/code>. So if you want to use &lt;code>mlx5_*&lt;/code> (called kernel names), you can use &lt;code>NAME_KERNEL&lt;/code> as the rename mode (default). If you want to use &lt;code>ibp*&lt;/code> (called PCI names), you can use &lt;code>NAME_PCI&lt;/code> as the rename mode or use &lt;code>NAME_FALLBACK&lt;/code>, which first tries &lt;code>NAME_PCI&lt;/code>.&lt;/p></description></item><item><title>NFS over RDMA Setup &amp; Benchmark</title><link>https://charlie0129.github.io/blog/p/nfs-over-rdma/</link><pubDate>Fri, 19 Jul 2024 22:36:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/nfs-over-rdma/</guid><description>&lt;p>So I decided to test NFS over RDMA somehow.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Hostname&lt;/th>
&lt;th>&lt;code>ft2000&lt;/code> (Server)&lt;/th>
&lt;th>&lt;code>t3640&lt;/code> (Client)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CPU&lt;/td>
&lt;td>Phytium FT-2000+/64&lt;/td>
&lt;td>Intel Core i9-10900K&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RAM&lt;/td>
&lt;td>Quad Channel DDR4 128GB&lt;/td>
&lt;td>Dual Channel DDR4 64GB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>NIC&lt;/td>
&lt;td>Mellanox ConnextX-4 Lx 25GbE&lt;/td>
&lt;td>Mellanox ConnextX-4 Lx 25GbE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OS&lt;/td>
&lt;td>UOS Server 20 1070a&lt;/td>
&lt;td>Debian 12&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Two servers are connected using a 25GbE network. The goal is to set up NFS over RDMA and benchmark it to see the performance difference between NFS over TCP and NFS over RDMA.&lt;/p>
&lt;h2 id="rdma-link-verification">RDMA Link Verification
&lt;/h2>&lt;p>&lt;strong>Install RDMA/IB dependencies:&lt;/strong>&lt;/p>
&lt;p>Dependencies should be the same on both devices (no server and client differences). The difference is only the package manager (and OS).&lt;/p>
&lt;blockquote>
&lt;p>If you are using Mellanox OFED, you can skip this step. Be sure to install Mellanox OFED using &lt;code>--with-nfsrdma&lt;/code> flag. Otherwise, you will not be able to use NFS over RDMA.&lt;/p>&lt;/blockquote>
&lt;p>On UOS Server 20 1070a, which is Anolis8-based, which is again CentOS8-based:&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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@ft2000:/# yum install -y libibverbs librdmacm opensm-libs rdma-core rdma-core-devel \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> librdmacm-utils opensm-static srp_daemon ucx-devel ucx-rdmacm infiniband-diags ibacm \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> opensm-devel ucx ucx-ib libibumad opensm mstflint ucx-cma openmpi rshim libibverbs-utils \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> perftest rdma-core rdma-core-devel infiniband-diags rshim
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>On Debian 12:&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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@t3640:/# apt install -y infiniband-diags srptools perftest opensm-doc librdmacm-dev \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rdmacm-utils librdmacm1 ibacm libibmad-dev libibmad5 libibumad-dev libibumad3 \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> ibverbs-utils libibverbs-dev libibverbs1 mstflint rdma-core opensm fio librbd1 \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> librados2 libibnetdisc5 ibverbs-providers
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Make sure link is up on both servers using &lt;code>ip&lt;/code> or &lt;code>ibstatus&lt;/code> command:&lt;/strong>&lt;/p>
&lt;p>Client:&lt;/p>
&lt;p>Use &lt;code>ip&lt;/code> (note &lt;code>state UP&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;/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@t3640:/# ip l show dev enp1s0f1np1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">6: enp1s0f1np1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Or use &lt;code>ibstatis&lt;/code> (note &lt;code>phys state: 5: LinkUp&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;/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@t3640:/# ibstatus
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Infiniband device &amp;#39;mlx5_0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> base lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> sm lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rate: 10 Gb/sec (1X QDR)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> link_layer: Ethernet
&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">Infiniband device &amp;#39;mlx5_1&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> base lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> sm lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rate: 25 Gb/sec (1X EDR)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> link_layer: Ethernet
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Server:&lt;/p>
&lt;p>Use &lt;code>ip&lt;/code> (note &lt;code>state UP&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;/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@ft2000:/# ip l show dev enp16s0f0np0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">4: enp16s0f0np0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Or use &lt;code>ibstatis&lt;/code> (note &lt;code>phys state: 5: LinkUp&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;/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@ft2000:/# ibstatus
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Infiniband device &amp;#39;mlx5_0&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> base lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> sm lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rate: 25 Gb/sec (1X EDR)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> link_layer: Ethernet
&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">Infiniband device &amp;#39;mlx5_1&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> base lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> sm lid: 0x0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> state: 1: DOWN
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> phys state: 3: Disabled
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rate: 40 Gb/sec (4X QDR)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> link_layer: Ethernet
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Verify IP connectivity between the servers (ping the IP address of the other server):&lt;/strong>&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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@t3640:/# ping 192.168.23.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">PING 192.168.23.1 (192.168.23.1) 56(84) bytes of data.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">64 bytes from 192.168.23.1: icmp_seq=1 ttl=64 time=0.199 ms
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">64 bytes from 192.168.23.1: icmp_seq=2 ttl=64 time=0.206 ms
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Make sure the the InfiniBand kernel modules are enabled.&lt;/strong>&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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> On both servers
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@ft2000:/# lsmod | grep ^ib
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_srpt 262144 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_isert 262144 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_iser 262144 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_umad 262144 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_ipoib 327680 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_cm 262144 3 rdma_cm,ib_ipoib,ib_srpt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_uverbs 327680 2 rdma_ucm,mlx5_ib
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">ib_core 458752 12 rdma_cm,ib_ipoib,rpcrdma,ib_srpt,iw_cm,ib_iser,ib_umad,ib_isert,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Make sure you have a lossless network:&lt;/strong>&lt;/p>
&lt;p>In case the RDMA is running over Ethernet (RoCE) you need to make sure that the network is configured to be loss-less, which means that either flow control (FC) or priority flow control PFC is enabled on the adapter ports and the switch.&lt;/p>
&lt;p>In case of lab environment or small setup, you can use Global Pause Flow Control to create loss-less environment. To check what is the global pause configuration use the following command (by default it is enabled normally).&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="go">root@t3640:/# ethtool -a enp1s0f0np0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Pause parameters for enp1s0f0np0:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Autonegotiate: off
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">RX: on
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">TX: on
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In case it is disabled, 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;/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@t3640:/# ethtool -A enp1s0f0np0 rx on tx on
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Test RDMA speed. Refer to my previous blog &lt;a class="link" href="https://charlie0129.github.io/blog/p/ib-perf-test/" >InfiniBand Performance Test&lt;/a> for details.&lt;/strong>&lt;/p>
&lt;p>Run the following command on client and server, respectively:&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">root@t3640:/# ib_send_bw --report_gbit -F -d mlx5_1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> device name can be seen in &lt;span class="sb">`&lt;/span>ibstatus&lt;span class="sb">`&lt;/span> &lt;span class="nb">command&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;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="go">root@ft2000:/# ib_send_bw --report_gbit -a -F -d mlx5_0 192.168.23.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"> Send BW Test
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Dual-port : OFF Device : mlx5_0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Number of qps : 1 Transport type : IB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Connection type : RC Using SRQ : OFF
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> PCIe relax order: ON
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> ibv_wr* API : ON
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> TX depth : 128
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> CQ Moderation : 100
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Mtu : 1024[B]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Link type : Ethernet
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> GID index : 3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Max inline data : 0[B]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rdma_cm QPs : OFF
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Data ex. method : Ethernet
&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"> local address: LID 0000 QPN 0x0087 PSN 0x176d37
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> GID: 00:00:00:00:00:00:00:00:00:00:255:255:192:168:23:01
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> remote address: LID 0000 QPN 0x0188 PSN 0x789e84
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> GID: 00:00:00:00:00:00:00:00:00:00:255:255:192:168:23:02
&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"> #bytes #iterations BW peak[Gb/sec] BW average[Gb/sec] MsgRate[Mpps]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> 65536 1000 23.14 23.13 0.044119
&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>As you can see, the speed is around 23 Gb/sec. This is the maximum speed of the network (25G). If you see a lower speed, you need to check the network configuration and the switch configuration.&lt;/p>
&lt;h2 id="configure-nfs-without-rdma">Configure NFS without RDMA
&lt;/h2>&lt;p>Just to make sure NFS is working.&lt;/p>
&lt;p>&lt;strong>Install NFS on server:&lt;/strong>&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">root@ft2000:/# yum -y install nfs-utils # On Debian: apt install nfs-kernel-server
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@ft2000:/# systemctl start nfs-server rpcbind
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@ft2000:/# systemctl enable nfs-server rpcbind
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Use &lt;code>/tmp&lt;/code> as a test dir and share it:&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>Since I want to test the network performance (TCP and RDMA), I don&amp;rsquo;t want to be bottlenecked by the disk speed. So I will use &lt;code>/tmp&lt;/code>, which is backed by RAM (usually), as a reasonably fast directory to avoid bottlenecks.&lt;/p>
&lt;p>Make sure it is &lt;code>tmpfs&lt;/code>, otherwise IO will be limited by your disk speed.&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">root@ft2000:/# mount | grep /tmp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">tmpfs on /tmp type tmpfs (rw,nosuid,nodev)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/blockquote>
&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">root@ft2000:/# echo &amp;#39;/tmp *(rw,async,insecure,no_root_squash)&amp;#39; &amp;gt;&amp;gt;/etc/exports
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Make it available:&lt;/strong>&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">root@ft2000:/# exportfs -a
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@ft2000:/# systemctl restart nfs-server
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Install NFS on client:&lt;/strong>&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">root@t3640:/# apt install nfs-common
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Mount the NFS share temporarily:&lt;/strong>&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">root@t3640:/# mkdir -p /mnt/nfs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@t3640:/# mount -t nfs 192.168.23.1:/tmp /mnt/nfs
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Verify the mount:&lt;/strong>&lt;/p>
&lt;p>Should see the files in &lt;code>/tmp&lt;/code> on the 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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@t3640:/# ls -l /mnt/nfs
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="configure-nfs-with-rdma">Configure NFS with RDMA
&lt;/h2>&lt;p>&lt;strong>Load RDMA transport module on the server:&lt;/strong>&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">root@ft2000:/# modprobe svcrdma
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> To make it permanent: &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;svcrdma&amp;#39;&lt;/span> &amp;gt;&amp;gt;/etc/modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@ft2000:/# systemctl restart nfs-server
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Instruct the server to listen on the RDMA transport port (20049 is the default port):&lt;/strong>&lt;/p>
&lt;p>Note: if you see &lt;code>echo: write error: protocol not supported&lt;/code>, it means that the NFSoRDMA is not supported in the Mellanox OFED. You need to use the inbox OS driver (refer to &lt;em>Install RDMA/IB dependencies&lt;/em> section in this blog) or re-install Mellanox OFED with &lt;code>--with-nfsrdma&lt;/code> flag. I was tripped by this issue.&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">root@ft2000:/# echo rdma 20049 &amp;gt; /proc/fs/nfsd/portlist
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> To make it permanent: &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;rdma=nfsrdma&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/nfs.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Load the RDMA transport module on the client&lt;/strong>&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">root@t3640:/# modprobe xprtrdma
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> To make it permanent: &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;xprtrdma&amp;#39;&lt;/span> &amp;gt;&amp;gt;/etc/modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@t3640:/# systemctl restart nfs-utils.service
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Mount the NFS share with RDMA:&lt;/strong>&lt;/p>
&lt;p>Before running the mount command, unmount the NFS share if it is mounted (&lt;code>umount /mnt/nfs&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;/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@t3640:/# mount -t nfs -o rdma,port=20049 192.168.23.1:/tmp /mnt/nfs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> To make it permanent: &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;192.168.23.1:/tmp /mnt/nfs nfs defaults,proto=rdma,port=20049 0 0&amp;#39;&lt;/span> &amp;gt;&amp;gt; /etc/fstab
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Check the mount parameters:&lt;/strong>&lt;/p>
&lt;p>If you see &lt;code>proto=rdma&lt;/code>, it means the NFS is mounted using RDMA.&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">root@t3640:/# nfsstat -m
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">/mnt/nfs from 192.168.23.1:/tmp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Flags: rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,proto=rdma,port=20049,timeo=600,retrans=2,sec=sys,clientaddr=192.168.23.2,local_lock=none,addr=192.168.23.1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Verify the mount:&lt;/strong>&lt;/p>
&lt;p>Should see the files in &lt;code>/tmp&lt;/code> on the 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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">root@t3640:/# ls -l /mnt/nfs
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="benchmark">Benchmark
&lt;/h2>&lt;p>Use &lt;code>fio&lt;/code> to benchmark the NFS performance. Parameters such as &lt;code>bs&lt;/code>, &lt;code>iodepth&lt;/code> will be changed to different values later to evaluate the performance in different scenarios.&lt;/p>
&lt;p>I will only test &lt;strong>random read&lt;/strong> performance since sequential read performance is not a main concern even using TCP let alone RDMA.&lt;/p>
&lt;p>The actual command will be:&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">root@t3640:/# fio --rw=randread --bs=4k --numjobs=1 --iodepth=1 --runtime=10 --time_based --loops=1 \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> --ioengine=libaio --direct=1 --invalidate=1 --fsync_on_close=1 --randrepeat=1 --norandommap \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> --exitall --name task1 --filename=/mnt/nfs/testfile --size=256M
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>The full log and related scripts can be found in the &lt;a class="link" href="#appendix" >Appendix&lt;/a>.&lt;/p>&lt;/blockquote>
&lt;p>Here are the results we have all been waiting for!&lt;/p>
&lt;p>For IO operations per second (IOPS), the average IOPS for NFS over RDMA is higher than NFS over TCP. The difference is more significant when the block size is larger and the most significant when &lt;code>iodepth=12&lt;/code>. We can also see that no matter the block size and iodepth, the IOPS for NFS over TCP is capped at around 47 KIOPS, while the one of RDMA is significantly higher at over 180 KIOPS.&lt;/p>
&lt;p>The reason why the difference is smaller with larger block sizes is that the network bandwidth is the bottleneck. For our 25GbE network, the maximum bandwidth is around 2800 MiB/s. We are hitting this limit with larger block sizes and this much IOPS. This is unrelated to the RDMA or TCP. If you have a faster network, you will see this difference for larger block sizes as well.&lt;/p>
&lt;p>For 4K block size:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>IO Depth&lt;/th>
&lt;th>KIOPS (RDMA)&lt;/th>
&lt;th>KIOPS (TCP)&lt;/th>
&lt;th>Ratio&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>8&lt;/td>
&lt;td>124.5&lt;/td>
&lt;td>36.1&lt;/td>
&lt;td>3.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>12&lt;/td>
&lt;td>156.0&lt;/td>
&lt;td>38.1&lt;/td>
&lt;td>4.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>16&lt;/td>
&lt;td>165.8&lt;/td>
&lt;td>41.4&lt;/td>
&lt;td>4.0&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>24&lt;/td>
&lt;td>177.0&lt;/td>
&lt;td>45.8&lt;/td>
&lt;td>3.9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>32&lt;/td>
&lt;td>179.4&lt;/td>
&lt;td>50.7&lt;/td>
&lt;td>3.5&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/nfs-over-rdma/plots/iops_avg.svg"
loading="lazy"
alt="IOPS vs Block Size for NFS over RDMA and TCP"
>
&lt;/p>
&lt;p>For bandwidth, the average bandwidth for NFS over RDMA is significantly higher (around 4x for small block sizes).&lt;/p>
&lt;p>The reason why the difference is more significant when the block size is small (4-16) is that we are limited by the network bandwidth. Since we have a 25GbE network, the maximum bandwidth is around 2800 MiB/s so you can see that the bandwidth is capped at 2800 MiB/s for large block sizes and deep IO depths. If you have a faster network, you will see this 4x difference for larger block sizes as well.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/nfs-over-rdma/plots/bw_avg.svg"
loading="lazy"
alt="Bandwidth vs Block Size for NFS over RDMA and TCP"
>
&lt;/p>
&lt;p>Latency-wise, the average latency for NFS over RDMA is slightly lower than NFS over TCP. The difference is more significant for small block size and large iodepths.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/nfs-over-rdma/plots/lat_avg.svg"
loading="lazy"
alt="Latency vs Block Size for NFS over RDMA and TCP"
>
&lt;/p>
&lt;h2 id="appendix">Appendix
&lt;/h2>&lt;ul>
&lt;li>Benchmark script &lt;a class="link" href="benchmark.sh" >benchmark.sh&lt;/a>: automatically run &lt;code>fio&lt;/code> with different &lt;code>bs&lt;/code> and &lt;code>iodepth&lt;/code> values and saves logs (it saves stdout, not the fio-generated log, e.g. &lt;code>--bandwidth-log&lt;/code>).&lt;/li>
&lt;li>Plotting script &lt;a class="link" href="plot.py" >plot.py&lt;/a>: parses the &lt;code>fio&lt;/code> logs (stdout) saved by &lt;code>benchmark.sh&lt;/code> and plots the results, which is the three figures you see above in the &lt;a class="link" href="#benchmark" >Benchmark&lt;/a> section.&lt;/li>
&lt;/ul>
&lt;details>
&lt;summary>Raw fio Logs (Click to Show)&lt;/summary>
&lt;ul>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth1.log" >rdma_fio_bs4k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth2.log" >rdma_fio_bs4k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth3.log" >rdma_fio_bs4k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth4.log" >rdma_fio_bs4k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth6.log" >rdma_fio_bs4k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth8.log" >rdma_fio_bs4k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth12.log" >rdma_fio_bs4k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth16.log" >rdma_fio_bs4k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth24.log" >rdma_fio_bs4k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth32.log" >rdma_fio_bs4k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth64.log" >rdma_fio_bs4k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs4k_iodepth128.log" >rdma_fio_bs4k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth1.log" >rdma_fio_bs8k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth2.log" >rdma_fio_bs8k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth3.log" >rdma_fio_bs8k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth4.log" >rdma_fio_bs8k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth6.log" >rdma_fio_bs8k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth8.log" >rdma_fio_bs8k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth12.log" >rdma_fio_bs8k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth16.log" >rdma_fio_bs8k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth24.log" >rdma_fio_bs8k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth32.log" >rdma_fio_bs8k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth64.log" >rdma_fio_bs8k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs8k_iodepth128.log" >rdma_fio_bs8k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth1.log" >rdma_fio_bs16k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth2.log" >rdma_fio_bs16k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth3.log" >rdma_fio_bs16k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth4.log" >rdma_fio_bs16k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth6.log" >rdma_fio_bs16k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth8.log" >rdma_fio_bs16k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth12.log" >rdma_fio_bs16k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth16.log" >rdma_fio_bs16k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth24.log" >rdma_fio_bs16k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth32.log" >rdma_fio_bs16k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth64.log" >rdma_fio_bs16k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs16k_iodepth128.log" >rdma_fio_bs16k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth1.log" >rdma_fio_bs32k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth2.log" >rdma_fio_bs32k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth3.log" >rdma_fio_bs32k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth4.log" >rdma_fio_bs32k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth6.log" >rdma_fio_bs32k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth8.log" >rdma_fio_bs32k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth12.log" >rdma_fio_bs32k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth16.log" >rdma_fio_bs32k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth24.log" >rdma_fio_bs32k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth32.log" >rdma_fio_bs32k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth64.log" >rdma_fio_bs32k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs32k_iodepth128.log" >rdma_fio_bs32k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth1.log" >rdma_fio_bs64k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth2.log" >rdma_fio_bs64k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth3.log" >rdma_fio_bs64k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth4.log" >rdma_fio_bs64k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth6.log" >rdma_fio_bs64k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth8.log" >rdma_fio_bs64k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth12.log" >rdma_fio_bs64k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth16.log" >rdma_fio_bs64k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth24.log" >rdma_fio_bs64k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth32.log" >rdma_fio_bs64k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth64.log" >rdma_fio_bs64k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs64k_iodepth128.log" >rdma_fio_bs64k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth1.log" >rdma_fio_bs128k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth2.log" >rdma_fio_bs128k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth3.log" >rdma_fio_bs128k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth4.log" >rdma_fio_bs128k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth6.log" >rdma_fio_bs128k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth8.log" >rdma_fio_bs128k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth12.log" >rdma_fio_bs128k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth16.log" >rdma_fio_bs128k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth24.log" >rdma_fio_bs128k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth32.log" >rdma_fio_bs128k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth64.log" >rdma_fio_bs128k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs128k_iodepth128.log" >rdma_fio_bs128k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth1.log" >rdma_fio_bs256k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth2.log" >rdma_fio_bs256k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth3.log" >rdma_fio_bs256k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth4.log" >rdma_fio_bs256k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth6.log" >rdma_fio_bs256k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth8.log" >rdma_fio_bs256k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth12.log" >rdma_fio_bs256k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth16.log" >rdma_fio_bs256k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth24.log" >rdma_fio_bs256k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth32.log" >rdma_fio_bs256k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth64.log" >rdma_fio_bs256k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs256k_iodepth128.log" >rdma_fio_bs256k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth1.log" >rdma_fio_bs512k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth2.log" >rdma_fio_bs512k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth3.log" >rdma_fio_bs512k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth4.log" >rdma_fio_bs512k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth6.log" >rdma_fio_bs512k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth8.log" >rdma_fio_bs512k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth12.log" >rdma_fio_bs512k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth16.log" >rdma_fio_bs512k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth24.log" >rdma_fio_bs512k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth32.log" >rdma_fio_bs512k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth64.log" >rdma_fio_bs512k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs512k_iodepth128.log" >rdma_fio_bs512k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth1.log" >rdma_fio_bs1024k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth2.log" >rdma_fio_bs1024k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth3.log" >rdma_fio_bs1024k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth4.log" >rdma_fio_bs1024k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth6.log" >rdma_fio_bs1024k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth8.log" >rdma_fio_bs1024k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth12.log" >rdma_fio_bs1024k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth16.log" >rdma_fio_bs1024k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth24.log" >rdma_fio_bs1024k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth32.log" >rdma_fio_bs1024k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth64.log" >rdma_fio_bs1024k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/rdma_fio_bs1024k_iodepth128.log" >rdma_fio_bs1024k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth1.log" >tcp_fio_bs4k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth2.log" >tcp_fio_bs4k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth3.log" >tcp_fio_bs4k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth4.log" >tcp_fio_bs4k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth6.log" >tcp_fio_bs4k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth8.log" >tcp_fio_bs4k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth12.log" >tcp_fio_bs4k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth16.log" >tcp_fio_bs4k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth24.log" >tcp_fio_bs4k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth32.log" >tcp_fio_bs4k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth64.log" >tcp_fio_bs4k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs4k_iodepth128.log" >tcp_fio_bs4k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth1.log" >tcp_fio_bs8k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth2.log" >tcp_fio_bs8k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth3.log" >tcp_fio_bs8k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth4.log" >tcp_fio_bs8k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth6.log" >tcp_fio_bs8k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth8.log" >tcp_fio_bs8k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth12.log" >tcp_fio_bs8k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth16.log" >tcp_fio_bs8k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth24.log" >tcp_fio_bs8k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth32.log" >tcp_fio_bs8k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth64.log" >tcp_fio_bs8k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs8k_iodepth128.log" >tcp_fio_bs8k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth1.log" >tcp_fio_bs16k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth2.log" >tcp_fio_bs16k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth3.log" >tcp_fio_bs16k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth4.log" >tcp_fio_bs16k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth6.log" >tcp_fio_bs16k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth8.log" >tcp_fio_bs16k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth12.log" >tcp_fio_bs16k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth16.log" >tcp_fio_bs16k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth24.log" >tcp_fio_bs16k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth32.log" >tcp_fio_bs16k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth64.log" >tcp_fio_bs16k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs16k_iodepth128.log" >tcp_fio_bs16k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth1.log" >tcp_fio_bs32k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth2.log" >tcp_fio_bs32k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth3.log" >tcp_fio_bs32k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth4.log" >tcp_fio_bs32k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth6.log" >tcp_fio_bs32k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth8.log" >tcp_fio_bs32k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth12.log" >tcp_fio_bs32k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth16.log" >tcp_fio_bs32k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth24.log" >tcp_fio_bs32k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth32.log" >tcp_fio_bs32k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth64.log" >tcp_fio_bs32k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs32k_iodepth128.log" >tcp_fio_bs32k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth1.log" >tcp_fio_bs64k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth2.log" >tcp_fio_bs64k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth3.log" >tcp_fio_bs64k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth4.log" >tcp_fio_bs64k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth6.log" >tcp_fio_bs64k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth8.log" >tcp_fio_bs64k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth12.log" >tcp_fio_bs64k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth16.log" >tcp_fio_bs64k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth24.log" >tcp_fio_bs64k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth32.log" >tcp_fio_bs64k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth64.log" >tcp_fio_bs64k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs64k_iodepth128.log" >tcp_fio_bs64k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth1.log" >tcp_fio_bs128k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth2.log" >tcp_fio_bs128k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth3.log" >tcp_fio_bs128k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth4.log" >tcp_fio_bs128k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth6.log" >tcp_fio_bs128k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth8.log" >tcp_fio_bs128k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth12.log" >tcp_fio_bs128k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth16.log" >tcp_fio_bs128k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth24.log" >tcp_fio_bs128k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth32.log" >tcp_fio_bs128k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth64.log" >tcp_fio_bs128k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs128k_iodepth128.log" >tcp_fio_bs128k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth1.log" >tcp_fio_bs256k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth2.log" >tcp_fio_bs256k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth3.log" >tcp_fio_bs256k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth4.log" >tcp_fio_bs256k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth6.log" >tcp_fio_bs256k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth8.log" >tcp_fio_bs256k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth12.log" >tcp_fio_bs256k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth16.log" >tcp_fio_bs256k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth24.log" >tcp_fio_bs256k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth32.log" >tcp_fio_bs256k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth64.log" >tcp_fio_bs256k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs256k_iodepth128.log" >tcp_fio_bs256k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth1.log" >tcp_fio_bs512k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth2.log" >tcp_fio_bs512k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth3.log" >tcp_fio_bs512k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth4.log" >tcp_fio_bs512k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth6.log" >tcp_fio_bs512k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth8.log" >tcp_fio_bs512k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth12.log" >tcp_fio_bs512k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth16.log" >tcp_fio_bs512k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth24.log" >tcp_fio_bs512k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth32.log" >tcp_fio_bs512k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth64.log" >tcp_fio_bs512k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs512k_iodepth128.log" >tcp_fio_bs512k_iodepth128.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth1.log" >tcp_fio_bs1024k_iodepth1.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth2.log" >tcp_fio_bs1024k_iodepth2.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth3.log" >tcp_fio_bs1024k_iodepth3.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth4.log" >tcp_fio_bs1024k_iodepth4.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth6.log" >tcp_fio_bs1024k_iodepth6.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth8.log" >tcp_fio_bs1024k_iodepth8.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth12.log" >tcp_fio_bs1024k_iodepth12.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth16.log" >tcp_fio_bs1024k_iodepth16.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth24.log" >tcp_fio_bs1024k_iodepth24.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth32.log" >tcp_fio_bs1024k_iodepth32.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth64.log" >tcp_fio_bs1024k_iodepth64.log&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="logs/tcp_fio_bs1024k_iodepth128.log" >tcp_fio_bs1024k_iodepth128.log&lt;/a>&lt;/li>
&lt;/ul>
&lt;/details></description></item><item><title>Flash 10G CX4121A to 25G</title><link>https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/</link><pubDate>Fri, 19 Jul 2024 16:37:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/</guid><description>&lt;p>10G ConnectX-4 Lx NICs can be flashed to 25G with the right firmware. This is useful if you have a 10G NIC and want to upgrade to 25G without buying a new NIC. This article will guide you through the process of flashing a 10G Mellanox ConnectX-4 Lx NIC to 25G.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g.jpg"
width="4032"
height="3024"
srcset="https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_e67c9199f79997b.webp 480w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_f473233eaea7608d.jpg 480w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_c3aef0b072ec866.webp 1024w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_dc742250568c7fee.jpg 1024w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_f66edbb97499b063.webp 1536w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_78d6e0bb25964f66.jpg 1536w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_91d79469bdb58ec7.webp 2048w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_30a548030205647.jpg 2048w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_4a93a94a834c4b11.webp 2560w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_da6e88a334cf467c.jpg 2560w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_be28c42d38efe826.webp 4032w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-10g_hu_b51529c87b4602cd.jpg 4032w"
loading="lazy"
alt="10G ConnectX-4 Lx"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g.jpg"
width="3455"
height="2591"
srcset="https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_56e8aacbea38e6dd.webp 480w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_8e6b89cef8aee16d.jpg 480w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_ed897d5a8fd11782.webp 1024w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_25c0489be6304c6c.jpg 1024w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_7763f81dd3853a26.webp 1536w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_a70cb4540c546c23.jpg 1536w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_39f701fb24c38667.webp 2048w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_d0e4c3fbcae55987.jpg 2048w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_244c2634c4e6d6e.webp 2560w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_2b581b867a6ad0ea.jpg 2560w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_fa581e091ddafd5b.webp 3455w, https://charlie0129.github.io/blog/p/mcx4121a-10g-to-25g/images/cx4121a-25g_hu_581adf59df5a6c23.jpg 3455w"
loading="lazy"
alt="25G ConnectX-4 Lx"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>Warning: Do not try this on MCX4121C. It will brick the NIC.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Download firmware from &lt;a class="link" href="https://network.nvidia.com/support/firmware/connectx4lxen/" target="_blank" rel="noopener"
>Firmware for ConnectX®-4 Lx EN&lt;/a>. Choose &lt;code>MCX4121A-ACUT&lt;/code>. I will assume the firmware file is named &lt;code>fw-ConnectX4Lx-rel-14_32_1010-MCX4121A-ACU_Ax-UEFI-14.25.17-FlexBoot-3.6.502.bin&lt;/code>.&lt;/p>
&lt;p>If you are wondering what&amp;rsquo;s the difference between models, here&amp;rsquo;s a table for ConnectX-4 Lx cards (&lt;a class="link" href="files/pb-connectx-4-lx-en-card.pdf" >original document&lt;/a>). You should choose 25G models to flash to.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Max Network Speed&lt;/th>
&lt;th>Interface Type&lt;/th>
&lt;th>Supported Ethernet Speeds (GbE)&lt;/th>
&lt;th>Host Interface&lt;/th>
&lt;th>Additional Features&lt;/th>
&lt;th>OPN&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1x 10GbE&lt;/td>
&lt;td>SFP28&lt;/td>
&lt;td>10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>&lt;/td>
&lt;td>MCX4111A-XCAT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2x 10GbE&lt;/td>
&lt;td>SFP28&lt;/td>
&lt;td>10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>&lt;/td>
&lt;td>MCX4121A-XCAT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2x 10GbE&lt;/td>
&lt;td>SFP28&lt;/td>
&lt;td>10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>Host Management, UEFI Enabled&lt;/td>
&lt;td>MCX4121A-XCHT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1x 25GbE&lt;/td>
&lt;td>SFP28&lt;/td>
&lt;td>25, 10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>&lt;/td>
&lt;td>MCX4111A-ACAT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1x 25GbE&lt;/td>
&lt;td>SFP28&lt;/td>
&lt;td>25, 10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>UEFI Enabled&lt;/td>
&lt;td>MCX4111A-ACUT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2x 25GbE&lt;/td>
&lt;td>SFP28&lt;/td>
&lt;td>25, 10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>&lt;/td>
&lt;td>MCX4121A-ACAT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2x 25GbE&lt;/td>
&lt;td>SFP28&lt;/td>
&lt;td>25, 10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>UEFI Enabled&lt;/td>
&lt;td>MCX4121A-ACUT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1x 40GbE&lt;/td>
&lt;td>QSFP28&lt;/td>
&lt;td>40, 25, 10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>&lt;/td>
&lt;td>MCX4131A-BCAT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1x 50GbE&lt;/td>
&lt;td>QSFP28&lt;/td>
&lt;td>50, 40, 25, 10, 1&lt;/td>
&lt;td>PCIe 3.0 x8&lt;/td>
&lt;td>&lt;/td>
&lt;td>MCX4131A-GCAT&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/li>
&lt;li>
&lt;p>Download and install &lt;a class="link" href="https://network.nvidia.com/products/adapter-software/firmware-tools/" target="_blank" rel="noopener"
>NVIDIA Firmware Tools (MFT)&lt;/a>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>mst start&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>mst ststus&lt;/code> You will see you MST device, e.g. &lt;code>/dev/mst/mt4117_pciconf0&lt;/code>. I will use this device path in this article. If your path differs from this, please change it accordingly.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Save GUID, MAC: &lt;code>flint -d /dev/mst/mt4117_pciconf0 query full &amp;gt; flint_query.txt&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Save hardware info: &lt;code>flint -d /dev/mst/mt4117_pciconf0 hw query &amp;gt; flint_hwinfo.txt&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Save current firmware: &lt;code>flint -d /dev/mst/mt4117_pciconf0 ri orig_firmware.bin&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Save current firmware config: &lt;code>flint -d /dev/mst/mt4117_pciconf0 dc orig_firmware.ini&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Save current PXE ROM (if exists): &lt;code>flint -d /dev/mst/mt4117_pciconf0 rrom orig_rom.bin&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Save current PCI VPD: &lt;code>mlxburn -d /dev/mst/mt4117_pciconf0 -vpd &amp;gt; orig_vpd.txt&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>flint -i fw-ConnectX4Lx-rel-14_32_1010-MCX4121A-ACU_Ax-UEFI-14.25.17-FlexBoot-3.6.502.bin verify&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>flint -i fw-ConnectX4Lx-rel-14_32_1010-MCX4121A-ACU_Ax-UEFI-14.25.17-FlexBoot-3.6.502.bin -d /dev/mst/mt4117_pciconf0 -allow_psid_change burn&lt;/code>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Reboot you machine. You should see your NIC is now have 25G speed.&lt;/p>
&lt;p>Bonus:&lt;/p>
&lt;ul>
&lt;li>To change MAC address: &lt;code>flint -d /dev/mst/mt4117_pciconf0 -mac 02c90abcdef0 sg&lt;/code>&lt;/li>
&lt;li>To change GUID: &lt;code>flint -d /dev/mst/mt4117_pciconf0 -guid 0002c9000abcdef0 sg&lt;/code>&lt;/li>
&lt;/ul></description></item><item><title>InfiniBand Performance Test</title><link>https://charlie0129.github.io/blog/p/ib-perf-test/</link><pubDate>Fri, 19 Jul 2024 11:10:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/ib-perf-test/</guid><description>&lt;p>We have a 200Gbps InfiniBand network among about 100 nodes, connected using &lt;em>NVIDIA ConnectX-7 NICs (HDR200)&lt;/em> to a &lt;em>NVIDIA MQM9700&lt;/em> switch. Each OSFP twin port (2xNDR, 2x400Gbps) on &lt;em>QM9700&lt;/em> is splitted into 4xNDR200 ports to connect to 4x&lt;em>NVIDIA ConnectX-7 NICs&lt;/em> using 2xNDR to 4xNDR200 DAC/ACC (OSFP to 4x OSFP).&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/ib-perf-test/images/topo.png"
width="2020"
height="1008"
srcset="https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_72e9afa7913190fb.webp 480w, https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_109f3449fa4d8720.jpg 480w, https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_ffa04b394bad063d.webp 1024w, https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_593e2cbffc5a91a6.jpg 1024w, https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_e2adfdb5ca88bf30.webp 1536w, https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_133ab95db94b548.jpg 1536w, https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_ca7e03894ccd3f51.webp 2020w, https://charlie0129.github.io/blog/p/ib-perf-test/images/topo_hu_552338335ed791f9.jpg 2020w"
loading="lazy"
alt="Topology"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="480px"
>
&lt;/p>
&lt;p>We want to test the performance of the network between 2 nodes just to make sure it is working. We expect to see a bandwidth of around 200Gbps.&lt;/p>
&lt;p>Check the status of the Infiniband devices. Make sure at least one link is up (&lt;code>phys state: 5: LinkUp&lt;/code>) on both nodes.&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">root@tj01-h20-node139:/# ibstatus
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Infiniband device &amp;#39;mlx5_7&amp;#39; port 1 status:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> default gid: fe80:0000:0000:0000:&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> base lid: 0x6c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> sm lid: 0x4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> state: 4: ACTIVE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> phys state: 5: LinkUp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> rate: 200 Gb/sec (2X NDR)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> link_layer: InfiniBand
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Check if both nodes are connected to the Subnet Manager. You should see the name of both nodes appear on the list.&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;/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@tj01-h20-node139:/# ibnetdiscover
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Topology file: generated on Fri Jul 19 02:49:25 2024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Initiated from node &amp;lt;redacted&amp;gt; port &amp;lt;redacted&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&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">vendid=0x2c9
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">devid=0xd2f2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">sysimgguid=0x9c05&amp;lt;redacted&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">switchguid=0x9c05&amp;lt;redacted&amp;gt;(9c05&amp;lt;redacted&amp;gt;)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Switch 129 &amp;#34;S-9c05&amp;lt;redacted&amp;gt;&amp;#34; # &amp;#34;MF0;CNTSN-POD229-QM97-IB-02:MQM9700/U1&amp;#34; enhanced port 0 lid 1 lmc 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">[1] &amp;#34;H-b83f&amp;lt;redacted&amp;gt;&amp;#34;[1](b83f&amp;lt;redacted&amp;gt;) # &amp;#34;tj01-4090-node005 mlx5_2&amp;#34; lid 22 2xNDR
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">[2] &amp;#34;H-946d&amp;lt;redacted&amp;gt;&amp;#34;[1](946d&amp;lt;redacted&amp;gt;) # &amp;#34;tj01-4090-node004 mlx5_2&amp;#34; lid 27 2xNDR
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">[3] &amp;#34;H-a088&amp;lt;redacted&amp;gt;&amp;#34;[1](a088&amp;lt;redacted&amp;gt;) # &amp;#34;tj01-4090-node006 mlx5_2&amp;#34; lid 10 2xNDR
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">[4] &amp;#34;H-946d&amp;lt;redacted&amp;gt;&amp;#34;[1](946d&amp;lt;redacted&amp;gt;) # &amp;#34;tj01-4090-node007 mlx5_2&amp;#34; lid 12 2xNDR
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">[6] &amp;#34;H-946d&amp;lt;redacted&amp;gt;&amp;#34;[1](946d&amp;lt;redacted&amp;gt;) # &amp;#34;tj01-4090-node009 mlx5_2&amp;#34; lid 11 2xNDR
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">...omitted
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Make sure &lt;code>perftest&lt;/code> is installed.&lt;/p>
&lt;p>On one node:&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">root@tj01-4090-node099:/# ib_send_bw --report_gbit -a -F -d mlx5_2
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>--report_gbit&lt;/code>: Show result in gigabit.&lt;/li>
&lt;li>&lt;code>-a&lt;/code>: Run sizes from 2 till 2^23.&lt;/li>
&lt;li>&lt;code>-F&lt;/code>: Do not show a warning even if cpufreq_ondemand module is loaded, and cpu-freq is not on max.&lt;/li>
&lt;li>&lt;code>-d&lt;/code>: Use IB device. To make sure you are using the device you want to test (if you have multiple IB devices like me).&lt;/li>
&lt;/ul>
&lt;p>On another node:&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">root@tj01-h20-node139:/# ib_send_bw --report_gbit -a -F -d mlx5_2 tj01-4090-node099
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>tj01-4090-node099&lt;/code> is the IP address (hostname) of the other node.&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">---------------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Send BW Test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Dual-port : OFF Device : mlx5_2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Number of qps : 1 Transport type : IB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Connection type : RC Using SRQ : OFF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> PCIe relax order: ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ibv_wr* API : ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TX depth : 128
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CQ Moderation : 100
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Mtu : 4096[B]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Link type : IB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Max inline data : 0[B]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rdma_cm QPs : OFF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Data ex. method : Ethernet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> local address: LID 0x6c QPN 0x0050 PSN 0xddd611
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> remote address: LID 0x28 QPN 0x0050 PSN 0xa3c746
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> #bytes #iterations BW peak[Gb/sec] BW average[Gb/sec] MsgRate[Mpps]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 1000 0.052812 0.050794 3.174618
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 1000 0.080899 0.080547 2.517093
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8 1000 0.17 0.17 2.624748
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16 1000 0.35 0.35 2.741373
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 32 1000 0.70 0.70 2.736427
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 64 1000 1.62 1.62 3.159528
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 128 1000 3.07 3.07 2.994503
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 256 1000 5.46 5.46 2.663988
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 512 1000 13.24 13.23 3.230121
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1024 1000 26.58 26.56 3.241834
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2048 1000 51.80 51.75 3.158744
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4096 1000 82.15 82.11 2.505647
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8192 1000 89.37 89.33 1.363014
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16384 1000 89.01 87.24 0.665597
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 32768 1000 172.67 96.54 0.368268
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 65536 1000 181.95 108.83 0.207586
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 131072 1000 181.82 137.78 0.131399
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 262144 1000 187.32 163.15 0.077797
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 524288 1000 190.12 179.08 0.042696
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1048576 1000 191.18 187.28 0.022326
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2097152 1000 192.69 192.69 0.011485
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4194304 1000 195.53 195.53 0.005827
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8388608 1000 196.69 196.69 0.002931
&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></description></item><item><title>macOS Nix First Taste</title><link>https://charlie0129.github.io/blog/p/macos-nix-first-taste/</link><pubDate>Thu, 27 Jun 2024 15:13:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/macos-nix-first-taste/</guid><description>&lt;h2 id="note">Note
&lt;/h2>&lt;p>Note that this blog currently acts as a reference for me. It is not a comprehensive guide on why and how to use Nix on macOS.&lt;/p>
&lt;p>I chose nix because I am tired of Homebrew&amp;rsquo;s slowness and NOT PROVIDING BOTTLES (BINARY) FOR OLD MACOS VERSIONS.&lt;/p>
&lt;h2 id="install">Install
&lt;/h2>&lt;p>I am not using the official installer because it cannot survive macOS updates. Instead, I am using the Determinate System&amp;rsquo;s Nix 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;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="nv">ARCH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>uname -m &lt;span class="p">|&lt;/span> sed &lt;span class="s1">&amp;#39;s/arm64/aarch64/&amp;#39;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>uname -s &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;[:upper:]&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;[:lower:]&amp;#39;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -L -o nix-installer https://github.com/DeterminateSystems/nix-installer/releases/latest/download/nix-installer-&lt;span class="nv">$ARCH&lt;/span>-&lt;span class="nv">$OS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod +x nix-installer
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Optionally, move nix-installer to your PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># sudo install nix-installer /usr/local/bin&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./nix-installer install --explain
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="pin-nixpkgs">Pin Nixpkgs
&lt;/h2>&lt;p>I don&amp;rsquo;t want to download a tarball and extract it every now and then. I know you can set tarball-ttl to a higher value, but I don&amp;rsquo;t want to do that either. Just pin nixpkgs.&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">nix registry pin nixpkgs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you are like me in China, you may want to use a mirror.&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="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;substituters = https://mirrors.sjtug.sjtu.edu.cn/nix-channels/store/ https://cache.nixos.org/&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> sudo tee -a /etc/nix/nix.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="using">Using
&lt;/h2>&lt;p>I wrote some scripts just to make my life easier.&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/charlie0129/dotfiles/blob/master/bin/common/nix-install" target="_blank" rel="noopener"
>nix-install&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="htts://github.com/charlie0129/dotfiles/blob/master/bin/common/nix-list" >nix-list&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="htts://github.com/charlie0129/dotfiles/blob/master/bin/common/nix-search" >nix-search&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="htts://github.com/charlie0129/dotfiles/blob/master/bin/common/nix-uninstall" >nix-uninstall&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="later-steps">Later Steps
&lt;/h2>&lt;p>There is &lt;a class="link" href="https://github.com/LnL7/nix-darwin" target="_blank" rel="noopener"
>nix-darwin&lt;/a> to make declarative configuration easier. I don&amp;rsquo;t have time to try it yet but I will definitely give it a try when I have time.&lt;/p></description></item><item><title>高速做种指南</title><link>https://charlie0129.github.io/blog/p/high-speed-seeding-guide/</link><pubDate>Wed, 19 Jun 2024 22:11:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/high-speed-seeding-guide/</guid><description>&lt;h2 id="前言">前言
&lt;/h2>&lt;p>由于最近接入了 10Gbps 网络，发现在 PT 做种时，传输速度并没有达到预期的速度，于是优化了一波。本文记录了一些针对高速做种的建议、注意点和技巧。&lt;/p>
&lt;h2 id="硬件条件">硬件条件
&lt;/h2>&lt;h3 id="足够快的网络">足够快的网络
&lt;/h3>&lt;p>做种本质上就是网络传输，快速的网络是做种的基础。&lt;/p>
&lt;h4 id="确保上传带宽">确保上传带宽
&lt;/h4>&lt;p>做种嘛，主要是给别人传输数据，吃的是你的上传带宽，跟下载几乎没有关系，这是最重要的。例如原本 50Mbps 的上传情况下，买一条 100Mbps 的宽带比以下所有的优化都重要。&lt;/p>
&lt;p>但是在中国，一般来说家庭宽带的上行速度都是比较低的。例如，电信的 1000M 宽带，上行速度一般不会超过 100Mbps，通常在 50Mbps 左右。这样的话，你的上传速度就被限制在 6.25MB/s 左右，在这个速度下，其实最主要的瓶颈在于你的上传带宽上，不太需要下面说的硬件/软件优化🫠。你可能需要找不少资源来找到高上传带宽的宽带，例如某些地区的移动宽带会给 IPv6 相当大的上传。你还要注意如何不被运营商认为是 PCDN 而封你的号。&lt;/p>
&lt;p>由于本文聊的是高速做种，针对的是接近 1Gbps 的上传带宽的用户。国外的情况暂且不讨论（高速宽带价格更低，很容易购买到超过 1Gbps 的云服务器、seeding box 等），在国内，拥有高速上传带宽的用户主要有几种情况：&lt;/p>
&lt;ul>
&lt;li>企业用户（例如大型公司、数据中心等），使用价格高昂，上下对等（即下载和上传速度一样快），租云服务器也属于这个范畴；&lt;/li>
&lt;li>高校用户，使用价格较低（对用户来说，对学校来说不是这回事），上下对等；&lt;/li>
&lt;li>其他特殊用户，例如某些地区的移动家庭宽带，500M 宽带上行可以达到 200M。&lt;/li>
&lt;/ul>
&lt;p>如果你是这些用户，那么你的上传带宽可能会达到 1Gbps 甚至更高，这时候就需要考虑下面的硬件/软件优化了。不过估计阅读本文的人大部分是高校教育网用户，低廉的价格超大的上传带宽（&lt;/p>
&lt;p>在本文中，我的接入网络为上下 10Gbps 的教育网 CERNET 对等接入。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet.jpg"
width="4032"
height="3024"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_6ac1c937881d3925.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_767c108a59fde344.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_83dc83859f5e5c2a.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_f5250ca49a0565e3.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_8f0e8942732e023a.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_50f707ef55337ac8.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_1d48a8e9598cc9bb.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_95d49a8c1453da20.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_86ce4cb85657c3aa.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_47d74685617f1d3b.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_ac200fdf6fa3e4d6.webp 4032w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/internet_hu_a16cf42efcc048a5.jpg 4032w"
loading="lazy"
alt="10G 网络接入"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;h4 id="外界可联通">外界可联通
&lt;/h4>&lt;p>大部分情况下，你做种时是别人主动连接你的，所以你的网络需要是可被连接的。&lt;/p>
&lt;p>对于 IPv4 来说：&lt;/p>
&lt;ul>
&lt;li>最好的情况是你拥有一个独立的公网 IP ，这是最简单的情况。如果公网 IP 在你的网卡上，那么你不需要做任何设置。如果公网 IP 在你家的光猫上，那么通过简单的端口映射/UPnP 等就能完成。这种情况国内不常见。&lt;/li>
&lt;li>其次如果你的网络是 NAT1（全锥形 NAT） 的（指国内运营商级 NAT，一般你的光猫上能获取 100.x.x.x 这样的 IP），那么通过一些复杂的奇技淫巧（例如 UDP hole punching）也能实现可被连接。&lt;/li>
&lt;li>最差的情况是你的网络是 NAT4（对称形 NAT） 的，这种情况下，你的网络是不可被连接的，除非你使用一些中继服务器来帮助你穿透。不过这种情况下，因为中继服务器的带宽有限，通常不能达到高速做种的要求，这里不讨论。&lt;/li>
&lt;/ul>
&lt;p>如何判断你是 NAT1 还是 NAT4 ？你可以使用 &lt;a class="link" href="https://github.com/talkiq/pystun3" target="_blank" rel="noopener"
>pystun3&lt;/a>。&lt;/p>
&lt;p>如果你使用 IPv6 做种（常见于教育网），运营商（学校）下发的 IPv6 地址一般都是公网 IP，不需要做任何设置，等同于情况一。&lt;/p>
&lt;p>如果你不确定自己的网络是否可被连接，可以阅读文末注意点。&lt;/p>
&lt;h4 id="peering">Peering
&lt;/h4>&lt;p>在数 Gbps 的高速网络下，你所在的运营商网络和你的种子的 peer 之间的连通性也值得考虑（但没那么重要）。运营商各张网之间，例如电信的 163 骨干网，教育网 CERNET ，甚至国外的运营商等，他们之间互联需要走 BGP ，如果你和你需要连接的用户之间连通性不好，那么你的上传速度可能会受到影响。&lt;/p>
&lt;p>国内一般不需要考虑，这种一般在跨国传输的情况下比较明显，如果你所在的网的国际出口带宽不够，连接国外的 peer 时你会发现你的上传速度上不去。&lt;/p>
&lt;h3 id="足够快的硬盘">足够快的硬盘
&lt;/h3>&lt;p>重要性仅次于网络的是硬盘速度，尤其关注 &lt;strong>随机读&lt;/strong>。一种常见的错误认知是：我的机械硬盘顺序读能有 200MiB/s 那么我就能吃满 1Gbps 的上传带宽应该轻轻松松吧。这是完全错误的，由于做种会产生大量的 &lt;strong>随机读&lt;/strong> ，机械硬盘在这方面是最弱鸡的。&lt;/p>
&lt;p>例如，在 1Gbps 左右上传，大文件做种（活跃数据超过我的内存大小 128GB ）的情况下，能稳定 10k IOPS 直达我的磁盘的读取（例如下图 14k IOPS 的读）。一块 7200 RPM 的 SATA 机械硬盘也就 100 IOPS ，一块 15000RPM 的 SAS 机械硬盘也就 200 IOPS 左右，这有着快 2 个数量级的差距。所以，如果你的硬盘是机械硬盘（HDD, spinning rust :p），建议放弃治疗，一旦内存缓存不够了你的上传速度就 gg 了，这辈子都不可能吃满 1Gbps 的，500Mbps 都够呛。根据我的经验， 3*8TB 的 7200RPM 企业级 HDD RAID5 在活跃数据超过内存缓存（128GB）的情况下也就勉勉强强能吃 200Mbps 的上传带宽（此时磁盘已经 100% busy 了），如果缓存更小的话这个数值甚至更低。&lt;/p>
&lt;p>注意：这里不考虑完全使用内存缓存（因为大部分人都没有几百 GB 的内存）一旦种子活跃数据超过可用的内存缓存大小，硬盘就是你的上传速度的瓶颈。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iops.png"
width="958"
height="214"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iops_hu_a62be9a100d0d422.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iops_hu_1c7bb2b6c6347f1c.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iops_hu_8372a631afe5c126.webp 958w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iops_hu_fb7d4a04fe75413b.jpg 958w"
loading="lazy"
alt="1Gbps 上传时 14k IOPS 的读请求"
class="gallery-image"
data-flex-grow="447"
data-flex-basis="1074px"
>
&lt;/p>
&lt;p>&lt;strong>所以高速做种的场景下一定要用 SSD ，最次也需要是 SATA SSD，建议使用更高性能的 NVME SSD 。&lt;/strong>&lt;/p>
&lt;p>本文使用了 3 块 Micron 5400 PRO 1.92TB 的 SSD 组成的 RAID5 。注意，这对于我的网络接入（10 Gbps）的情况下仍然是远远不够的，正常需要高性能 NVME SSD 才行。不过我大部分情况也就 1Gbps 左右，跑不满 10Gbps ，凑合用吧。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd.jpg"
width="4032"
height="3024"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_71768682229bc521.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_d6fee8c78334f34b.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_c001fde81315af0a.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_eaf6ee13d78ed765.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_124fb4010895bbdc.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_6402bceeb8d010e2.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_b9ce0b7a150e838e.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_9654e30bca52624.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_1c9820c7300efd22.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_bc736ac201db4abb.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_cd89dc8be46fa548.webp 4032w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ssd_hu_c9b36e0dd3f1b2c7.jpg 4032w"
loading="lazy"
alt="3*1.92TB SSD"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;p>如果你不确定你的硬盘是否足够快，那么你可以阅读下文判断磁盘 IO 瓶颈的部分。&lt;/p>
&lt;h3 id="足够大的内存">足够大的内存
&lt;/h3>&lt;p>足够大的内存可以提供：&lt;/p>
&lt;ul>
&lt;li>更大的磁盘缓存，减少磁盘压力（高速上传时，SSD 也不一定能跟上）；&lt;/li>
&lt;li>更大的 send buffer ，提高网络传输效率/IO 吞吐；&lt;/li>
&lt;/ul>
&lt;p>一般来说 32GB 左右能够满足需求，本文中使用了 128GB 内存测试。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram.jpg"
width="3798"
height="2848"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_c191358532ddc4c2.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_9d471c4430e49edf.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_43fe4b0fef2e6b69.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_402892627914cb2.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_52d2d5e40026f648.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_d724384199af9004.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_2afcdd7fdb60b33.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_bc29c00f9d8ef12d.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_6d02889ac6f6666c.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_63f9f9dddf15c0b4.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_92a17f68f1a78e67.webp 3798w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ram_hu_3fd62f22e407ab85.jpg 3798w"
loading="lazy"
alt="4*32G 内存"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;h3 id="足够快的网卡">足够快的网卡
&lt;/h3>&lt;p>更好的网卡能够提供更多的 hardware offloading ，减轻 CPU 的负担，提高网络传输效率。一些高端网卡也支持更多的 queue ，提高并发传输能力，例如 CX-4 Lx 支持 64 个 queue ，可以看到这些 queue 上的收发包表示它们在被积极利用（实际上一般软件无法同时用不到这么多，包括 qBittorrent，所以 8 队列的 I350 就够了）。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue.png"
width="2740"
height="1586"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_87ce5e9f2b42c91c.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_62a5a3e65a27f316.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_5b4bbc9df213a117.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_b2a214ba7c5f5ee8.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_542f8196789379d5.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_cf6cd8609c59c3fc.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_bc6b7143feecacd7.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_48256ba81691ab76.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_5652cdd9c2b6a3f8.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_a7833ed4ebdb1735.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_d9217edb15a1e7b7.webp 2740w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/cx4lxqueue_hu_17635e13d599281b.jpg 2740w"
loading="lazy"
alt="网卡队列"
class="gallery-image"
data-flex-grow="172"
data-flex-basis="414px"
>
&lt;/p>
&lt;p>最差也要使用 Intel 的 Gigabit 网卡，例如 I350 、I210 ，更好的可以用 Intel X520/X540 ，甚至 Mellanox ConnectX-4/5/6 等更高端的网卡。不要用 Realtek （小螃蟹）的，在大连接数吃满 1Gbps 的情况下性能很烂。&lt;/p>
&lt;p>例如下图是我的计算机在 700Mbps 下行 1.2Gbps 上行时每秒的收发包数量，约为 170 Kpps 。性能差的网卡是很难达到这个性能的。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps.png"
width="1670"
height="1032"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_77807694310453f9.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_fddbc5eb561ac6c8.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_55fbbdf92302ed4b.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_a5c85b55ef82325b.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_5d7e43d87ebf74d3.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_b40e6c40d339d6fb.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_475920fadb32cc1e.webp 1670w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/kpps_hu_95bc7e52a0b5118f.jpg 1670w"
loading="lazy"
alt="1.5Gbps 时每秒的收发包数量"
class="gallery-image"
data-flex-grow="161"
data-flex-basis="388px"
>
&lt;/p>
&lt;p>本文中使用了 Mellanox ConnectX-4 Lx 25G (CX4121A) 网卡（虽然现在被 NVIDIA 收购了，不过我还是习惯称之为 Mellanox ）。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic.jpg"
width="4032"
height="3024"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_1849ad8ebc2844d1.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_10f8f6bb31259317.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_c863a182290b84f4.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_2264a97cb298ba32.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_b34ed9be158631ed.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_dda5cd1ddcbab175.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_6268e08092eb7de0.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_b19c563ca0f327b3.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_6d8a8d1d0119eb94.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_32cee5281466fa60.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_7d3c99e43441cdc6.webp 4032w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/nic_hu_38190a6091d312bd.jpg 4032w"
loading="lazy"
alt="Mellanox ConnectX-4 Lx 25G"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
>
&lt;/p>
&lt;h3 id="足够快的-cpu">足够快的 CPU
&lt;/h3>&lt;p>一个正常的现代 CPU 即可，例如 Intel Core i5-12500 。请不要使用树莓派、老掉牙的 CPU（例如 Intel Xeon E5-2403 v2 这种 4C4T 1.8GHz 的垃圾），这种情况下，你的 CPU 仍然是整个系统的瓶颈，特别是在高速上传时，CPU 的负载会很高。网卡越烂，上传速度越快，你就需要越好的 CPU 来处理。&lt;/p>
&lt;p>本文中使用了国产的飞腾 2000+ ，虽然看起来有 64 个 ARM64 核心，但是性能还是比较烂的，不太够说实话，用起来跟只有 10 个核的 i9-10900K 也差的比较多。&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;/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">Architecture: aarch64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">CPU op-mode(s): 64-bit
&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">Address sizes: 44 bits physical, 48 bits virtual
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">CPU(s): 64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">On-line CPU(s) list: 0-63
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thread(s) per core: 1
&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): 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node(s): 8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Vendor ID: Phytium
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">BIOS Vendor ID: Phytium
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Model: 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Model name: FTC662
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">BIOS Model name: FT-2000+/64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Stepping: 0x1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">BogoMIPS: 100.00
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">L1d cache: 2 MiB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">L1i cache: 2 MiB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">L2 cache: 512 MiB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node0 CPU(s): 0-7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node1 CPU(s): 8-15
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node2 CPU(s): 16-23
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node3 CPU(s): 24-31
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node4 CPU(s): 32-39
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node5 CPU(s): 40-47
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node6 CPU(s): 48-55
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NUMA node7 CPU(s): 56-63
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Flags: fp asimd evtstrm crc32 cpuid
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="软件调优">软件调优
&lt;/h2>&lt;h3 id="qbittorrent">qBittorrent
&lt;/h3>&lt;h4 id="基本设置">基本设置
&lt;/h4>&lt;ul>
&lt;li>提升连接限制。如果你的网络/磁盘足够强劲，你可以去除所有的连接限制：&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbconn.png"
width="1214"
height="392"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbconn_hu_58678337e5f2ed18.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbconn_hu_e5e23de374656a6d.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbconn_hu_c5d1250f5b48fb1d.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbconn_hu_89b1ba58012bc366.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbconn_hu_6442abab20c802f8.webp 1214w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbconn_hu_dd8053d8272ca36c.jpg 1214w"
loading="lazy"
alt="qBittorrent 连接限制"
class="gallery-image"
data-flex-grow="309"
data-flex-basis="743px"
>
&lt;/p>
&lt;ul>
&lt;li>提高队列。磁盘性能足够的情况下，可以同时让多个种子做种，你也可以直接关闭：&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbqueue.png"
width="1214"
height="348"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbqueue_hu_51f5f9e10d26bc57.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbqueue_hu_1057e3f4efadcddb.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbqueue_hu_54ef5b83b29b38cc.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbqueue_hu_d94a596255581773.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbqueue_hu_4f3838a1b6c6c870.webp 1214w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbqueue_hu_172e404a719f51e3.jpg 1214w"
loading="lazy"
alt="qBittorrent 队列"
class="gallery-image"
data-flex-grow="348"
data-flex-basis="837px"
>
&lt;/p>
&lt;ul>
&lt;li>提高内存限制。虽然目前（2024 年 6 月）这个设置只对 Windows 有效，不过设置了也无妨：&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbramlim.png"
width="1214"
height="326"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbramlim_hu_fb53d4fefcd0da47.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbramlim_hu_32b1a80a9660462f.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbramlim_hu_6d3cf036f929c20d.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbramlim_hu_cecaab0e51904a91.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbramlim_hu_b6c7fc17dadeb429.webp 1214w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbramlim_hu_8c7f1e8080e36ae6.jpg 1214w"
loading="lazy"
alt="qBittorrent 内存限制"
class="gallery-image"
data-flex-grow="372"
data-flex-basis="893px"
>
&lt;/p>
&lt;h4 id="libtorrent-调优">libtorrent 调优
&lt;/h4>&lt;p>我们针对 libtorrent &amp;gt;=2.0 版本，1 Gbps 左右的对等带宽， 64G 左右内存的用户，其他用户请酌情调整以下参数。&lt;/p>
&lt;ul>
&lt;li>&lt;code>aio_threads&lt;/code> &lt;em>Asynchronous I/O threads&lt;/em> 默认 10 。例如在高速下载时，由于需要把下载的数据都坐 SHA1/SHA256 校验，CPU 可能存在瓶颈。提高这个值可以减轻让多核 CPU 并行处理。常见的说法是设置为 4 *线程数 。由于我使用了 64 核的处理器，256 显得过大了，所以我设置为 64 。&lt;/li>
&lt;li>&lt;code>file_pool_size&lt;/code> &lt;em>File pool size&lt;/em> 默认 40。一个 session 直接同时打开多少个文件。大量做种时，这个值设置的太小会导致频繁的文件打开/关闭，降低性能。而又设置的太大会超过一个进程的文件描述符限制（ulimit -n），导致打开文件失败。我设置为 900 （Linux 默认的文件描述符限制是 1024），留一些给其他的文件使用。&lt;/li>
&lt;li>&lt;code>max_queued_disk_bytes&lt;/code> &lt;em>Disk queue size&lt;/em> 默认 1024 KiB 。这个值表示磁盘 IO 写入队列的大小，如果达到了这个上限，libtorrent 就会停止下载，直到磁盘完成了一部分写入。这个值设置的太小会导致下载性能下降，设置的太大会导致内存占用过高。我设置为 262144 KiB 。&lt;/li>
&lt;li>&lt;code>default-disk-io-constructor&lt;/code> &lt;em>Disk IO type&lt;/em> 默认 &lt;code>mmap_disk_io_constructor&lt;/code> 。这个值表示 libtorrent 使用的磁盘 IO 类型，&lt;code>posix_disk_io_constructor&lt;/code> 是使用 POSIX IO ，&lt;code>mmap&lt;/code> 是使用 mmap ，mmap 在 kernel 里有大量优化，建议只用 mmap （Memory Mapped Files）。如果你看到了大量内存占用，请看文末注意点。&lt;/li>
&lt;li>&lt;code>disk_io_write_mode&lt;/code> 和 &lt;code>disk_io_read_mode&lt;/code> 默认 &lt;code>enable_os_cache&lt;/code> 。维持默认。&lt;/li>
&lt;li>&lt;code>piece_extent_affinity&lt;/code> &lt;em>Use piece extent affinity&lt;/em> 默认 &lt;code>0&lt;/code> 。这个值表示 libtorrent 会尽量下载相邻的 piece ，提高磁盘 IO 吞吐。建议打开。&lt;/li>
&lt;li>&lt;code>suggest_mode&lt;/code> &lt;em>Send upload piece suggestions&lt;/em> 默认 &lt;code>no_piece_suggestions&lt;/code> 。这个值表示会告诉从我这边下载的 peer 建议下载那些在我读缓存里的 piece 而不是其他的没在缓存里的，可以提高做种速度减少磁盘 IO 。建议打开。&lt;/li>
&lt;li>&lt;code>send_buffer_low_watermark&lt;/code> &lt;em>Send buffer low watermark&lt;/em> 默认 10 KiB。最小的目标 send buffer 大小（包括等待磁盘读取的字节数）。这实际上是初始窗口大小，它决定了我们能够多快地提高发送速率。我设置为 4096 KiB。&lt;/li>
&lt;li>&lt;code>send_buffer_watermark&lt;/code> &lt;em>Send buffer watermark&lt;/em> 默认 500 KiB。如果发送缓冲区的字节数少于它，将从磁盘额外读取 16 KiB 的数据到缓冲区。这是上限，所以实际大小可能小于它。过小影响上传速率，过大浪费内存。我设置为 32768 KiB。&lt;/li>
&lt;li>&lt;code>send_buffer_watermark_factor&lt;/code> &lt;em>Send buffer watermark factor&lt;/em> 默认 50。这是个百分比，peer 的上传速率乘以这个因子以获得 实际的 send_buffer_watermark，但是不超过上面的 send_buffer_watermark。对于高速连接，设置得更高可以提高上传性能和磁盘吞吐，设置得过高可能会浪费内存并且偏好读。我的目的是高速做种，我设置为 200。&lt;/li>
&lt;li>&lt;code>connection_speed&lt;/code> &lt;em>Outgoing connections per second&lt;/em> 默认 30。这个值表示每秒最多建立多少个出站连接，影响去下载 peer 那边数据的效率。我设置为 500 。&lt;/li>
&lt;li>&lt;code>listen_queue_size&lt;/code> &lt;em>Socket backlog size&lt;/em> 默认 5 。它是传递给监听 socket listen() 的值，用于指定未处理传入连接的队列长度。当我们不积极等待连接被接受时，这些连接将会被排队。由于我们是大带宽高性能做种，我设置为 3000。&lt;/li>
&lt;li>&lt;code>mixed_mode_algorithm&lt;/code> &lt;em>μTP-TCP mixed mode algorithm&lt;/em> 默认 &lt;code>prefer_tcp&lt;/code> 。这个值用于确定在同时存在 TCP 和 uTP 连接时如何处理这两种连接。我们不需要为 uTP 做出让步，所以设置为 &lt;code>prefer_tcp&lt;/code> 。&lt;/li>
&lt;li>&lt;code>allow_multiple_connections_per_ip&lt;/code> &lt;em>Allow multiple connections from the same IP address&lt;/em> 默认 &lt;code>true&lt;/code> 。这个值表示是否允许同一个 IP 建立多个连接。我们不需要限制，所以设置为 &lt;code>true&lt;/code> 。&lt;/li>
&lt;li>&lt;code>choking_algorithm&lt;/code> &lt;em>Upload slots behavior&lt;/em> 使用 &lt;code>fixed_slots_choker&lt;/code>。&lt;/li>
&lt;li>&lt;code>seed_choking_algorithm&lt;/code> &lt;em>Upload choking algorithm&lt;/em> 使用 &lt;code>fastest_upload&lt;/code>。&lt;/li>
&lt;li>&lt;code>max_out_request_queue&lt;/code> &lt;em>Maximum outstanding requests to a single peer&lt;/em> 默认 500 。表示发给 peer 的最大未处理请求数。我设置为 2000 。&lt;/li>
&lt;/ul>
&lt;h3 id="自动下载自动-announce">自动下载/自动 Announce
&lt;/h3>&lt;p>一般种子流量最大的时候为刚发布之后，为了在种子发布后能够快速下载，抢占最早的上传机会。我们需要自动下载种子，然后立即开始做种。称之为 &lt;a class="link" href="https://www.reddit.com/r/seedboxes/comments/9brx3b/comment/e570unm/" target="_blank" rel="noopener"
>racing&lt;/a> 。&lt;/p>
&lt;p>这里我使用了自己编写的脚本，你也可以用 autodl-irssi 等工具。&lt;/p>
&lt;h2 id="注意点">注意点
&lt;/h2>&lt;h3 id="判断磁盘-io-瓶颈">判断磁盘 IO 瓶颈
&lt;/h3>&lt;p>使用 &lt;code>iostat -xm 1&lt;/code>，找到存放种子数据的盘，特别关注最后的百分比，接近 100% 表示你的磁盘是瓶颈了。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy.png"
width="1592"
height="548"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_8e840d1701b93e6d.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_94e9ad173e6d24c9.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_14f1a8ded2e0aeaa.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_2b548b2ad414ff64.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_6fc427ebc2151ce.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_314aa34dc66cf6b8.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_500cb34a123095c6.webp 1592w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/iobusy_hu_e248c56d4a19ab45.jpg 1592w"
loading="lazy"
alt="iostat 查看磁盘 busy%"
class="gallery-image"
data-flex-grow="290"
data-flex-basis="697px"
>
&lt;/p>
&lt;p>你也可以在 htop 中看到 IO 的情况（在 Setup 的 Meter 中添加 Disk IO ）。htop 中的百分比是所有磁盘的合计，如果的下载盘是软 RAID 你有可能看到超过 100% 的情况，我这里是单个设备（硬 RAID），所以图中 99.1% 表示 IO 已经满了，我的硬盘已经跟不上了。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio.png"
width="1764"
height="1058"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_8f24ef2035839054.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_30d6f43da75aa995.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_df21ce627ddf944a.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_11af5105f7251320.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_32f3c744550c6b9a.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_8dbd2d5f6ad8a85b.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_d099b6907945a3a4.webp 1764w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopio_hu_803d2dbdec47c5fb.jpg 1764w"
loading="lazy"
alt="iostat 查看磁盘 busy%"
class="gallery-image"
data-flex-grow="166"
data-flex-basis="400px"
>
&lt;/p>
&lt;p>上面两张图是我使用的 3 块 SSD 阵列在 1~2 Gbps 上传，IO 爆发时的情况，可以看到其实 IO 已经快满了。如果你发现你的 busy% 接近 100% 了，那么你的磁盘 IO 就是瓶颈了，建议更换更好更快的 SSD。&lt;/p>
&lt;p>如果你用的是机械硬盘，应该是轻轻松松全程 100% ，完全撑不住 :p&lt;/p>
&lt;h3 id="判断网络-io-瓶颈">判断网络 IO 瓶颈
&lt;/h3>&lt;p>首先查看你的上传/下载速度是否超过了你的互联网带宽/物理网络接口的速度。你可以在 htop 中看到（记得在 Setup -&amp;gt; Meters 中添加 Network IO ）如果超过了，那么没什么办法，除非换网络/换网卡。如果没有，可以继续往下看。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet.png"
width="2722"
height="922"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_39b730d379e2537e.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_aff10b94c3300115.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_a365a3b4adb354e.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_af3b6755ca9f1180.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_af0c19b2ef7319a8.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_69ee16580ff6ca.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_278ec5f58bc76014.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_140bd045cd9605b5.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_ff96964e602d8e34.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_3c33761982f188bc.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_86c2f57bbab57aed.webp 2722w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnet_hu_2b75c24c58f08f20.jpg 2722w"
loading="lazy"
alt="查看网络 IO "
class="gallery-image"
data-flex-grow="295"
data-flex-basis="708px"
>
&lt;/p>
&lt;p>如果你看到 softirq 占用过高，那么你的网络 IO 可能是瓶颈了，建议更换更好的 CPU 或网卡。&lt;/p>
&lt;p>例如你可以在 htop 中看到紫色的 softirq 的占用（注意在 Setup -&amp;gt; Display Options 中勾选 Detailed CPU time ）。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi.png"
width="2102"
height="1166"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_3af233a81c09fd40.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_5fd4489618a27bc7.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_980275abcfcfd458.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_c6d3d601f305cd5e.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_94b32dc2125c154f.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_6717b7ce1455fe2d.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_c3cc8d5dfb232735.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_a758ab25c55721a.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_a0f8ebf1b6d4403.webp 2102w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsi_hu_69f253ef2190ffaf.jpg 2102w"
loading="lazy"
alt="查看 softirq"
class="gallery-image"
data-flex-grow="180"
data-flex-basis="432px"
>
&lt;/p>
&lt;p>如果你发现某个/几个核心的 softirq 占满了，那么你的网络 IO 可能是瓶颈了。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull.png"
width="2102"
height="1220"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_4a20a810e483c3ad.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_8383303e10b8a357.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_c48bbaa0dab1f4f4.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_6d14b03095505c1a.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_3bc5b14ec904fc34.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_404bd3920b676aad.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_7d42f4a53a1727b8.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_a2e7e58ddcd5d0fc.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_377b6086792d1fd8.webp 2102w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopsifull_hu_eb0b11315ab54899.jpg 2102w"
loading="lazy"
alt="异常 softirq"
class="gallery-image"
data-flex-grow="172"
data-flex-basis="413px"
>
&lt;/p>
&lt;p>正常情况你不应该看到很高的 softirq 占用。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal.png"
width="2740"
height="1586"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_181ca5070d41f539.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_e34cf6dc9e800900.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_de1953cdfa086a0a.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_ad94b0c41228fd7.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_a5f626a96dc36b4f.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_c855769df5351d79.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_3fb88060b81d7b.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_98568b3595860d40.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_b989fa81ff85e195.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_2380bfeb46069303.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_82aa9f4e705e30b2.webp 2740w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/htopnormal_hu_8ad82b1db7fbcbc3.jpg 2740w"
loading="lazy"
alt="正常 softirq"
class="gallery-image"
data-flex-grow="172"
data-flex-basis="414px"
>
&lt;/p>
&lt;h3 id="判断-cpu-瓶颈">判断 CPU 瓶颈
&lt;/h3>&lt;p>一般来说，通过看 htop 即可。注意，你不仅要看全部的 CPU 核心占用，&lt;strong>还要看单个核心的占用&lt;/strong>。如果你发现某个核占满了（这种情况更易发生）、整体 CPU 占用很高（目前 CPU 核心很多了，这个不太容易发生），那么你的 CPU 可能是瓶颈了。同时，看到 CPU 占用很高时，注意上面一节提到的 softirq 占用，这可能是网络 IO 的瓶颈。&lt;/p>
&lt;p>例如在我的计算机上，我偶尔在 IO 爆发时能见到 CPU 占用 5000%+ 的峰值，这说明我的CPU对我的使用场景来说不太行。（图中其实你会发现我的磁盘IO也满了。。）&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage.png"
width="2722"
height="808"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_9d559c0eb7270955.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_f4a3a2afe1c20c29.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_a7e748e9146256a9.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_82963b4c241d3c9.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_86f3e3b76f78431.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_58a9171ecd19a249.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_c979e156553852b6.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_34d7e33952be4024.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_1d74d058da36678b.webp 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_f1e32d7d9424f117.jpg 2560w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_a4988cfe170de731.webp 2722w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/ft2000cpuusage_hu_e0d2c987d175cb1c.jpg 2722w"
loading="lazy"
alt="FT2000 在偶尔 IO 爆发时 qBittorrent 4000%&amp;#43; 的 CPU 使用率"
class="gallery-image"
data-flex-grow="336"
data-flex-basis="808px"
>
&lt;/p>
&lt;h3 id="qbittorrent-内存占用过大">qBittorrent 内存占用过大？
&lt;/h3>&lt;p>如果你发现 qBittorrent 的内存占用过大，你不应该担心。这是 mmap 的特性，它会把文件映射到内存中，这样可以减少磁盘 IO ，提高性能。如果你关注总的内存占用，你会发现它并不会增加。自从 libtorrnet 2.0 使用 mmap 以来，有很多用户 &lt;a class="link" href="https://github.com/arvidn/libtorrent/issues/6667" target="_blank" rel="noopener"
>反馈内存占用过大&lt;/a> ，但是这是正常的。&lt;/p>
&lt;p>例如我的计算机上，qBittorrent 的内存占用超过了 80GB ，但是总的内存占用只有 5GB。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram.png"
width="2102"
height="1166"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_70a9efbe71026e87.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_e3dfd41e12515fbe.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_ce62f2236ae330c2.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_90c9189215d4c444.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_3f52a51b9e8ceda6.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_13adb9beee421a9a.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_471f9a2421ec67fe.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_187bbb8a73d620e2.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_ba8802af2b1bcc35.webp 2102w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/qbhighram_hu_1968d432d6ec3563.jpg 2102w"
loading="lazy"
alt="qBittorrent 内存占用示例"
class="gallery-image"
data-flex-grow="180"
data-flex-basis="432px"
>
&lt;/p>
&lt;h3 id="检测是否可被连接">检测是否可被连接
&lt;/h3>&lt;p>你可以使用外网 TCPing 来检查，例如一些网站可以替你检测： &lt;a class="link" href="https://www.itdog.cn/tcping" target="_blank" rel="noopener"
>IPv4&lt;/a>、&lt;a class="link" href="https://www.itdog.cn/tcping_ipv6" target="_blank" rel="noopener"
>IPv6&lt;/a> 。输入你的公网 IP 和端口，如果各地均能够 ping 通，那么你是可被连接的。&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping.png"
width="2458"
height="1080"
srcset="https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_c796628aa02a8bd7.webp 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_f101d91e676d272f.jpg 480w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_914572234e2e0626.webp 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_d3f4d698c48b4105.jpg 1024w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_ab5d8f75d003e7b0.webp 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_178cf78b979e3329.jpg 1536w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_d6429e75146e855f.webp 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_1070a1e6ddafb629.jpg 2048w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_73fff28e0081cb5a.webp 2458w, https://charlie0129.github.io/blog/p/high-speed-seeding-guide/images/tcping_hu_c118bdf43f78eb78.jpg 2458w"
loading="lazy"
alt="外网 TCPing"
class="gallery-image"
data-flex-grow="227"
data-flex-basis="546px"
>
&lt;/p>
&lt;h3 id="上传仍然不及预期">上传仍然不及预期？
&lt;/h3>&lt;p>如果你各方面都确认没有瓶颈（网络、磁盘、CPU 等）但是上传速度仍然很慢，那么可能是你的种子不够热门，或者说你的 peer 的下行太慢了。这方面除了选择热门的种子（做种人少，下载人多，提高下载的 peer 连接你的机会），其实也没什么办法了。&lt;/p>
&lt;p>因为你的上传是需要 peer 来连接你的，这就不是你能决定的事，可遇而不可求。举个例子，如果想跑满 10Gbps 的上传，那么需要 10 个 peer ，每个都以 1Gbps 的速度从你这拉数据，这是非常困难的。首先，别人的下载速度很少有 1G 的；其次，做种的人几十个，别人不可能只从你这下载，很可能从几十个不同的 peer 那边下载，一下就分散了，一个 peer 到你这只有几十分之一的流量了。&lt;/p>
&lt;p>所以，这种情况下，除了选择合适的种子提高 peer 连接你的机会也没什么办法了。合适的种子通常是刚发布、热门、做种人少、下载人多的种子。&lt;/p></description></item><item><title>gVisor Killed My Node - A Debug Process</title><link>https://charlie0129.github.io/blog/p/gvisor-oom-debug/</link><pubDate>Mon, 15 Apr 2024 23:38:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/gvisor-oom-debug/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>Recent Update&lt;/strong>: One month later, after we opened our issue &lt;a class="link" href="https://github.com/google/gvisor/issues/10264" target="_blank" rel="noopener"
>#10264&lt;/a>, it turns out that the gVisor developers are also aware of tha lacking of documentation about the &lt;code>systemd-cgroup&lt;/code> option. Finally:&lt;/p>
&lt;ul>
&lt;li>They have added the &lt;code>systemd-cgroup&lt;/code> option to the documentation in &lt;a class="link" href="https://github.com/google/gvisor/commit/8c3abba8000496c06341653f158dc34a4318565a" target="_blank" rel="noopener"
>8c3abba&lt;/a>, although the documentation website doesn&amp;rsquo;t seem to be updated as of now.&lt;/li>
&lt;li>&lt;code>runsc&lt;/code> will throw a warning if it detects a systemd-like path but &lt;code>systemd-cgroup&lt;/code> is not used to tell you about the possible misconfiguration.&lt;/li>
&lt;/ul>
&lt;p>So if you are using gVisor with systemd cgroup, you should add the &lt;code>systemd-cgroup&lt;/code> option to the &lt;code>runsc&lt;/code> configuration, if not already.&lt;/p>&lt;/blockquote>
&lt;h2 id="the-problem">The Problem
&lt;/h2>&lt;p>Recently we found some of our Kubernetes nodes were constant going down, completely dead, with no luck connecting to it. Taints like &lt;code>node.kubernetes.io/unreachable:NoSchedule&lt;/code> and &lt;code>node.kubernetes.io/unreachable:NoExecute&lt;/code> were automatically added to the nodes because the &lt;code>kubelet&lt;/code> was not able to communicate with the API server. The only way to bring the node back was to restart it. After some debugging, we found out that some bad gVisor-created (&lt;code>runsc&lt;/code> runtime) Pods was eating all the memory of the node and killing it. Traditional Pods with &lt;code>runc&lt;/code> runtime were running fine.&lt;/p>
&lt;p>You may say, &amp;ldquo;But there are memory limit on a Pod. If it uses more than the limit it should be killed by the OOM killer, right&amp;rdquo;？ That&amp;rsquo;s what I thought too, so I applied the following Deployment to intentionally consume some memory and see if it will be killed.&lt;/p>
&lt;details>
&lt;summary>Click to see full yaml&lt;/summary>
&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">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apps/v1&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">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deployment&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">metadata&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">labels&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">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">memory-eater-bash&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">memory-eater-bash&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">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">default&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">spec&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">replicas&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">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">selector&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">matchLabels&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">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">memory-eater-bash&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">template&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">metadata&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">labels&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">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">memory-eater-bash&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">spec&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">containers&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">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">bash&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">args&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">c&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">big_var=data; while true; do big_var=&amp;#34;$big_var$big_var&amp;#34;; done; sleep 2d&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">python:3.12-bookworm&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">ubuntu&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">securityContext&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">readOnlyRootFilesystem&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">runAsNonRoot&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">runAsUser&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">999&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">resources&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">limits&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">cpu&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">100m&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">memory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">256Mi&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">requests&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">cpu&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">100m&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">memory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">200Mi&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">dnsPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Default&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">hostNetwork&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">restartPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Always&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">runtimeClassName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gvisor&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;/details>
&lt;p>I applied 256MiB memory limit on it, hoping it will be kill after it uses more than 256MiB memory. But it didn&amp;rsquo;t. &lt;em>It kept eating all the memory until the node was dead&lt;/em>, bypass OOM protection provided by Kubernetes. It ate over 500GiB of memory, which is the total memory of the node, and we were no longer able to connect to the node. Luckily, we eviction process kicked in and existing Pods on the node are moved to other nodes, so the service was not affected by much.&lt;/p>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/gvisor-oom-debug/images/bad-pod-memory-usage.png"
width="1106"
height="538"
srcset="https://charlie0129.github.io/blog/p/gvisor-oom-debug/images/bad-pod-memory-usage_hu_b90a3efcf7392c28.webp 480w, https://charlie0129.github.io/blog/p/gvisor-oom-debug/images/bad-pod-memory-usage_hu_7ba58d493ffed2f9.jpg 480w, https://charlie0129.github.io/blog/p/gvisor-oom-debug/images/bad-pod-memory-usage_hu_6bec7790d7fb5582.webp 1024w, https://charlie0129.github.io/blog/p/gvisor-oom-debug/images/bad-pod-memory-usage_hu_9e613324adbe9273.jpg 1024w, https://charlie0129.github.io/blog/p/gvisor-oom-debug/images/bad-pod-memory-usage_hu_c2cb55ef979de6f8.webp 1106w, https://charlie0129.github.io/blog/p/gvisor-oom-debug/images/bad-pod-memory-usage_hu_68b662a4e2e32556.jpg 1106w"
loading="lazy"
alt="Bad Pod Memory Usage"
class="gallery-image"
data-flex-grow="205"
data-flex-basis="493px"
>
&lt;/p>
&lt;h2 id="the-debug-process">The Debug Process
&lt;/h2>&lt;p>Of course, we need to apply the above memory-eater Deployment to reproduce the issue. But killing a node every time is not a good idea 😂 , especially when we need to manually restart it. So I used another Deployment to consume &lt;em>some&lt;/em> (1GiB) memory, but not all of it.&lt;/p>
&lt;details>
&lt;summary>Click to see full yaml&lt;/summary>
&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">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apps/v1&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">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deployment&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">metadata&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">labels&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">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">memory-eater-python&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">memory-eater-python&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">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">default&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">spec&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">replicas&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">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">selector&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">matchLabels&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">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">memory-eater-python&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">template&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">metadata&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">labels&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">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">memory-eater-python&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">spec&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">containers&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">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">python&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">args&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">c&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">big_list = []; print(&amp;#39;Allocating 1GB of memory...&amp;#39;); [big_list.append(&amp;#39; &amp;#39; * 10**6) for _ in range(1000)]; import time; time.sleep(100000)&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">python:3.12-bookworm&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">ubuntu&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">securityContext&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">readOnlyRootFilesystem&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">runAsNonRoot&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">runAsUser&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">999&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">resources&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">limits&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">cpu&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">100m&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">memory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">256Mi&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">requests&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">cpu&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">100m&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">memory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">200Mi&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">dnsPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Default&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">hostNetwork&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">restartPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Always&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">runtimeClassName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gvisor&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;/details>
&lt;p>Just as I expected, the memory limit did not work. Time to debug.&lt;/p>
&lt;h3 id="trying-to-reproduce">Trying to reproduce
&lt;/h3>&lt;p>Before I trying to do anything on the production cluster, I tried to reproduce the issue on my local machine. Just to be safe not to kill the production cluster and get fired :P (just kidding. It&amp;rsquo;s because debugging is a lot easier on my local machine).&lt;/p>
&lt;p>I used Docker with gVisor as per the &lt;a class="link" href="https://gvisor.dev/docs/user_guide/quick_start/docker/" target="_blank" rel="noopener"
>official documentation&lt;/a>. It was working fine. Our production cluster is using &lt;code>containerd&lt;/code>, so was it a &lt;code>containerd&lt;/code> issue? I was in a hurry so I used &lt;code>minikube&lt;/code> with &lt;code>gVisor&lt;/code> addon enabled, which should provide a Kubernetes cluster with &lt;code>containerd&lt;/code> and gVisor (and a &lt;code>containerd&lt;/code> shim for &lt;code>runsc&lt;/code>). At the time of writing, it was Kubernetes &lt;code>v1.28.3&lt;/code>, containerd &lt;code>v1.6.24&lt;/code>, and gVisor &lt;code>release-20240401.0&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p>I know, I know. We should keep versions of the software same as the production cluster. But I was in a hurry and I was not sure if the issue was with &lt;code>containerd&lt;/code> or &lt;code>gVisor&lt;/code>. So I just went with it. Spoiler: yes, it has something to do with versions. If I were to do it again, I would use the same versions as the production cluster.&lt;/p>&lt;/blockquote>
&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">minikube start --addons&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gvisor&amp;#34;&lt;/span> --container-runtime&lt;span class="o">=&lt;/span>containerd --driver&lt;span class="o">=&lt;/span>docker --cpus&lt;span class="o">=&lt;/span>&lt;span class="m">6&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>I applied the memory-eater Deployment and waited some surprising results. But the 256MiB memory limit was working as expected. The Pod was killed after it used more than 256MiB memory. I smelled some fishy things going on in the production cluster.&lt;/p>
&lt;h3 id="debugging-on-the-production-cluster">Debugging on the production cluster
&lt;/h3>&lt;p>Again, I applied the memory-eater Deployment, but on the production cluster this time. Hoping it will not kill the node. To my relief, it did not kill the node. But it did not kill the Pod with its memory (969MiB) above limits (256MiB) either (which is 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;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> kubectl top pod memory-eater-python-67cc4dd4f7-sp2r6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">NAME CPU(cores) MEMORY(bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">memory-eater-python-67cc4dd4f7-sp2r6 0m 969Mi
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Before I dig anything deeper (like with some gVisor debugging tools), I checked the sandbox created by &lt;code>runsc&lt;/code> (gVisor) to see if there is anything suspicious.&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;/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> On production node
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nv">PID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>ps aux &lt;span class="p">|&lt;/span> grep memory-eater-python &lt;span class="p">|&lt;/span> grep runsc-sandbox &lt;span class="p">|&lt;/span> awk &lt;span class="s1">&amp;#39;{print $2}&amp;#39;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> cat /proc/&lt;span class="nv">$PID&lt;/span>/cmdline &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;\0&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">runsc-sandbox
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--log-format=json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--panic-log=/var/log/pods/default_memory-eater-python-74975bf59f-7fflh_1cf55082-d769-4a51-a424-2312c23eae82/gvisor_panic.log
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--root=/run/containerd/runsc/k8s.io
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--log=/run/containerd/io.containerd.runtime.v2.task/k8s.io/6a541285fa39d743dc20bd8b3169c4a8f5cd4d77542144fe82d3f6f7af469c2c/log.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--log-fd=3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--panic-log-fd=4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">boot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--apply-caps=false
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--bundle=/run/containerd/io.containerd.runtime.v2.task/k8s.io/6a541285fa39d743dc20bd8b3169c4a8f5cd4d77542144fe82d3f6f7af469c2c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--controller-fd=10
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--cpu-num=96
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--io-fds=5,6
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--mounts-fd=7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--overlay-mediums=0,0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--setup-root=false
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--spec-fd=11
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--start-sync-fd=8
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--stdio-fds=12,13,14
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--total-host-memory=540886331392
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--total-memory=540886331392 # 503GiB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--user-log-fd=9
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--product-name=AS
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">-4124GS-TNR
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--proc-mount-sync-fd=22
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">6a541285fa39d743dc20bd8b3169c4a8f5cd4d77542144fe82d3f6f7af469c2c
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Hmm, something caught my eye.&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-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">--total-host-memory=540886331392
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--total-memory=540886331392 # 503GiB
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Why is the &lt;code>total-memory&lt;/code> and &lt;code>total-host-memory&lt;/code> set to 503GiB? That&amp;rsquo;s the total memory of the node. I assume it should be set to the memory limit of the Pod, which is 256MiB. To verify my assumption, I checked the sandbox created by &lt;code>runsc&lt;/code> on my local machine and it indeed was set to 256MiB.&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-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> On my &lt;span class="nb">local&lt;/span> machine
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nv">PID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>ps aux &lt;span class="p">|&lt;/span> grep memory-eater-python &lt;span class="p">|&lt;/span> grep runsc-sandbox &lt;span class="p">|&lt;/span> awk &lt;span class="s1">&amp;#39;{print $2}&amp;#39;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> cat /proc/&lt;span class="nv">$PID&lt;/span>/cmdline &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;\0&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">runsc-sandbox
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--root=/run/containerd/runsc/k8s.io
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--log=/run/containerd/io.containerd.runtime.v2.task/k8s.io/f11a26742777348875e6c7f182d9f8cc5c4a1c3a7f726ad5e8cb956b0c2dea96/log.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--log-format=json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--panic-log=/var/log/pods/default_memory-eater-python-fc75975c8-vncwz_480a4dd9-22ad-4e43-9ecb-b797735e990a/gvisor_panic.log
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--log-fd=3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--panic-log-fd=4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">boot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--apply-caps=false
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--bundle=/run/containerd/io.containerd.runtime.v2.task/k8s.io/f11a26742777348875e6c7f182d9f8cc5c4a1c3a7f726ad5e8cb956b0c2dea96
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--controller-fd=10
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--cpu-num=6
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--dev-io-fd=-1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--gofer-mount-confs=lisafs:none,lisafs:none
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--io-fds=5,6
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--mounts-fd=7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--setup-root=false
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--spec-fd=11
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--start-sync-fd=8
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--stdio-fds=12,13,14
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--total-host-memory=2061119488
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--total-memory=268435456 # 256MiB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--user-log-fd=9
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">--proc-mount-sync-fd=22
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">f11a26742777348875e6c7f182d9f8cc5c4a1c3a7f726ad5e8cb956b0c2dea96
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>It has the correct limits on my local 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;/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">--total-host-memory=2061119488
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--total-memory=268435456 # 256MiB
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>So the issue was with this &lt;code>--total-memory&lt;/code> argument. But why is it incorrect on the production cluster?&lt;/p>
&lt;h3 id="checking-the-versions">Checking the versions
&lt;/h3>&lt;p>Seeing the difference, I checked the versions on my local cluster and the production cluster.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Tool&lt;/th>
&lt;th>Local&lt;/th>
&lt;th>Production&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Kubernetes&lt;/td>
&lt;td>1.28.3&lt;/td>
&lt;td>1.26.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>containerd&lt;/td>
&lt;td>v1.6.24&lt;/td>
&lt;td>v1.7.0-rc.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>runsc&lt;/td>
&lt;td>20240401.0&lt;/td>
&lt;td>20231009.0&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Both are running Ubuntu Server 22.04 LTS, using systemd+cgroupsv2.&lt;/p>
&lt;p>I assume the problem was with runsc (gVisor).&lt;/p>
&lt;h2 id="whats-wrong-with-gvisor">What&amp;rsquo;s wrong with gVisor?
&lt;/h2>&lt;h3 id="cgroups-v2-support">Cgroups v2 support
&lt;/h3>&lt;p>After I know the issue is with the &lt;code>--total-memory&lt;/code> flag. I found there is a similar issue reported &lt;a class="link" href="https://github.com/google/gvisor/issues/9580" target="_blank" rel="noopener"
>google/gvisor #9580&lt;/a>. The author had great explanation of the issue:&lt;/p>
&lt;blockquote>
&lt;p>Under Kubernetes + cgroups v2 + systemd, gVisor launches all processes into the container subgroup associated with the pause container. This makes some sense given that cgroups v2 specifies that processes can only exist at leaf nodes, and the pod&amp;rsquo;s cgroup is registered as a slice (an intermediate unit which cannot have its own processes) with systemd. When the sandbox is launched gVisor needs a container subgroup and the pause container is the first to be launched. The pause container is a child of the pod cgroup and therefore inherits the limits of the parent pod cgroup, BUT the child&amp;rsquo;s controllers reflect the default max value. This in turn means that this code which reads the memory limit and cpu quota reads these as unlimited.&lt;/p>&lt;/blockquote>
&lt;p>Let me explain a bit more. So a normal cgroup v2 hierarchy for a Pod looks 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;/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">/sys/fs/cgroup/kubepods.slice/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── kubepods-burstable.slice
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── kubepods-burstable-pod&amp;lt;pod_id&amp;gt;.slice # has memory limit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── cri-containerd-&amp;lt;container_id&amp;gt;.scope # no memory limit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The pod slice has a memory limit (cat &lt;code>memory.max&lt;/code> gives &lt;code>xxx&lt;/code>), but the container scope does not (cat &lt;code>memory.max&lt;/code> gives &lt;code>max&lt;/code>). The container scope is where the container is running. The container scope is a child of the pod slice. So the container should inherit the memory limit of the pod slice. This is how cgroups works.&lt;/p>
&lt;p>But gVisor only checks the container scope and says &amp;ldquo;oh, it has no memory limit. Let&amp;rsquo;s set the total memory to max (the total memory of the node)&amp;rdquo;. And that&amp;rsquo;s why the &lt;code>--total-memory&lt;/code> is set to 503GiB on the production cluster.&lt;/p>
&lt;p>This issue was fixed in &lt;a class="link" href="https://github.com/google/gvisor/pull/9631" target="_blank" rel="noopener"
>google/gvisor #9631&lt;/a>. The code below is from the PR which fixed the issue (after the comments). So it checks the Pos slice (parent) if the memory limit is not set in the container slice (leaf node).&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: runsc/cgroup/cgroup_v2.go&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/google/gvisor/blob/43c2c00c5006e7d21ce9cd431692622031891231/runsc/cgroup/cgroup_v2.go#L333-L346&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// MemoryLimit returns the memory limit.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">c&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">cgroupV2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">MemoryLimit&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">limStr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nf">getMemoryLimit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">MakePath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// In cgroupv2+systemd, limits are set in the parent slice rather&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// than the leaf node. Check the parent to see if this is the case.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">limStr&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">maxLimitStr&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">parentLimStr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nf">getMemoryLimit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filepath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Dir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">MakePath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">errors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Is&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ErrNotExist&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">parentLimStr&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s">&amp;#34;&amp;#34;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">limStr&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">parentLimStr&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">limStr&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">maxLimitStr&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MaxUint64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">strconv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ParseUint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">limStr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The &lt;code>runsc&lt;/code> binary on our production cluster was built before the fix was merged. So I assume the issue was fixed in the &lt;code>runsc&lt;/code> binary after the merge.&lt;/p>
&lt;h3 id="systemd-cgroup-configuration">Systemd cgroup configuration
&lt;/h3>&lt;p>So if the issue was fixed in the latest release, simply upgrading the &lt;code>runsc&lt;/code> binary should fix the issue. No. The issue was still there. I checked the &lt;code>runsc&lt;/code> binary on the production cluster and it was built after the fix was merged. So what&amp;rsquo;s wrong?&lt;/p>
&lt;p>Remember what a normal cgroup v2 hierarchy looks like in the previous section? Although the cgroups created by &lt;code>runc&lt;/code> are just like that, that&amp;rsquo;s not how the ones created by &lt;code>runsc&lt;/code> looks like on our production cluster. Instead, they look 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;/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">/sys/fs/cgroup/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── kubepods.slice
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── kubepods-burstable.slice
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └── kubepods-burstable-pod&amp;lt;pod_id&amp;gt;.slice
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── system.slice
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── kubepods-burstable-pod&amp;lt;pod_id&amp;gt;.slice:cri-containerd:&amp;lt;container_id&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Why are the container cgroups not under the pod cgroups? Why are they in system.slice? If that was the case, the fix above won&amp;rsquo;t do anything because the parent slice is not the pod slice. The parent slice is system.slice, it has no memory limit. So the container will have no memory limit even after the fix. OOM&amp;rsquo;in the node.&lt;/p>
&lt;p>After some issue-searching on the gVisor GitHub repo, I found that runsc initially has no support for systemd cgroups &lt;a class="link" href="https://github.com/google/gvisor/issues/193" target="_blank" rel="noopener"
>google/gvisor #193&lt;/a>. Support for systemd cgroup was added in &lt;a class="link" href="https://github.com/google/gvisor/pull/7287" target="_blank" rel="noopener"
>google/gvisor #7287&lt;/a>. It required a specific configuration in the &lt;code>runsc&lt;/code> configuration:&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-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl">// File: runsc/config/config.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// Link: https://github.com/google/gvisor/blob/bf86207401cab99d859b25acd4911038608f0d33/runsc/config/config.go#L229-L230
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ // Use systemd to configure cgroups.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ SystemdCgroup bool `flag:&amp;#34;systemd-cgroup&amp;#34;`
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Hmm, looks like there was nothing like that in the &lt;code>runsc&lt;/code> configuration on the production cluster. Go! Fix it! Find one of the cordoned node, drain it, upgrade the &lt;code>runsc&lt;/code> binary, and change the configuration.&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-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl">/etc/containerd/config.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [plugins.&amp;#34;io.containerd.grpc.v1.cri&amp;#34;.containerd.runtimes.runsc]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> runtime_type = &amp;#34;io.containerd.runsc.v1&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ [plugins.&amp;#34;io.containerd.grpc.v1.cri&amp;#34;.containerd.runtimes.runsc.options]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ TypeUrl = &amp;#34;io.containerd.runsc.v1.options&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ ConfigPath = &amp;#34;/etc/containerd/runsc.toml&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/etc/containerd/runsc.toml&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ [runsc_config]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ systemd-cgroup = &amp;#34;true&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Restart containerd &lt;code>systemctl restart containerd&lt;/code>. Apply a new Pod with &lt;em>toleration&lt;/em> and &lt;em>nodeSelector&lt;/em> to schedule it to a cordoned node. And it worked! The container cgroups are now under the pod cgroups. The &lt;code>--total-memory&lt;/code> is set to 256MiB. The Pod was killed after it used more than 256MiB memory. The node was not killed. The issue was fixed.&lt;/p>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>So the issue was a combination of old gVisor version and incorrect configuration. The gVisor version on the production cluster was built before the fix was merged. The configuration was missing the &lt;code>systemd-cgroup&lt;/code> option. The issue was fixed by upgrading the &lt;code>runsc&lt;/code> binary and adding the &lt;code>systemd-cgroup&lt;/code> option to the configuration.&lt;/p></description></item><item><title>Thoughts on Re-Encoded BD Animes</title><link>https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/</link><pubDate>Tue, 17 Jan 2023 21:28:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/</guid><description>&lt;h2 id="introduction">Introduction
&lt;/h2>&lt;p>Re-encoded BD anime series are my go-to when watching animes. They have great quality while being a fraction of the size of a Blu-ray disc. Let alone they come with all kinds of extra contents.&lt;/p>
&lt;p>Nowadays, re-encoded BD anime series usually have video tracks compressed using 10-bit x265 (a software implementation of the H.265/HEVC video compression standard), and audio tracks compressed using FLAC (a lossless audio codec).&lt;/p>
&lt;p>The video tracks are great, generally a good balance between size and quality, no complaints. The audio tracks are my main problem. Sometimes they take up too much space with no real benefit for most viewers.&lt;/p>
&lt;blockquote>
&lt;p>Other versions like WEB-DL are not considered because they are already heavily compressed so they don&amp;rsquo;t have the space concerns.&lt;/p>&lt;/blockquote>
&lt;h2 id="video-tracks">Video Tracks
&lt;/h2>&lt;p>I am totally satisfied with the video tracks, because:&lt;/p>
&lt;ol>
&lt;li>the x265 encoder is very efficient (especially at variable bitrate controlling, which most hardware encoders still suffer from, i.e. NVIDIA NVENC, Intel QuickSync, and AMD VCN), being able to compress video to a fraction of the size of the BD original while preserving video quality;&lt;/li>
&lt;li>the encoding team usually does a great job doing manual optimizations to fix problems in the original video, such as aliasing, ringing, and color banding. (Some team even goes as far as AI upscaling the video, although a bit controversial.)&lt;/li>
&lt;/ol>
&lt;p>Despite being one third (or even less) of the original bitrate, you can barely tell the difference between the re-encoded video and the original one, even when comparing side-by-side. In fact, the re-encoded video is sometimes even better due to the manual optimizations by the encoding team. This saves a lot of space while preserving video quality.&lt;/p>
&lt;blockquote>
&lt;p>Video encoding is a complex topic, involving a huge amount of trial and error, even x265-encoding alone has a TON of dials and knobs to tweak, and this is only part of the whole encoding process. I will not go into details here, simply because of my lack of knowledge.&lt;/p>&lt;/blockquote>
&lt;h3 id="samples">Samples
&lt;/h3>&lt;p>For example, you can see what the encoding team (VCB-Studio) does to optimize the original video quality from the note of &lt;em>Haiyore! Nyaruko-san / 潜行吧！奈亚子&lt;/em> :&lt;/p>
&lt;blockquote>
&lt;p>For Season 1, the source is of nothing special. Its native resolution is 720p, then upscaled by a poor algorithm. This leads to many defects: lines featuring severe aliasing and ringing as well as serious ringing on the image borders. Dark scenes are widely spread throughout Season 1, together with the plentiful use of dark fades, resulting in heavy colour banding.
For the lines, most of the problems can be solved by descaling and reconstruction, then slight AA and moderate de-ringing as supplements. For colour banding, since most scenes have only a little colour banding, in order to deal with severe colour banding in dark scenes, we designed a complex mask for protection in combination with luminance information. We also use a colour banding detection algorithm. Thanks to all those, we finally adaptively controlled the strength of de-banding. At the end, we added some grains to improve the viewing exprience.&lt;/p>
&lt;p>第一季原盘画质一般。原生分辨率为 720p，被比较劣质的算法拉升上来，导致线条带有严重锯齿和振铃，而且画面的边框部分有很强的拉升带来的边缘振铃。画面暗场极多，而且大量运用暗式淡入淡出，这些场景出现了严重的色带。
对于线条，大部分问题都可以通过逆向拉升再重构解决，然后补上轻微的抗锯齿处理和中等强度的去振铃处理。对于色带，由于大部分场景的色带都很少，只有少部分暗场有较严重的色带，我们结合亮度信息设计了复杂的保护手段，并使用了一个色带检测算法，根据检测的结果对去色带的力度进行了自适应调整。最后加上了一定强度的动态噪点，以改善原盘噪点的观感。&lt;/p>&lt;/blockquote>
&lt;details>
&lt;summary>MediaInfo: Haiyore! Nyaruko-san episode 1 from VCB-Studio&lt;/summary>
&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;/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="n">General&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Unique&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">276260962046491867529669363000855506406&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mh">0xCFD5ED098E39DF78A31330DA2C4C2DE6&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Complete&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">Volumes&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">pool&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">raidz&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="n">x4tb&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">public&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">anime&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">VCB&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">Studio&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="n">Haiyore&lt;/span>&lt;span class="o">!&lt;/span> &lt;span class="n">Nyaruko&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">san&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">VCB&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">Studio&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="n">Haiyore&lt;/span>&lt;span class="o">!&lt;/span> &lt;span class="n">Nyaruko&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">san&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">Ma10p_1080p&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">VCB&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">Studio&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="n">Haiyore&lt;/span>&lt;span class="o">!&lt;/span> &lt;span class="n">Nyaruko&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">san&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">01&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">Ma10p_1080p&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">x265_flac&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mkv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Matroska&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="n">version&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Version&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">File&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">946&lt;/span> &lt;span class="n">MiB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">23&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">56&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Overall&lt;/span> &lt;span class="n">bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Overall&lt;/span> &lt;span class="n">bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="mi">524&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Encoded&lt;/span> &lt;span class="n">date&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">UTC&lt;/span> &lt;span class="mi">2022&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">05&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">28&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">09&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">48&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">application&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">mkvmerge&lt;/span> &lt;span class="n">v48&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">0.0&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Fortress Around Your Heart&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libebml&lt;/span> &lt;span class="n">v1&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">4.0&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">libmatroska&lt;/span> &lt;span class="n">v1&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">6.0&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="n">Video&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">HEVC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Info&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">High&lt;/span> &lt;span class="n">Efficiency&lt;/span> &lt;span class="n">Video&lt;/span> &lt;span class="n">Coding&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="n">profile&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Main&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">L4&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">High&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">V_MPEGH&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ISO&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">HEVC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">23&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">56&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="mi">028&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Width&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">920&lt;/span> &lt;span class="n">pixels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Height&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">080&lt;/span> &lt;span class="n">pixels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Display&lt;/span> &lt;span class="n">aspect&lt;/span> &lt;span class="n">ratio&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Constant&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">23.976&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">24000&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">1001&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">FPS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">Color&lt;/span> &lt;span class="n">space&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">YUV&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Chroma&lt;/span> &lt;span class="n">subsampling&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">depth&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span> &lt;span class="n">bits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bits&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Pixel&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">Frame&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">0.081&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Stream&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">690&lt;/span> &lt;span class="n">MiB&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">73&lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">x265&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">97&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ga456c6e73&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">g9859a8cb5&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="n">Windows&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">clang&lt;/span> &lt;span class="mf">14.0&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">64&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="n">Kyouko&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="n">bit&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="n">bit&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="n">bit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Encoding&lt;/span> &lt;span class="n">settings&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">rc&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">crf&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crf&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">14.0000&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qcomp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.65&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qpstep&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">stats&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">stats&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">maxrate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">38000&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bufsize&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">40000&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">init&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.9&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">fullness&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">50.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">fullness&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">80.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crf&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">max&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crf&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">min&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lossless&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lossless&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">aq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">aq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">strength&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">aq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bias&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">strength&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">cbqpoffs&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crqpoffs&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">ipratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.40&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">pbratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.20&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">psy&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">2.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">psy&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rdoq&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">deblock&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">ref&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">modes&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">bframes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">adapt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">bframe&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pyramid&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">weightp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">weightb&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">keyint&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">keyint&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">360&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lookahead&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">80&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">gop&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lookahead&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">scenecut&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">40&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">hist&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">scenecut&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">radl&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">me&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">subme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">merange&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">38&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rdoq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rdpenalty&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">dynamic&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refine&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="o">-----&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">cutree&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sao&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rect&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">amp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">open&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">gop&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">wpp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pmode&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pme&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">psnr&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ssim&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">nr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">nr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">inter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">constrained&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">strong&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">smoothing&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">tu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">inter&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">depth&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">tu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">depth&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tu&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qg&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qpmax&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">69&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qpmin&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="o">-----&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">cpuid&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1111039&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">frame&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">threads&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">numa&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pools&lt;/span>&lt;span class="o">=+&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">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">input&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">csp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">input&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1920&lt;/span>&lt;span class="n">x1080&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">interlace&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">level&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">idc&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">high&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tier&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">uhd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">allow&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">non&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">conformance&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">repeat&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">headers&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">aud&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hrd&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">hash&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">temporal&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">layers&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">lookahead&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">slices&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">splice&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refresh&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ssim&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rd&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">signhide&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">tskip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">merge&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">temporal&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">mvp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">frame&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dup&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hme&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">analyze&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pics&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sao&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">non&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">deblock&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">selective&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sao&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">early&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">skip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rskip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">fast&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tskip&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">fast&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">splitrd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">skip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">zone&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">strict&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cbr&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">grain&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">vbv&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">sar&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">overscan&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">videoformat&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">colorprim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">transfer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">colormatrix&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">chromaloc&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">display&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">window&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">cll&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">luma&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">luma&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1023&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">log2&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">poc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lsb&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vui&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">timing&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">info&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vui&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hrd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">info&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">slices&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">qp&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pps&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ref&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">list&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pps&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">multi&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">pass&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rps&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">scenecut&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.05&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">hist&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">threshold&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.03&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">delta&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">qp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">aq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">motion&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hdr10&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hdr10&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dhdr10&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">idr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">recovery&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sei&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">analysis&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">reuse&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">analysis&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">reuse&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">analysis&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">load&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">reuse&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">scale&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">factor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">inter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">mv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ctu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">distortion&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sao&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">ctu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">info&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lowpass&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dct&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">analysis&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">copy&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pic&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ausize&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">factor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dynamic&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refine&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">single&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sei&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hevc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">aq&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">svt&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">field&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qp&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">adaptation&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">range&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">scenecut&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">aware&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">qp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="n">conformance&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">window&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">offsets&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">right&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">bottom&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">decoder&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">live&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">multi&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">pass&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Default&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Forced&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">Color&lt;/span> &lt;span class="nb">range&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Limited&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">Color&lt;/span> &lt;span class="n">primaries&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">BT&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">709&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Transfer&lt;/span> &lt;span class="n">characteristics&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">BT&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">709&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Matrix&lt;/span> &lt;span class="n">coefficients&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">BT&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">709&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="n">Audio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Info&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Free&lt;/span> &lt;span class="n">Lossless&lt;/span> &lt;span class="n">Audio&lt;/span> &lt;span class="n">Codec&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">A_FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">23&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">56&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">494&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="n">channels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span> &lt;span class="n">layout&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">L&lt;/span> &lt;span class="n">R&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Sampling&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">48.0&lt;/span> &lt;span class="n">kHz&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">11.719&lt;/span> &lt;span class="n">FPS&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">4096&lt;/span> &lt;span class="n">SPF&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">depth&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">24&lt;/span> &lt;span class="n">bits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Compression&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Lossless&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Stream&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">256&lt;/span> &lt;span class="n">MiB&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">27&lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libFLAC&lt;/span> &lt;span class="mf">1.3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">UTC&lt;/span> &lt;span class="mi">2017&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">01&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">01&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Language&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Japanese&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Default&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Forced&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&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="n">Menu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">00&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">00&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">00.000&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">en&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">Chapter&lt;/span> &lt;span class="mi">01&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">00&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">01&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">14.992&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">en&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">Chapter&lt;/span> &lt;span class="mi">02&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">00&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">02&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">45.040&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">en&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">Chapter&lt;/span> &lt;span class="mi">03&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">00&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">08.997&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">en&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">Chapter&lt;/span> &lt;span class="mi">04&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">00&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">10.037&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">en&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">Chapter&lt;/span> &lt;span class="mi">05&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">00&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">23&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">40.002&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">en&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">Chapter&lt;/span> &lt;span class="mi">06&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/details>
&lt;p>As of video quality, you can verify yourself. Here are some frames extracted from &lt;em>Haiyore! Nyaruko-san / 潜行吧！奈亚子&lt;/em> (VCB-Studio). Can you tell any difference between the original and the re-encoded video? Remember that the re-encoded video has a bitrate of only 4028 kbps, while the original video will &lt;em>at least&lt;/em> triple that.&lt;/p>
&lt;blockquote>
&lt;p>This blog uses Responsive Images to improve experience, so what you see here may be scaled down. To view the original image, you can download the original file from the links provided.&lt;/p>&lt;/blockquote>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Original&lt;/th>
&lt;th>Re-encoded&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_29fa20cfa5d689f9.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_3167d2319ea5dcbc.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_9c39ddbf377e17bb.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_6476173563f4acb.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_279a5772d504b296.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_2726e7e66e426c91.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_1e54bdae89d5403e.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414_hu_f7696d6ea6eb3222.jpg 1920w"
loading="lazy"
alt="original-0"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/13414.png" >original-0&lt;/a>&lt;/td>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_e1e4281a5106ad49.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_e5abee7a625fed34.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_d4f8cb8ee80023be.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_9df56fbbb22b5e55.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_4184ef5d573e2daf.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_413c45d0b513eed9.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_4f7b7dbaa0e55ae.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/13414v_hu_627670b4036867b0.jpg 1920w"
loading="lazy"
alt="re-encoded-0"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/13414v.png" >re-encoded-0&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_8201b8214c837890.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_c99e0fe9701069f9.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_78502d849aeb285.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_e30cc96697a4296a.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_30e7a2a04ed85772.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_573f9e3f910a4bc2.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_28ca9e9ae32eed02.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040_hu_5d3b1af9e578ef1a.jpg 1920w"
loading="lazy"
alt="original-1"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/22040.png" >original-1&lt;/a>&lt;/td>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_fcc816a9fb2d716c.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_120e34dbde91d5b2.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_85bc66003a38a75f.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_514da467feb6bcaf.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_a9549aa612378b47.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_c1fcac2114bb1c94.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_fcd9526743804f3.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/22040v_hu_9fbd4ea76a855a9e.jpg 1920w"
loading="lazy"
alt="re-encoded-1"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/22040v.png" >re-encoded-1&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_af848167285bf462.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_40d4a9ef0199719.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_b0974d92367da4f9.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_4d6befa049f0d91f.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_209adb520c40ae81.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_3eb385c09d6e2d32.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_f47006848b3681e9.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998_hu_78bd3d93cfc48cae.jpg 1920w"
loading="lazy"
alt="original-2"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/998.png" >original-2&lt;/a>&lt;/td>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_b880a4cab2178a7f.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_937f0e149cb3ffc4.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_17aa969fb9e6a0d2.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_7edb50b987252da6.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_3191c1a753f6189.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_ccc89210b1b1e1b8.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_4d126f15f7dbee60.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/998v_hu_cc432fc4d5d03fbd.jpg 1920w"
loading="lazy"
alt="re-encoded-2"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/998v.png" >re-encoded-2&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_19fbeab6da9d0764.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_b54a75dabe0f2385.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_e53839ea056071e6.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_3c53ff6d4269ad83.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_659d75d542915cb6.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_f25fd44439567897.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_b56a7603c1328074.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267_hu_24dd3a5ebea96f0a.jpg 1920w"
loading="lazy"
alt="original-3"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/19267.png" >original-3&lt;/a>&lt;/td>
&lt;td>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v.png"
width="1920"
height="1080"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_67c4a9088a347374.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_9b1f9022e5f438cb.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_e23866a2fabe145d.webp 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_9fedafa14d1e0275.jpg 1024w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_d880b3741a7cb3bf.webp 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_42d0642651afa4f.jpg 1536w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_d7363866b5e2055d.webp 1920w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/19267v_hu_4a0ab4ec1b7df344.jpg 1920w"
loading="lazy"
alt="re-encoded-3"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>
&lt;a class="link" href="images/19267v.png" >re-encoded-3&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Can&amp;rsquo;t notice any difference? Or the re-encoded one look better? Save the image locally and zoom in further :p. Yes, I DO put the correct pictures in the correct places.&lt;/p>&lt;/blockquote>
&lt;p>The static images are already hard to tell apart. The difference will be even smaller when you are watching the video, simply because there are motions in the video and you will not be able to focus on the details.&lt;/p>
&lt;p>So yeah, the video tracks are great. I will not touch them.&lt;/p>
&lt;h2 id="audio-tracks">Audio Tracks
&lt;/h2>&lt;h3 id="lossless-compression-problem">Lossless Compression Problem
&lt;/h3>&lt;p>However, the audio part is where I have a problem. It&amp;rsquo;s not about quality, but about size. Let me explain.&lt;/p>
&lt;p>Unlike the video tracks, which is compressed using a lossy codec (H.265/HEVC), the audio tracks are usually losslessly compressed, which means that its quality is 100% same to the original, literally. This is great for archival purposes, but it&amp;rsquo;s a overkill for most people. Because what comes with great quality is size. FLAC tracks usually have a bitrate of 1000+ kbps. One FLAC track in a single episode will typically be over 200 MB in size, as you can seen from the example below. By compressing them to 200 kbps, for example, you save 800+ kbps per track, or put it simply, cut a 200 MB track to 40 MB, which saves a lot of space. This is especially true for animes with multiple FLAC tracks. An extreme example is &lt;em>Kobayashi-san Chi no Maidragon / Miss Kobayachi&amp;rsquo;s Dragon Maid / 小林家的龙女仆&lt;/em> from team AI-Raws. It has three FLAC tracks, with a total bitrate of 3955 (1432+1294+1229) kbps. Compressing them to 600 (200*3) kbps will cut the entire episode size from 1.89 GB to 1.3 GB, which is a 36% reduction in size, saving 400 MB per episode. That&amp;rsquo;s a lot.&lt;/p>
&lt;details>
&lt;summary>MediaInfo: Kobayashi-san Chi no Maidragon episode 1 from AI-Raws&lt;/summary>
&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;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&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="n">General&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Unique&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">39274225615593709220200463866578723060&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mh">0x1D8BF0D2BC4C9D643B9E040209C9E0F4&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Complete&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">Volumes&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">pool&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">raidz&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="n">x4tb&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">public&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">anime&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">AI&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">Raws&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">Miss&lt;/span> &lt;span class="n">Kobayachi&lt;/span>&lt;span class="s1">&amp;#39;s Dragon Maid][BDRip][MKV]/[AI-Raws] 小林さんちのメイドラゴン #01 (BD HEVC 1920x1080 yuv444p10le FLAC 日本語字幕)[6FDC51A9].mkv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Matroska&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="n">version&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Version&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">File&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">1.89&lt;/span> &lt;span class="n">GiB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">25&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">11&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Overall&lt;/span> &lt;span class="n">bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Overall&lt;/span> &lt;span class="n">bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">10.8&lt;/span> &lt;span class="n">Mb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Encoded&lt;/span> &lt;span class="n">date&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">UTC&lt;/span> &lt;span class="mi">2022&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">01&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">11&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">application&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">mkvmerge&lt;/span> &lt;span class="n">v33&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">1.0&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Primrose&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libebml&lt;/span> &lt;span class="n">v1&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">3.7&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">libmatroska&lt;/span> &lt;span class="n">v1&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">5.0&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="n">Video&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">HEVC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Info&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">High&lt;/span> &lt;span class="n">Efficiency&lt;/span> &lt;span class="n">Video&lt;/span> &lt;span class="n">Coding&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="n">profile&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Format&lt;/span> &lt;span class="ne">Range&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">L4&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">High&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">V_MPEGH&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ISO&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">HEVC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">25&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">11&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">6&lt;/span> &lt;span class="mi">700&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Width&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">920&lt;/span> &lt;span class="n">pixels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Height&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">080&lt;/span> &lt;span class="n">pixels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Display&lt;/span> &lt;span class="n">aspect&lt;/span> &lt;span class="n">ratio&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Constant&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">23.976&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">24000&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">1001&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">FPS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Chroma&lt;/span> &lt;span class="n">subsampling&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">depth&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span> &lt;span class="n">bits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bits&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Pixel&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">Frame&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">0.135&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Stream&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">1.18&lt;/span> &lt;span class="n">GiB&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">62&lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">x265&lt;/span> &lt;span class="mf">2.9&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span>&lt;span class="n">d8424c799d&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="n">Windows&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">MSVC&lt;/span> &lt;span class="mi">1900&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">64&lt;/span> &lt;span class="n">bit&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="n">bit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Encoding&lt;/span> &lt;span class="n">settings&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">cpuid&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1111039&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">frame&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">threads&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">numa&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">wpp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pmode&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pme&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">psnr&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ssim&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">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">input&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">csp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">input&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1920&lt;/span>&lt;span class="n">x1080&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">interlace&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">total&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">frames&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">level&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">idc&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">high&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tier&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">uhd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">ref&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">allow&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">non&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">conformance&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">repeat&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">headers&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">annexb&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">aud&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hrd&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">hash&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">temporal&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">layers&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">open&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">gop&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">keyint&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">23&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">keyint&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">250&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">gop&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lookahead&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">bframes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">adapt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pyramid&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">bframe&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lookahead&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">25&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">lookahead&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">slices&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">scenecut&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">40&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">radl&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refresh&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">ctu&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">64&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rect&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">amp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">tu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">inter&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">depth&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">tu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">depth&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tu&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rdoq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">dynamic&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ssim&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rd&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">signhide&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tskip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">nr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">nr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">inter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">constrained&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">strong&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">smoothing&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">merge&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">modes&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">me&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">subme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">merange&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">57&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">temporal&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">mvp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">weightp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">weightb&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">analyze&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pics&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">deblock&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">sao&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sao&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">non&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">deblock&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">early&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">skip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rskip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">fast&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">tskip&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">fast&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lossless&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">splitrd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">skip&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rdpenalty&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">psy&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">2.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">psy&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rdoq&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refine&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lossless&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">cbqpoffs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">6&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crqpoffs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">6&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">rc&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">crf&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crf&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">14.5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qcomp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.60&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qpstep&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">stats&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">stats&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">maxrate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">25600&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bufsize&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10240&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vbv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">init&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.9&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crf&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">max&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">crf&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">min&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">ipratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.40&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">pbratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.30&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">aq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">aq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">strength&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.00&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">cutree&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">zone&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">strict&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cbr&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qg&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">grain&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qpmax&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">31&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">qpmin&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">vbv&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">sar&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">overscan&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">videoformat&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">colorprim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">transfer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">colormatrix&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">chromaloc&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">display&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">window&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cll&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">luma&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">luma&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1023&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">log2&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">poc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lsb&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vui&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">timing&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">info&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">vui&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hrd&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">info&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">slices&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">qp&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pps&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ref&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">list&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pps&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">multi&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">pass&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">rps&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">scenecut&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.05&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">delta&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">qp&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">aq&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">motion&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hdr&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hdr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dhdr10&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">opt&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">idr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">recovery&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sei&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">analysis&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">reuse&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">scale&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">factor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">intra&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">inter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">mv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">limit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sao&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">ctu&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">info&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lowpass&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dct&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">refine&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">mv&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">copy&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pic&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ausize&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">factor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dynamic&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">refine&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">single&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">sei&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Default&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Forced&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">Color&lt;/span> &lt;span class="nb">range&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Limited&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">Color&lt;/span> &lt;span class="n">primaries&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">BT&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">709&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="n">Audio&lt;/span> &lt;span class="c1">#1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Info&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Free&lt;/span> &lt;span class="n">Lossless&lt;/span> &lt;span class="n">Audio&lt;/span> &lt;span class="n">Codec&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">A_FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">25&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">11&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">432&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="n">channels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span> &lt;span class="n">layout&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">L&lt;/span> &lt;span class="n">R&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Sampling&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">48.0&lt;/span> &lt;span class="n">kHz&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">11.719&lt;/span> &lt;span class="n">FPS&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">4096&lt;/span> &lt;span class="n">SPF&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">depth&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">24&lt;/span> &lt;span class="n">bits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Compression&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Lossless&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Stream&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">258&lt;/span> &lt;span class="n">MiB&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libFLAC&lt;/span> &lt;span class="mf">1.2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">UTC&lt;/span> &lt;span class="mi">2007&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">09&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Default&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Forced&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&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="n">Audio&lt;/span> &lt;span class="c1">#2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Info&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Free&lt;/span> &lt;span class="n">Lossless&lt;/span> &lt;span class="n">Audio&lt;/span> &lt;span class="n">Codec&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">A_FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">25&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">11&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">294&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="n">channels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span> &lt;span class="n">layout&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">L&lt;/span> &lt;span class="n">R&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Sampling&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">48.0&lt;/span> &lt;span class="n">kHz&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">11.719&lt;/span> &lt;span class="n">FPS&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">4096&lt;/span> &lt;span class="n">SPF&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">depth&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">24&lt;/span> &lt;span class="n">bits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Compression&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Lossless&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Stream&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">233&lt;/span> &lt;span class="n">MiB&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Title&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="err">キャストコメンタリー&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libFLAC&lt;/span> &lt;span class="mf">1.2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">UTC&lt;/span> &lt;span class="mi">2007&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">09&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Default&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Forced&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&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="n">Audio&lt;/span> &lt;span class="c1">#3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Info&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Free&lt;/span> &lt;span class="n">Lossless&lt;/span> &lt;span class="n">Audio&lt;/span> &lt;span class="n">Codec&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">A_FLAC&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">25&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">11&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="mi">229&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="n">channels&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Channel&lt;/span> &lt;span class="n">layout&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">L&lt;/span> &lt;span class="n">R&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Sampling&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">48.0&lt;/span> &lt;span class="n">kHz&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Frame&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">11.719&lt;/span> &lt;span class="n">FPS&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">4096&lt;/span> &lt;span class="n">SPF&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">depth&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">24&lt;/span> &lt;span class="n">bits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Compression&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Lossless&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Stream&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">221&lt;/span> &lt;span class="n">MiB&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">11&lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Title&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="err">スタッフコメンタリー&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Writing&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libFLAC&lt;/span> &lt;span class="mf">1.2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">UTC&lt;/span> &lt;span class="mi">2007&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">09&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Default&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Forced&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&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="n">Text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Format&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">PGS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Muxing&lt;/span> &lt;span class="n">mode&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">zlib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">S_HDMV&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">PGS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Codec&lt;/span> &lt;span class="n">ID&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Info&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Picture&lt;/span> &lt;span class="n">based&lt;/span> &lt;span class="n">subtitle&lt;/span> &lt;span class="n">format&lt;/span> &lt;span class="n">used&lt;/span> &lt;span class="n">on&lt;/span> &lt;span class="n">BDs&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">HD&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">DVDs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Duration&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">24&lt;/span> &lt;span class="nb">min&lt;/span> &lt;span class="mi">30&lt;/span> &lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bit&lt;/span> &lt;span class="n">rate&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">277&lt;/span> &lt;span class="n">kb&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Count&lt;/span> &lt;span class="n">of&lt;/span> &lt;span class="n">elements&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2364&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Stream&lt;/span> &lt;span class="n">size&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mf">48.5&lt;/span> &lt;span class="n">MiB&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Language&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Japanese&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Default&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Forced&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">No&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/details>
&lt;h3 id="lossy-compression">Lossy Compression
&lt;/h3>&lt;p>But compressing them so hard will certainly bring quality losses&amp;hellip; Or will it? Well, yes, there must be quality loss when using lossy codecs, but the question is how much? Is it acceptable? You have seen that the example video tracks are compressed to 4028 kbps, which is a lot less than the original video. But the video tracks are still great. So, is the quality loss acceptable for audio tracks?&lt;/p>
&lt;p>In fact, using a modern codec at 256 kbps (or even half that), the quality loss is so little that I can almost guarantee that you will NOT hear a difference for two reasons:&lt;/p>
&lt;ol>
&lt;li>most codecs are considered &amp;ldquo;transparent&amp;rdquo; at 256 kbps or above (better codes will be &amp;ldquo;transparent&amp;rdquo; at even lower bitrate), which means that you cannot tell the difference between the original and the compressed one (more on this later);&lt;/li>
&lt;li>when watching anime, you are usually not paying attention to details in the audio, but the video. This makes the difference in audio tracks even less noticeable.&lt;/li>
&lt;/ol>
&lt;p>So we can safely compress audio without worrying about quality loss. But how much should we compress? Well, that depends on your own preference. Generally, you can choose a codec and a bitrate that is better than what is &amp;ldquo;transparent&amp;rdquo; to you.&lt;/p>
&lt;p>But hold on, how do I know what is &amp;ldquo;transparent&amp;rdquo; to me?&lt;/p>
&lt;h3 id="audio-transparency">Audio Transparency
&lt;/h3>&lt;p>There is a concept called &amp;ldquo;transparent&amp;rdquo; in audio encoding, which means the compressed audio is so good that a person cannot tell a difference between the original and the compressed one. This is usually done through ABX blind tests, where the listener is not told which one is the original and which one is the compressed one. If the listener cannot tell the difference, then the audio is considered transparent. Of course, this differs from person to person. You have to do the test with your own ears with different encoders at different bitrates to find your &amp;ldquo;transparent&amp;rdquo; threshold (which codec at which bitrate). When you find your threshold, any better codec at any better bitrate will also be transparent to you.&lt;/p>
&lt;blockquote>
&lt;p>About Bitrate and quality:&lt;/p>
&lt;p>They cannot be compared directly. For example, 320 kbps MP3 is not the same as 320 kbps AAC. (AAC is generally considered a better codec than MP3, so it can achieve the same quality with a lower bitrate.)&lt;/p>
&lt;ul>
&lt;li>
&lt;p>When dealing with the same codec (same encoder, same settings), the higher the bitrate, the better the quality. For example, MP3 at 320 kbps wll have better quality than MP3 at 256 kbps.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>But when dealing with different codecs, the bitrate is not the only factor that affects the quality. The codec itself, the encoder, encoding settings, and etc. all affect quality. For example, AAC (qaac, vbr, default settings) at 256 kbps is often said to have similar quality to MP3 at 320 kbps.&lt;/p>
&lt;/li>
&lt;/ul>&lt;/blockquote>
&lt;p>But the general consensus is that Opus is transparent at 160kbps and above, and AAC is transparent at 192kbps and above:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>A &lt;a class="link" href="https://hydrogenaud.io/index.php?topic=120007.0" target="_blank" rel="noopener"
>blind test of multiple codecs at ~192kbps VBR&lt;/a> shows that codecs at 192k is transparent to the test subject. Opus being the most transparent codec at 192k;&lt;/p>
&lt;blockquote>
&lt;p>&lt;img src="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/IgorC_192kbps1enc.png"
width="800"
height="420"
srcset="https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/IgorC_192kbps1enc_hu_f9553d78e3cd62e.webp 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/IgorC_192kbps1enc_hu_79b6d43b2148df85.jpg 480w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/IgorC_192kbps1enc_hu_ad252a688add138a.webp 800w, https://charlie0129.github.io/blog/p/thoughts-on-re-encoded-bd-animes/images/IgorC_192kbps1enc_hu_edaaf8602c54b268.jpg 800w"
loading="lazy"
alt="blind test – MultiCodec at ~192 VBR kbps"
class="gallery-image"
data-flex-grow="190"
data-flex-basis="457px"
>
&lt;/p>&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>Tests of different encoders at different bitrates published by SoundExpert show that most codecs at 128kbps and above are transparent to most test subjects. Detailed results can be found at &lt;a class="link" href="http://soundexpert.org/encoders-128-kbps;" target="_blank" rel="noopener"
>http://soundexpert.org/encoders-128-kbps;&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a class="link" href="https://hydrogenaud.io/index.php/topic,114656.0.html" target="_blank" rel="noopener"
>A topic from HydrogenAudio forum&lt;/a> even shows that Opus at ~80kbps is transparent to an average listener;&lt;/p>
&lt;blockquote>
&lt;p>It&amp;rsquo;s interesting to observe that 4 members have mentioned Opus@80kbps as point where is hard to spot artifacts for them.&lt;/p>
&lt;p>Opus ~80 kbps is roughly equivalent to LAME ~130 kbps (V5) which lands in an &amp;ldquo;excellent&amp;rdquo; area of quality (MOS 4.5+) &lt;a class="link" href="http://listening-tests.hydrogenaud.io/sebastian/mp3-128-1/results.htm" target="_blank" rel="noopener"
>http://listening-tests.hydrogenaud.io/sebastian/mp3-128-1/results.htm&lt;/a>&lt;/p>
&lt;p>So one could say that Opus 80 kbps is &amp;ldquo;excellent&amp;rdquo; at least for &lt;em>an average listener.&lt;/em> It&amp;rsquo;s clear that an experienced listeners can spot artifacts at much higher rates.&lt;/p>
&lt;blockquote>
&lt;p>I never noticed any artifacts at 80kbps though, though I haven&amp;rsquo;t tried to find any either. &lt;a class="link" href="https://hydrogenaud.io/index.php?msg=945143" target="_blank" rel="noopener"
>Quote from: noiselab on 2017-09-18 06:49:15&lt;/a>&lt;/p>&lt;/blockquote>
&lt;blockquote>
&lt;p>I tried it and for me it&amp;rsquo;s 80 kbit. &lt;a class="link" href="https://hydrogenaud.io/index.php?msg=945153" target="_blank" rel="noopener"
>Quote from: hlloyge on 2017-09-18 11:49:01&lt;/a>&lt;/p>&lt;/blockquote>
&lt;blockquote>
&lt;p>I&amp;rsquo;ll be honest, I struggled to ABX at 64kbps. 80kbps is enough for me. &lt;a class="link" href="https://hydrogenaud.io/index.php?msg=945154" target="_blank" rel="noopener"
>Quote from: Funkstar De Luxe on 2017-09-18 13:35:01&lt;/a>&lt;/p>&lt;/blockquote>
&lt;blockquote>
&lt;p>80kbps is pretty much my limit as well, can&amp;rsquo;t be bothered listening to killer samples all the time. &lt;a class="link" href="https://hydrogenaud.io/index.php?msg=945156" target="_blank" rel="noopener"
>Quote from: bstrobl on 2017-09-18 14:39:30&lt;/a>&lt;/p>&lt;/blockquote>&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>All iTunes music is delivered in AAC at 256kbps (before the arrival of Apple Music Lossless), which is an indication that most people cannot tell the difference as Apple is a company that cares about quality;&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>As you can see I am only focusing on AAC and Opus (as of lossy codecs), because AAC offers good sound quality and great compatibility while Opus has great sound quality and &amp;ldquo;okay&amp;rdquo; compatibility. Other codecs like MP3 are just not good enough to compete with them, so they will not be my choice later.&lt;/p>
&lt;h3 id="listening-samples">Listening Samples
&lt;/h3>&lt;p>Now bring your best audio equipment. Same as before, let&amp;rsquo;s listen to some samples to see if you can tell a difference, or you can do blind ABX tests by yourself to find your &amp;ldquo;transparent&amp;rdquo; threshold.&lt;/p>
&lt;p>All lossy codecs are encoded using VBR (CBR is not considered because it is not size-efficient) with a quality setting to match the resulting bitrates of different encoders as close as possible.&lt;/p>
&lt;details>
&lt;summary>Encoder versions&lt;/summary>
&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">opusenc 0.2-3-gf5f571b; libopus 1.3.1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OggEnc v2.88; libvorbis 1.3.6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">qaac 2.73; CoreAudioToolbox 7.10.9.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lame 3.100
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/details>
&lt;blockquote>
&lt;p>Note that if you cannot playback some audio tracks, make sure your browser supports this type of audio or you can download the audio tracks and play them locally. Latest versions of Chromium-based browsers should be fine. Safari might have some issues with Opus and Ogg.&lt;/p>&lt;/blockquote>
&lt;h4 id="orange--オレンジ">Orange / オレンジ
&lt;/h4>&lt;p>For the first example, I will use &lt;em>Orange / オレンジ&lt;/em> by 7!! &amp;mdash; the 2nd ending song from &lt;em>Shigatsu wa Kimi no Uso / 四月は君の嘘 / Your Lie in April / 四月是你的谎言&lt;/em> for its female voices. The audio is not complex (simpley put, do not contain many instruments), so it is easier for the encoders to achieve good results.&lt;/p>
&lt;h5 id="vocal">Vocal
&lt;/h5>&lt;p>Reference track:&lt;/p>
&lt;figure>
&lt;figcaption>flac, best, 893k&lt;/figcaption>
&lt;audio
style="margin:0 24px 0 24px;"
controls loop preload="none"
type="audio/flac"
src="media/orange-0-flac-893k.flac" />
&lt;/figure>
&lt;p>Compressed tracks:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>VBR Bitrate&lt;/th>
&lt;th>Opus&lt;/th>
&lt;th>AAC&lt;/th>
&lt;th>Ogg&lt;/th>
&lt;th>MP3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>~256k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr256, 276k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-opus-vbr256k-276k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q109, 225k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-0-qaac-q109-225k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q8, 234k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-vorbis-q8-234k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v0, 249k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-0-lame-v0-249k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~192k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr192, 212k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-opus-vbr192k-212k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q91, 171k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-0-qaac-q91-171k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q6, 178k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-vorbis-q6-178k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v2, 182k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-0-lame-v2-182k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~160k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr160, 180k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-opus-vbr160k-180k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q82, 142k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-0-qaac-q82-142k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q5, 153k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-vorbis-q5-153k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v4, 148k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-0-lame-v4-148k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~128k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr128, 146k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-opus-vbr128k-146k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q64, 113k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-0-qaac-q64-113k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q4, 131k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-vorbis-q4-131k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v5, 126k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-0-lame-v5-126k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~96k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr96, 111k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-opus-vbr96k-111k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q45, 86k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-0-qaac-q45-86k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q2, 92k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-vorbis-q2-92k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v7, 98k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-0-lame-v7-98k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~64k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr64, 76k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-opus-vbr64k-76k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q27, 66k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-0-qaac-q27-66k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q0, 61k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-0-vorbis-q0-61k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v9, 69k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-0-lame-v9-69k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h5 id="vocal-with-simple-instruments">Vocal with simple instruments
&lt;/h5>&lt;p>Reference track:&lt;/p>
&lt;figure>
&lt;figcaption>flac, best, 920k&lt;/figcaption>
&lt;audio
style="margin:0 24px 0 24px;"
controls loop preload="none"
type="audio/flac"
src="media/orange-1-flac-920k.flac" />
&lt;/figure>
&lt;p>Compressed tracks:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>VBR Bitrate&lt;/th>
&lt;th>Opus&lt;/th>
&lt;th>AAC&lt;/th>
&lt;th>Ogg&lt;/th>
&lt;th>MP3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>~256k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr256, 274k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-opus-vbr256k-274k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q109, 236k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-1-qaac-q109-236k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q8, 239k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-vorbis-q8-239k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v0, 256k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-1-lame-v0-256k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~192k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr192, 210k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-opus-vbr192k-210k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q91, 180k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-1-qaac-q91-180k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q6, 183k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-vorbis-q6-183k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v2, 183k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-1-lame-v2-183k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~160k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr160, 179k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-opus-vbr160k-179k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q82, 149k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-1-qaac-q82-149k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q5, 158k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-vorbis-q5-158k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v4, 147k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-1-lame-v4-147k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~128k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr128, 144k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-opus-vbr128k-144k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q64, 119k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-1-qaac-q64-119k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q4, 134k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-vorbis-q4-134k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v5, 126k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-1-lame-v5-126k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~96k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr96, 109k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-opus-vbr96k-109k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q45, 90k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-1-qaac-q45-90k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q2, 95k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-vorbis-q2-95k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v7, 100k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-1-lame-v7-100k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~64k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr64, 75k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-opus-vbr64k-75k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q27, 66k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-1-qaac-q27-66k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q0, 62k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-1-vorbis-q0-62k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v9, 70k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-1-lame-v9-70k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h5 id="vocal-with-more-complex-instruments">Vocal with more complex instruments
&lt;/h5>&lt;p>Reference track:&lt;/p>
&lt;figure>
&lt;figcaption>flac, best, 1089k&lt;/figcaption>
&lt;audio
style="margin:0 24px 0 24px;"
controls loop preload="none"
type="audio/flac"
src="media/orange-2-flac-1089k.flac" />
&lt;/figure>
&lt;p>Compressed tracks:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>VBR Bitrate&lt;/th>
&lt;th>Opus&lt;/th>
&lt;th>AAC&lt;/th>
&lt;th>Ogg&lt;/th>
&lt;th>MP3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>~256k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr256, 262k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-opus-vbr256k-262k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q109, 270k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-2-qaac-q109-270k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q8, 266k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-vorbis-q8-266k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v0, 288k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-2-lame-v0-288k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~192k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr192, 200k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-opus-vbr192k-200k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q91, 200k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-2-qaac-q91-200k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q6, 201k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-vorbis-q6-201k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v2, 206k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-2-lame-v2-206k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~160k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr160, 169k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-opus-vbr160k-169k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q82, 165k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-2-qaac-q82-165k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q5, 170k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-vorbis-q5-170k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v4, 154k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-2-lame-v4-154k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~128k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr128, 136k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-opus-vbr128k-136k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q64, 129k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-2-qaac-q64-129k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q4, 142k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-vorbis-q4-142k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v5, 134k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-2-lame-v5-134k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~96k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr96, 102k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-opus-vbr96k-102k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q45, 96k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-2-qaac-q45-96k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q2, 97k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-vorbis-q2-97k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v7, 101k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-2-lame-v7-101k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~64k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr64, 69k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-opus-vbr64k-69k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q27, 69k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/orange-2-qaac-q27-69k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q0, 62k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/orange-2-vorbis-q0-62k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v9, 68k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/orange-2-lame-v9-68k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="vision">Vision
&lt;/h4>&lt;p>The second song will be a more complex, or demanding one &amp;mdash; &lt;em>Vision&lt;/em> by 中島岬:&lt;/p>
&lt;p>&lt;em>Vision&lt;/em> reference track:&lt;/p>
&lt;figure>
&lt;figcaption>flac, best, 1058k&lt;/figcaption>
&lt;audio
style="margin:0 24px 0 24px;"
controls loop preload="none"
type="audio/flac"
src="media/vision-flac-1058k.flac" />
&lt;/figure>
&lt;p>&lt;em>Vision&lt;/em> compressed tracks:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>VBR Bitrate&lt;/th>
&lt;th>Opus&lt;/th>
&lt;th>AAC&lt;/th>
&lt;th>Ogg&lt;/th>
&lt;th>MP3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>~256k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr256, 260k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-opus-vbr256k-260k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q109, 297k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/vision-qaac-q109-297k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q8, 290k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-vorbis-q8-290k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v0, 288k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/vision-lame-v0-288k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~192k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr192, 197k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-opus-vbr192k-197k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q91, 218k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/vision-qaac-q91-218k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q6, 212k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-vorbis-q6-212k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v2, 209k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/vision-lame-v2-209k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~160k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr160, 165k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-opus-vbr160k-165k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q82, 178k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/vision-qaac-q82-178k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q5, 175k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-vorbis-q5-175k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v4, 156k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/vision-lame-v4-156k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~128k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr128, 133k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-opus-vbr128k-133k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q64, 142k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/vision-qaac-q64-142k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q4, 139k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-vorbis-q4-139k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v5, 135k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/vision-lame-v5-135k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~96k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr96, 100k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-opus-vbr96k-100k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q45, 109k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/vision-qaac-q45-109k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q2, 99k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-vorbis-q2-99k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v7, 109k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/vision-lame-v7-109k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~64k&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>opusenc, vbr64, 66k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-opus-vbr64k-66k.opus" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>qaac, q27, 77k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mp4" src="media/vision-qaac-q27-77k.m4a" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>oggenc2, q0, 63k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/ogg" src="media/vision-vorbis-q0-63k.ogg" />&lt;/figure>&lt;/td>
&lt;td>&lt;figure>&lt;figcaption>lame, v9, 73k&lt;/figcaption>&lt;audio style="margin:0 24px 0 24px;" controls loop preload="none" type="audio/mpeg" src="media/vision-lame-v9-73k.mp3" />&lt;/figure>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>You can clearly see the AAC and MP3 encoders tend to give a higher bitrate than target bitrate in VBR mode to handle complex songs.&lt;/p>&lt;/blockquote>
&lt;blockquote>
&lt;p>Want to listen to your own song at differnet bitrates? Use &lt;a class="link" href="convert.js" >this script&lt;/a> to encode it to all formats. This is exactly the same script that I wrote to encode the songs and generate the table above.&lt;/p>&lt;/blockquote>
&lt;p>Now, you should have your own understanding of the different codecs: above which bitrate of which codec is transparent to you.&lt;/p>
&lt;h3 id="my-take-on-the-codecs">My take on the codecs
&lt;/h3>&lt;p>Here is my opinion: &lt;strong>since ~192k Opus is already transparent to me (and to most people), why use 1000+ kbps FLAC? I can save a lot of space, without even tell a difference!&lt;/strong> Unless you have outstandingly gifted ears with high-end equipments, or you do heavy post-processing on audio, you should and will be fine with a transparent lossy encoding.&lt;/p>
&lt;p>Also, most audio in animes is voice, which is usually not complex for encoders, so the actual bitrate can be even lower to sound good.&lt;/p>
&lt;h2 id="still-writing">Still writing&amp;hellip;
&lt;/h2>&lt;p>The rest is still being written&amp;hellip;&lt;/p></description></item><item><title>Docker and ZFS - A Tough Pair</title><link>https://charlie0129.github.io/blog/p/docker-and-zfs-a-tough-pair/</link><pubDate>Sun, 07 Aug 2022 08:54:00 +0800</pubDate><guid>https://charlie0129.github.io/blog/p/docker-and-zfs-a-tough-pair/</guid><description>&lt;p>Outline:&lt;/p>
&lt;ul>
&lt;li>Know ZFS filesystem and Docker&amp;rsquo;s &lt;code>zfs&lt;/code> storage driver&lt;/li>
&lt;li>How Docker&amp;rsquo;s &lt;code>zfs&lt;/code> driver works and Why it is slow&lt;/li>
&lt;li>Why we can&amp;rsquo;t directly use &lt;code>overlayfs&lt;/code> on ZFS
&lt;ul>
&lt;li>Why &lt;code>overlayfs&lt;/code> don&amp;rsquo;t accept remote filesystems&lt;/li>
&lt;li>Why ZFS is identified as a remote filesystem&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>How to actually solve the problem&lt;/li>
&lt;/ul>
&lt;p>We will dive into the source code of &lt;a class="link" href="https://github.com/moby/moby" target="_blank" rel="noopener"
>moby&lt;/a>, &lt;a class="link" href="https://github.com/openzfs/zfs" target="_blank" rel="noopener"
>OpenZFS&lt;/a>, and Linux &lt;a class="link" href="https://github.com/torvalds/linux" target="_blank" rel="noopener"
>kernel&lt;/a> to find out.&lt;/p>
&lt;p>&lt;strong>Note:&lt;/strong> I admit this blog is not so beginner-friendly, which requires some prerequisites, otherwise you may have a hard time reading it though. I will give some questions or concepts after each prerequisite to help you know your understanding is enough on this topic.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>general computer/unix concepts&lt;/strong> (&lt;a class="link" href="https://en.wikipedia.org/wiki/Device_file#Block_devices" target="_blank" rel="noopener"
>block devices&lt;/a>, &lt;a class="link" href="https://en.wikipedia.org/wiki/Copy-on-write" target="_blank" rel="noopener"
>copy-on-write&lt;/a>, &lt;a class="link" href="https://en.wikipedia.org/wiki/Mount_%28computing%29" target="_blank" rel="noopener"
>mount points&lt;/a>)&lt;/li>
&lt;li>&lt;strong>basic filesystem concepts&lt;/strong> (difference between block devices and filesystems, common filesystems, &lt;a class="link" href="https://www.starlab.io/blog/introduction-to-the-linux-virtual-filesystem-vfs-part-i-a-high-level-tour" target="_blank" rel="noopener"
>basic understanding of Linux Virtual Filesystem&lt;/a>)&lt;/li>
&lt;li>&lt;strong>the basics of ZFS filesystem&lt;/strong> (&lt;a class="link" href="https://docs.oracle.com/cd/E18752_01/html/819-5461/ftyue.html" target="_blank" rel="noopener"
>terminologies like datasets and snapshots&lt;/a>, &lt;a class="link" href="https://manpages.ubuntu.com/manpages/jammy/man8/zfs-rollback.8.htmlunionfs" target="_blank" rel="noopener"
>what are rollbacks&lt;/a>)&lt;/li>
&lt;li>&lt;strong>Docker images&lt;/strong> (&lt;a class="link" href="https://docs.docker.com/storage/storagedriver/#images-and-layers" target="_blank" rel="noopener"
>what are image layers&lt;/a>, when they are created/deleted, how it works with UnionFS)&lt;/li>
&lt;/ul>
&lt;h2 id="background">Background
&lt;/h2>&lt;h3 id="what-is-zfs">What is ZFS
&lt;/h3>&lt;p>Described as &lt;em>The last word in filesystems&lt;/em>, ZFS is scalable, and includes extensive protection against data corruption, support for high storage capacities, efficient data compression, integration of the concepts of filesystem and volume management, snapshots and copy-on-write clones, continuous integrity checking and automatic repair, RAID-Z, native NFSv4 ACLs, and can be very precisely configured. &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;p>By saying ZFS, I am referring to OpenZFS on Linux and FreeBSD: &lt;a class="link" href="https://openzfs.github.io/openzfs-docs/index.html" target="_blank" rel="noopener"
>OpenZFS Documentation&lt;/a>&lt;/p>
&lt;p>ZFS is a great and sophisticated filesystem, really robust and stable. It never failed my expectations. I personally use ZFS on my personal devices, whenever possible, e.g. laptops (Ubuntu Desktop - for its built-in support for ZFS), NAS (TrueNAS SCALE), and servers (Proxmox VE and Ubuntu Server).&lt;/p>
&lt;h3 id="what-is-a-docker-storage-driver">What is a Docker storage driver
&lt;/h3>&lt;p>Docker uses storage drivers to store image layers, and to store data in the writable layer of a container. The container’s writable layer does not persist after the container is deleted, but is suitable for storing ephemeral data that is generated at runtime. Storage drivers are optimized for space efficiency, but (depending on the storage driver) write speeds are lower than native file system performance, especially for storage drivers that use a copy-on-write filesystem. Write-intensive applications, such as database storage, are impacted by a performance overhead, particularly if pre-existing data exists in the read-only layer. &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&lt;/p>
&lt;p>By default, Docker will use &lt;code>overlay2&lt;/code> whenever possible for all Linux distributions.&lt;/p>
&lt;h3 id="zfs-and-docker-storage-driver">ZFS and Docker storage driver?
&lt;/h3>&lt;p>The Docker Engine provides a &lt;code>zfs&lt;/code> storage drivers on Linux, which requires a ZFS filesystem, allowing for advanced options, such as creating snapshots, but require more maintenance and setup.&lt;/p>
&lt;p>According to Docker docs, the &lt;code>zfs&lt;/code> storage driver has the following advantages &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> :&lt;/p>
&lt;ul>
&lt;li>Avoids the container’s writable layer grow too large in write-heavy workloads.&lt;/li>
&lt;li>Performs better for write-heavy workloads (though not as well as Docker volumes).&lt;/li>
&lt;li>A good choice for high-density workloads such as PaaS.&lt;/li>
&lt;/ul>
&lt;p>Hmm, sounds good, right? Well, keep reading. If it is that good, this blog wouldn&amp;rsquo;t exist at the first place.&lt;/p>
&lt;blockquote>
&lt;p>In this blog, &lt;code>zfs&lt;/code> refers to Docker&amp;rsquo;s &lt;code>zfs&lt;/code> storage driver, mostly, but may also refer to ZFS filesystem. You should be able to distinguish them by context.&lt;/p>&lt;/blockquote>
&lt;h2 id="whats-the-problem">What&amp;rsquo;s the problem?
&lt;/h2>&lt;p>There is one single problem with ZFS that bothered me since the very beginning: &lt;strong>Docker&lt;/strong>.&lt;/p>
&lt;p>When using Docker on ZFS, one can only use its &lt;code>zfs&lt;/code> driver (no, you cannot use &lt;code>overlay2&lt;/code> directly. We will see why later.). Although Docker docs &lt;em>proudly&lt;/em> advertise its &lt;code>zfs&lt;/code> driver as something high-performance:&lt;/p>
&lt;blockquote>
&lt;p>zfs is a good choice for high-density workloads such as PaaS. &lt;sup id="fnref1:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>&lt;/p>&lt;/blockquote>
&lt;p>But in practice, this thing is &lt;em>slow as hell&lt;/em>, specifically, when creating image layers. Build times can go from &lt;strong>a fraction of a second&lt;/strong> on &lt;code>overlay2&lt;/code> to &lt;strong>several minutes&lt;/strong> on &lt;code>zfs&lt;/code>!&lt;/p>
&lt;p>&lt;strong>It is several magnitudes slower, a complete disaster.&lt;/strong>&lt;/p>
&lt;p>Let&amp;rsquo;s take the Dockerfile in &lt;a class="link" href="https://github.com/kubevela/kube-trigger" target="_blank" rel="noopener"
>kube-trigger&lt;/a> (a project that I worked on recently) as an example.&lt;/p>
&lt;details>
&lt;summary>Click to see the complete Dockerfile&lt;/summary>
&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-Dockerfile" data-lang="Dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Copyright 2022 The KubeVela Authors.&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="c">#&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="c"># Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;);&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="c"># you may not use this file except in compliance with the License.&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="c"># You may obtain a copy of the License at&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="c">#&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="c"># http://www.apache.org/licenses/LICENSE-2.0&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="c">#&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="c"># Unless required by applicable law or agreed to in writing, software&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="c"># distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS,&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="c"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&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="c"># See the License for the specific language governing permissions and&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="c"># limitations under the License.&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Note: the ${BIN} needs to be replaced with the actual binary,&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="c"># otherwise it won&amp;#39;t work. Refer to Makefile for how it can be done.&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ARG&lt;/span> &lt;span class="nv">BUILD_IMAGE&lt;/span>&lt;span class="o">=&lt;/span>golang:1.17&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="k">ARG&lt;/span> &lt;span class="nv">BASE_IMAGE&lt;/span>&lt;span class="o">=&lt;/span>gcr.io/distroless/static:nonroot&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Force native build platform, and cross-build to target platform later.&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="k">FROM&lt;/span>&lt;span class="s"> --platform=${BUILDPLATFORM:-linux/amd64} ${BUILD_IMAGE} as builder&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /workspace&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="k">COPY&lt;/span> go.mod go.mod&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="k">COPY&lt;/span> go.sum go.sum&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ARG&lt;/span> GOPROXY&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="k">ENV&lt;/span> &lt;span class="nv">GOPROXY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">GOPROXY&lt;/span>&lt;span class="si">}&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="k">RUN&lt;/span> go mod download&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> build build&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="k">COPY&lt;/span> hack hack&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="k">COPY&lt;/span> cmd cmd&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="k">COPY&lt;/span> api api&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="k">COPY&lt;/span> controllers controllers&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="k">COPY&lt;/span> pkg pkg&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ARG&lt;/span> TARGETARCH&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="k">ARG&lt;/span> ARCH&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="k">ARG&lt;/span> TARGETOS&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="k">ARG&lt;/span> OS&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="k">ARG&lt;/span> VERSION&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="k">ARG&lt;/span> GOFLAGS&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="k">ARG&lt;/span> DIRTY_BUILD&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="k">ARG&lt;/span> ENTRY&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nv">ARCH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">TARGETARCH&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">ARCH&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="nv">amd64&lt;/span>&lt;span class="si">}}&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> &lt;span class="nv">OS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">TARGETOS&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">OS&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="nv">linux&lt;/span>&lt;span class="si">}}&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> &lt;span class="nv">OUTPUT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">BIN&lt;/span>&lt;span class="si">}&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> &lt;span class="nv">VERSION&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">VERSION&lt;/span>&lt;span class="si">}&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> &lt;span class="nv">GOFLAGS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">GOFLAGS&lt;/span>&lt;span class="si">}&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> /bin/sh build/build.sh &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="si">${&lt;/span>&lt;span class="nv">ENTRY&lt;/span>&lt;span class="si">}&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> ${BASE_IMAGE}&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="k">WORKDIR&lt;/span>&lt;span class="s"> /&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="k">COPY&lt;/span> --from&lt;span class="o">=&lt;/span>builder /workspace/&lt;span class="si">${&lt;/span>&lt;span class="nv">BIN&lt;/span>&lt;span class="si">}&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="k">ENTRYPOINT&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/${BIN}&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/details>
&lt;p>We will focus on L39-L46:&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-Dockerfile" data-lang="Dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">ARG&lt;/span> TARGETARCH&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="k">ARG&lt;/span> ARCH&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="k">ARG&lt;/span> TARGETOS&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="k">ARG&lt;/span> OS&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="k">ARG&lt;/span> VERSION&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="k">ARG&lt;/span> GOFLAGS&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="k">ARG&lt;/span> DIRTY_BUILD&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="k">ARG&lt;/span> ENTRY&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You might be thinking, this is just some build args, so what? Yes, this part almost does nothing (creates some image layers), and should finish immediately. That&amp;rsquo;s exactly the case on &lt;code>overlay2&lt;/code>, but not on &lt;code>zfs&lt;/code>, which will &lt;strong>take minutes&lt;/strong>!&lt;/p>
&lt;p>Such slow build times are driving me crazy.&lt;/p>
&lt;h2 id="why-zfs-driver-is-so-slow">Why &lt;code>zfs&lt;/code> driver is so slow?
&lt;/h2>&lt;h3 id="how-zfs-storage-driver-works">How ZFS storage driver works?
&lt;/h3>&lt;p>When using docker on a &lt;code>zfs&lt;/code> dataset, the only option is Docker&amp;rsquo;s &lt;code>zfs&lt;/code> driver, which uses ZFS dataset operations to create layered filesystems. The &lt;code>zfs&lt;/code> storage driver for Docker stores &lt;strong>each layer of each image&lt;/strong> as a separate legacy dataset. Even just a handful of images can result in a huge number of layers, each layer corresponding to a &lt;code>legacy&lt;/code> ZFS dataset. As a result, there are hundreds of datasets created when only running a dozen containers.&lt;/p>
&lt;blockquote>
&lt;p>The base layer of an image is a ZFS filesystem. Each child layer is a ZFS clone based on a ZFS snapshot of the layer below it. A container is a ZFS clone based on a ZFS Snapshot of the top layer of the image it’s created from. &lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>&lt;/p>&lt;/blockquote>
&lt;h3 id="wheres-the-bottleneck">Where&amp;rsquo;s the bottleneck?
&lt;/h3>&lt;p>Although when building images it do not have to deal with such many datasets. It will still spend a fair amount of time mounting and unmounting these datasets (can be seen from Docker debug logs).&lt;/p>
&lt;p>We can take a look at the code from Docker daemon (&lt;code>moby/moby&lt;/code>).&lt;/p>
&lt;p>Mount will happen (if necessary) whenever &lt;code>Get&lt;/code> is called:&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: daemon/graphdriver/zfs/zfs.go&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/moby/moby/blob/7e44b7cddd43b1771a44a2dd56548627e491c950/daemon/graphdriver/zfs/zfs.go#L365-L408&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Get returns the mountpoint for the given id after creating the target directories if necessary.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Driver&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">Get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountLabel&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">_&lt;/span> &lt;span class="nx">containerfs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ContainerFS&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">retErr&lt;/span> &lt;span class="kt">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">locker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">locker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mountpoint&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">mountPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">count&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ctr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Increment&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">count&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">containerfs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewLocalContainerFS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">retErr&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">c&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ctr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Decrement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">c&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">mntErr&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unmount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">mntErr&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logrus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;storage-driver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;zfs&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Error unmounting %v: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mntErr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">rmErr&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Rmdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">rmErr&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">IsNotExist&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rmErr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logrus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;storage-driver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;zfs&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Debugf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Failed to remove %s: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rmErr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}()&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="nx">filesystem&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">zfsPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">FormatMountLabel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountLabel&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logrus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;storage-driver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;zfs&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Debugf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">`mount(&amp;#34;%s&amp;#34;, &amp;#34;%s&amp;#34;, &amp;#34;%s&amp;#34;)`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">filesystem&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">)&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="nx">root&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">idMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">RootPair&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Create the target directories if they don&amp;#39;t exist&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">idtools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">MkdirAllAndChown&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mo">0755&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">root&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">mount&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Mount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filesystem&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;zfs&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">errors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Wrap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;error creating zfs mount&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="c1">// this could be our first mount after creation of the filesystem, and the root dir may still have root&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// permissions instead of the remapped root uid:gid (if user namespaces are enabled):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">root&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Chown&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;error modifying zfs mountpoint (%s) directory ownership: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="k">return&lt;/span> &lt;span class="nx">containerfs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewLocalContainerFS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Unmount will happen whenever &lt;code>Put&lt;/code> is called:&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: daemon/graphdriver/zfs/zfs.go&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/moby/moby/blob/7e44b7cddd43b1771a44a2dd56548627e491c950/daemon/graphdriver/zfs/zfs.go#L410-L431&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Put removes the existing mountpoint for the given id if it exists.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Driver&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">Put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">error&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">locker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">locker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mountpoint&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">mountPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">count&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ctr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Decrement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">count&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="nx">logger&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">logrus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;storage-driver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;zfs&amp;#34;&lt;/span>&lt;span class="p">)&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="nx">logger&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Debugf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">`unmount(&amp;#34;%s&amp;#34;)`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">)&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="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unmount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MNT_DETACH&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logger&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Warnf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Failed to unmount %s mount %s: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Rmdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">IsNotExist&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logger&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Debugf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Failed to remove %s mount point %s: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mountpoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Although Docker will not mount a filesystem twice, changes still exist when consecutive &lt;code>Get/Put&lt;/code> call happens.&lt;/p>
&lt;p>I am not an OpenZFS developer, but it seems to me that there is a bottleneck with ZFS with such frequent mount/unmount actions (with a large mount datasets and snapshots).&lt;/p>
&lt;p>As you can see, Docker is already optimizing this situation by using the mount syscall directly (instead of calling user-space mount command, which will again, after going to kernel-space from user-space, require the kernel to call the zfs mount binary in user-space, due to ZFS&amp;rsquo;s license issues with the &lt;code>vfs_mount&lt;/code> in kernel).&lt;/p>
&lt;h2 id="one-possible-solution">One possible solution
&lt;/h2>&lt;p>So there is not much to optimize in &lt;code>zfs&lt;/code> storage drivers. It is the actual zfs mount process that is slowing image build times down. Now, the problem with Docker&amp;rsquo;s &lt;code>zfs&lt;/code> storage driver is clear. There are two options left:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>optimize ZFS mount times&lt;/p>
&lt;/li>
&lt;li>
&lt;p>just get rid of &lt;code>zfs&lt;/code> storage driver&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Of course, you can also grab another disk in &lt;code>ext4&lt;/code> as use &lt;code>overlayfs&lt;/code> on top of it. But I only have &lt;code>zfs&lt;/code>-formatted disks, so I only have the above two options.&lt;/p>
&lt;p>The first one &amp;ldquo;optimize ZFS mount times&amp;rdquo; isn&amp;rsquo;t really an option. Currently, I don&amp;rsquo;t have the expertise or time to work on OpenZFS.&lt;/p>
&lt;p>With that out of the way, we only have one option left: &amp;ldquo;do not use &lt;code>zfs&lt;/code> storage driver&amp;rdquo;, i.e., use &lt;code>overlay2&lt;/code> on &lt;code>zfs&lt;/code> datasets.&lt;/p>
&lt;h2 id="but-it-doesnt-work">But it doesn&amp;rsquo;t work
&lt;/h2>&lt;p>Now, the problem is, how do we use &lt;code>overlay2&lt;/code> storage driver on a ZFS filesystem (dataset)?&lt;/p>
&lt;p>&lt;strong>Simply put, that&amp;rsquo;s not possible (directly).&lt;/strong> ZFS makes use of &lt;code>d_revalidate&lt;/code> . Having &lt;code>d_revalidate&lt;/code> set to not &lt;code>NULL&lt;/code> will make &lt;code>overlayfs&lt;/code> refuse to work.&lt;/p>
&lt;p>But why? To understand, we need to analyze some source code from OpenZFS and Linux kernel.&lt;/p>
&lt;h3 id="whats-d_revalidate">What&amp;rsquo;s &lt;code>d_revalidate&lt;/code>?
&lt;/h3>&lt;p>&lt;code>d_revalidate&lt;/code> is defined in Linux kernel &lt;code>include/linux/dcache.h&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: include/linux/dcache.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/torvalds/linux/blob/1612c382ffbdf1f673caec76502b1c00e6d35363/include/linux/dcache.h#L128
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">dentry_operations&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">d_revalidate&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">dentry&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// The rest are omitted.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="n">____cacheline_aligned&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The kernel documentation has a nice description on &lt;code>d_revalidate&lt;/code>.&lt;/p>
&lt;p>TL;DR: &lt;code>d_revalidate&lt;/code> is typically used with network filesystems, and is called called when the VFS needs to revalidate a &lt;code>dentry&lt;/code>, marking this &lt;code>dentry&lt;/code> is still valid or not, to prevent things change without the client being aware of it.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&lt;code>d_revalidate&lt;/code> is called called when the VFS needs to revalidate a dentry.&lt;/strong> This is called whenever a name look-up finds a dentry in the dcache. Most local filesystems leave this as NULL, because all their dentries in the dcache are valid. &lt;strong>Network filesystems are different since things can change on the server without the client necessarily being aware of it.&lt;/strong>&lt;/p>
&lt;p>This function should return a positive value if the dentry is still valid, and zero or a negative error code if it isn’t.&lt;/p>
&lt;p>d_revalidate may be called in rcu-walk mode (flags &amp;amp; LOOKUP_RCU). If in rcu-walk mode, the filesystem must revalidate the dentry without blocking or storing to the dentry, d_parent and d_inode should not be used without care (because they can change and, in d_inode case, even become NULL under us).&lt;/p>
&lt;p>If a situation is encountered that rcu-walk cannot handle, return -ECHILD and it will be called again in ref-walk mode.&lt;/p>
&lt;p>&lt;em>Excerpt from: &lt;a class="link" href="https://docs.kernel.org/filesystems/vfs.html#struct-dentry-operations" target="_blank" rel="noopener"
>Overview of the Linux Virtual File System — The Linux Kernel documentation&lt;/a>&lt;/em>&lt;/p>&lt;/blockquote>
&lt;h3 id="why-use-d_revalidate">Why use &lt;code>d_revalidate&lt;/code>?
&lt;/h3>&lt;p>ZFS makes use of &lt;code>d_revalidate&lt;/code> to invalidate &lt;code>dcache&lt;/code> after rolling back.&lt;/p>
&lt;p>See? This is the same situation as the kernel doc describes. When a roll back happens, the underlying files are changed, but the &lt;code>dcache&lt;/code> it not updated, so we need to use &lt;code>d_revalidate&lt;/code> to mark it as invalid.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: module/os/linux/zfs/zpl_inode.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/openzfs/zfs/blob/1d3ba0bf01020f5459b1c28db3979129088924c0/module/os/linux/zfs/zpl_inode.c#L701-L739
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#ifdef HAVE_D_REVALIDATE_NAMEIDATA
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="nf">zpl_revalidate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">dentry&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">nameidata&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">nd&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">flags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">nd&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="n">nd&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="nl">flags&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="nf">zpl_revalidate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">dentry&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">flags&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif &lt;/span>&lt;span class="cm">/* HAVE_D_REVALIDATE_NAMEIDATA */&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="cm">/* CSTYLED */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">zfsvfs_t&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">zfsvfs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_fs_info&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">error&lt;/span>&lt;span class="p">;&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">flags&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">LOOKUP_RCU&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ECHILD&lt;/span>&lt;span class="p">);&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="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * After a rollback negative dentries created before the rollback
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * time must be invalidated. Otherwise they can obscure files which
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * are only present in the rolled back dataset.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_inode&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">spin_lock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_lock&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">error&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">time_before&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_time&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">zfsvfs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">z_rollback_time&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">spin_unlock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_lock&lt;/span>&lt;span class="p">);&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * The dentry may reference a stale inode if a mounted file system
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * was rolled back to a point in time where the object didn&amp;#39;t exist.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_inode&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nf">ITOZ&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_inode&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">z_is_stale&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&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="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>d_revalidate&lt;/code> is set to the &lt;code>zpl_revalidate&lt;/code> function that we have seen 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;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-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: module/os/linux/zfs/zpl_inode.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/openzfs/zfs/blob/1d3ba0bf01020f5459b1c28db3979129088924c0/module/os/linux/zfs/zpl_inode.c#L830-L832
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">dentry_operations_t&lt;/span> &lt;span class="n">zpl_dentry_operations&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">d_revalidate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">zpl_revalidate&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>But why is having &lt;code>d_revalidate&lt;/code> set to not &lt;code>NULL&lt;/code> will make &lt;code>overlayfs&lt;/code> refuse to work?&lt;/p>
&lt;h3 id="how-overlayfs-refuses-d_revalidate-enabled-fs">How &lt;code>overlayfs&lt;/code> refuses &lt;code>d_revalidate&lt;/code>-enabled fs
&lt;/h3>&lt;p>To understand how and why How &lt;code>overlayfs&lt;/code> refuses &lt;code>d_revalidate&lt;/code>-enabled fs, let&amp;rsquo;s turn our focus to the Linux kernel.&lt;/p>
&lt;h4 id="dcache_op_revalidate-flag-is-set">&lt;code>DCACHE_OP_REVALIDATE&lt;/code> flag is set
&lt;/h4>&lt;p>If a &lt;code>dentry&lt;/code> has &lt;code>d_revalidate&lt;/code> set to not &lt;code>NULL&lt;/code>, which is the case with ZFS, kernel will mark &lt;code>DCACHE_OP_REVALIDATE&lt;/code> in its &lt;code>d_flags&lt;/code>. A &lt;code>d_flags&lt;/code> is just flags to tell what operations that this &lt;code>dentry&lt;/code> supports, and &lt;code>DCACHE_OP_REVALIDATE&lt;/code> means it supports &lt;code>d_revalidate&lt;/code> operations.&lt;/p>
&lt;p>Now in our case, ZFS uses &lt;code>d_revalidate&lt;/code>, so our &lt;code>d_flags&lt;/code> have a &lt;code>DCACHE_OP_REVALIDATE&lt;/code> present.&lt;/p>
&lt;p>&lt;em>Keep this in mind.&lt;/em> This flag will cause &lt;code>overlayfs&lt;/code> to identify it as a remote fs. You will see why 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;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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: fs/dcache.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/torvalds/linux/blob/3bc1bc0b59d04e997db25b84babf459ca1cd80b7/fs/dcache.c#L1915-L1943
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="nf">d_set_d_op&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">dentry&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">dentry_operations&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">WARN_ON_ONCE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_op&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">WARN_ON_ONCE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">DCACHE_OP_HASH&lt;/span> &lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCACHE_OP_COMPARE&lt;/span> &lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCACHE_OP_REVALIDATE&lt;/span> &lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCACHE_OP_WEAK_REVALIDATE&lt;/span> &lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCACHE_OP_DELETE&lt;/span> &lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCACHE_OP_REAL&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">op&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_hash&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">DCACHE_OP_HASH&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_compare&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">DCACHE_OP_COMPARE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_revalidate&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">DCACHE_OP_REVALIDATE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_weak_revalidate&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">DCACHE_OP_WEAK_REVALIDATE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_delete&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">DCACHE_OP_DELETE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_prune&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">DCACHE_OP_PRUNE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_real&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">DCACHE_OP_REAL&lt;/span>&lt;span class="p">;&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="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">EXPORT_SYMBOL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">d_set_d_op&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="mounting-process-of-overlayfs">Mounting process of &lt;code>overlayfs&lt;/code>
&lt;/h4>&lt;p>To understand how &lt;code>overlayfs&lt;/code> rejects &lt;code>d_revalidate&lt;/code> enabled fs, we need to look at the code that mounts &lt;code>overlayfs&lt;/code>.&lt;/p>
&lt;p>When we mount a &lt;code>overlayfs&lt;/code>, we call &lt;code>ovl_mount()&lt;/code> in the kernel &lt;code>fs/overlayfs&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: fs/overlayfs/super.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/torvalds/linux/blob/3bc1bc0b59d04e997db25b84babf459ca1cd80b7/fs/overlayfs/super.c#L2158-L2162
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">dentry&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nf">ovl_mount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">file_system_type&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">fs_type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">flags&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">dev_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">raw_data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nf">mount_nodev&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fs_type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">raw_data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ovl_fill_super&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>ovl_fill_super()&lt;/code> will be called to fill the dir (&lt;code>workdir&lt;/code>) where &lt;code>overlayfs&lt;/code> mounts, which will call &lt;code>ovl_get_workdir&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;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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: fs/overlayfs/super.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/torvalds/linux/blob/3bc1bc0b59d04e997db25b84babf459ca1cd80b7/fs/overlayfs/super.c#L2047-L2079
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="nf">ovl_fill_super&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">super_block&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">silent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Omitted till L2048
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">upperdir&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">struct&lt;/span> &lt;span class="n">super_block&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">upper_sb&lt;/span>&lt;span class="p">;&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="n">err&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">EINVAL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">workdir&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">pr_err&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;missing &amp;#39;workdir&amp;#39;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">out_err&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="n">err&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">ovl_get_upper&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ofs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">layers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">upperpath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">out_err&lt;/span>&lt;span class="p">;&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="n">upper_sb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">ovl_upper_mnt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">mnt_sb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">ovl_should_sync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ofs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">errseq&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">errseq_sample&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">upper_sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_wb_err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">errseq_check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">upper_sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_wb_err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ofs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">errseq&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">err&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">EIO&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">pr_err&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Cannot mount volatile when upperdir has an unseen error. Sync upperdir fs to clear state.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">out_err&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&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="n">err&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">ovl_get_workdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ofs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">upperpath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">out_err&lt;/span>&lt;span class="p">;&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">workdir&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_flags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">SB_RDONLY&lt;/span>&lt;span class="p">;&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="n">sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_stack_depth&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">upper_sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_stack_depth&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_time_gran&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">upper_sb&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">s_time_gran&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Saw something called &lt;code>lowerdir&lt;/code> and &lt;code>upperdir&lt;/code>? Here&amp;rsquo;s what they means in &lt;code>overlayfs&lt;/code>:&lt;/p>
&lt;blockquote>
&lt;p>An overlay filesystem combines two filesystems - an &amp;lsquo;upper&amp;rsquo; filesystem and a &amp;rsquo;lower&amp;rsquo; filesystem. When a name exists in both filesystems, the object in the &amp;lsquo;upper&amp;rsquo; filesystem is visible while the object in the &amp;rsquo;lower&amp;rsquo; filesystem is either hidden or, in the case of directories, merged with the &amp;lsquo;upper&amp;rsquo; object.&lt;/p>
&lt;p>It would be more correct to refer to an upper and lower &amp;lsquo;directory tree&amp;rsquo; rather than &amp;lsquo;filesystem&amp;rsquo; as it is quite possible for both directory trees to be in the same filesystem and there is no requirement that the root of a filesystem be given for either upper or lower.&lt;/p>
&lt;p>The lower filesystem can be any filesystem supported by Linux and does not need to be writable. The lower filesystem can even be another overlayfs. The upper filesystem will normally be writable and if it is it must support the creation of trusted.* extended attributes, and must provide valid d_type in readdir responses, so NFS is not suitable.&lt;/p>
&lt;p>A read-only overlay of two read-only filesystems may use any filesystem type.&lt;/p>
&lt;p>&lt;em>Excerpt from &lt;a class="link" href="https://docs.kernel.org/filesystems/overlayfs.html#upper-and-lower" target="_blank" rel="noopener"
>Overlay Filesystem — The Linux Kernel documentation&lt;/a>&lt;/em>&lt;/p>&lt;/blockquote>
&lt;p>And &lt;code>ovl_get_workdir&lt;/code> will get where the &lt;code>workdir&lt;/code> is and it call &lt;code>ovl_make_workdir&lt;/code> to make the &lt;code>workdir&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: fs/overlayfs/super.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/torvalds/linux/blob/3bc1bc0b59d04e997db25b84babf459ca1cd80b7/fs/overlayfs/super.c#L1513
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="nf">ovl_get_workdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">super_block&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">ovl_fs&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">struct&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">upperpath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Omitted till L1513
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">err&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">ovl_make_workdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ofs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">workpath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="zfs-is-identified-as-a-remote-fs">ZFS is identified as a remote fs
&lt;/h4>&lt;p>Now the good bit comes, notice the comments. This where the &lt;code>overlayfs&lt;/code> rejects remote fs. (We will see why ZFS is identified as remote fs 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;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-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: fs/overlayfs/super.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/torvalds/linux/blob/3bc1bc0b59d04e997db25b84babf459ca1cd80b7/fs/overlayfs/super.c#L1434-L1444
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="nf">ovl_make_workdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">super_block&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">ovl_fs&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">struct&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">workpath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Omitted till L1433
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * We allowed sub-optimal upper fs configuration and don&amp;#39;t want to break
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * users over kernel upgrade, but we never allowed remote upper fs, so
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * we can enforce strict requirements for remote upper fs.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">ovl_dentry_remote&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ofs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">workdir&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">d_type&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="o">!&lt;/span>&lt;span class="n">rename_whiteout&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">ofs&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">noxattr&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">pr_err&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;upper fs missing required features.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">err&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">EINVAL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">out&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In &lt;code>ovl_dentry_remote&lt;/code>, it directly marks &lt;code>dentry&lt;/code> which has &lt;code>DCACHE_OP_REVALIDATE&lt;/code> flags (Remember what we said before? ZFS sets this flag.) as remote, and thus the code above will go into the if-condition, then rejecting 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;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-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File: fs/overlayfs/super.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Link: https://github.com/torvalds/linux/blob/3bc1bc0b59d04e997db25b84babf459ca1cd80b7/fs/overlayfs/util.c#L97-L101
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">ovl_dentry_remote&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">dentry&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">dentry&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">d_flags&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">DCACHE_OP_REVALIDATE&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">DCACHE_OP_WEAK_REVALIDATE&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Now everything comes together. Ah, this is why having &lt;code>d_revalidate&lt;/code> set to not &lt;code>NULL&lt;/code> will lead to Linux treating ZFS as a &lt;em>remote&lt;/em> filesystem (like NFS) and &lt;strong>thus things like &lt;code>overlayfs&lt;/code> won&amp;rsquo;t work with ZFS&lt;/strong>.&lt;/p>
&lt;p>There is PRs in OpenZFS to fix this problem: &lt;a class="link" href="https://github.com/openzfs/zfs/pull/9600" target="_blank" rel="noopener"
>https://github.com/openzfs/zfs/pull/9600&lt;/a> , &lt;a class="link" href="https://github.com/openzfs/zfs/pull/9414" target="_blank" rel="noopener"
>https://github.com/openzfs/zfs/pull/9414&lt;/a> . But currently they are held and I don&amp;rsquo;t the expertise or time to work on it either. Hopefully wish I can pick up that PR and fix it (if possible).&lt;/p>
&lt;h2 id="final-solution">Final solution
&lt;/h2>&lt;p>So, the only option left is not possible now. Is there something we can do?&lt;/p>
&lt;p>As I said earlier, &amp;ldquo;Simply put, that&amp;rsquo;s not possible (&lt;em>&lt;strong>directly&lt;/strong>&lt;/em>)&amp;rdquo;. Well, it turns out, there is still an &lt;em>indirect&lt;/em> way &amp;ndash; &lt;strong>ZFS Volumes&lt;/strong>. Let&amp;rsquo;s see what Oracle says:&lt;/p>
&lt;blockquote>
&lt;p>A ZFS volume is a dataset that represents &lt;strong>a block device&lt;/strong>.&lt;/p>
&lt;p>&lt;em>Excerpt from: &lt;a class="link" href="https://docs.oracle.com/cd/E19253-01/819-5461/gaypf/index.html" target="_blank" rel="noopener"
>https://docs.oracle.com/cd/E19253-01/819-5461/gaypf/index.html&lt;/a>&lt;/em>&lt;/p>&lt;/blockquote>
&lt;p>Note that it is a &lt;em>block device&lt;/em>. This is really important, which means we can treat it like a conventional hard drive and do whatever we want on ZFS datasets!&lt;/p>
&lt;p>Since it is a block device, we can use it as a Swap device, iSCSI target, and in this case, a block device holding a &lt;code>ext4&lt;/code> partitation to put &lt;code>overlayfs&lt;/code> on.&lt;/p>
&lt;h2 id="solve-problem">Solve problem
&lt;/h2>&lt;p>Finally! We now decide to use ZFS Volumes (zvol) to hold our &lt;code>overlayfs&lt;/code> , i.e., &lt;code>overlayfs&lt;/code> on top of &lt;code>ext4&lt;/code> on top of &lt;code>zvol&lt;/code> on top of ZFS datasets. (Well, it is a bit complex. But trust me, even with so many fs layers, the performance is still wayyyy higher than Docker&amp;rsquo;s &lt;code>zfs&lt;/code> driver.)&lt;/p>
&lt;p>Let&amp;rsquo;s fix this now.&lt;/p>
&lt;p>&lt;strong>Stop Docker:&lt;/strong>&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-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo systemctl stop docker
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Destroy the dataset that Docker uses previously.&lt;/strong> You can use &lt;code>zfs list&lt;/code> to find all datasets. In our case, it is &lt;code>rpool/ROOT/ubuntu_uzcb39/var/lib/docker&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-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># I mount this dataset at /var/lib/docker, which docker uses. Remove it.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo zfs destroy rpool/ROOT/ubuntu_uzcb39/var/lib/docker -R -r
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Be careful! This will destroy datasets recursively.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Create a ZFS Volume.&lt;/strong>&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-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo zfs create -sV 64G rpool/ROOT/ubuntu_uzcb39/var/lib/docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># rpool/ROOT/ubuntu_uzcb39/var/lib/docker is where the dataset is. Note that this will not be mounted to /var/lib/docker, which is different from the one above.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -V creates a zvol&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -s makes it sparse, i.e, dynamically expands instead of taking all defined space&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># set a max size of 64G&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Format the zvol&lt;/strong>. ZFS Volumes are identified as devices in the &lt;code>/dev/zvol/{dsk,rdsk}/pool&lt;/code> directory. Since we created a block device, let&amp;rsquo;s format it to &lt;code>ext4&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The zvol we just created is mounted at /dev/zvol/rpool/ROOT/ubuntu_uzcb39/var/lib/docker.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo mkfs.ext4 /dev/zvol/rpool/ROOT/ubuntu_uzcb39/var/lib/docker
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Mount the &lt;code>ext4&lt;/code> partitation to &lt;code>/var/lib/docker&lt;/code>.&lt;/strong>&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-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo mkdir -p /var/lib/docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Mount ext4-formatted zvol (block device) to /var/lib/docker&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo mount /dev/zvol/rpool/ROOT/ubuntu_uzcb39/var/lib/docker /var/lib/docker
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Check if it is successfully mounted.&lt;/strong>&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-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">df -hT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /dev/zd0 ext4 63G 5.8G 54G 10% /var/lib/docker&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># It is mounted, great!&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Start Docker back up and check status.&lt;/strong>&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-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo systemctl start docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># We are using overlay2 now!&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;gt; Storage Driver: overlay2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;gt; Backing Filesystem: extfs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;gt; Supports d_type: true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;gt; Native Overlay Diff: true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;gt; userxattr: false&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Make changes persistent.&lt;/strong> Make sure the zvol is automatically mounted to &lt;code>/var/lib/docker&lt;/code> after system reboots.&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-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo vim /etc/fstab
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Append this:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /dev/zvol/rpool/ROOT/ubuntu_uzcb39/var/lib/docker /var/lib/docker ext4 defaults 0 0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Horrey! Build time are several orders of magnitude faster now!&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a class="link" href="https://wiki.debian.org/ZFS" target="_blank" rel="noopener"
>ZFS - Debian Wiki&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a class="link" href="https://docs.docker.com/storage/storagedriver/#storage-drivers-versus-docker-volumes" target="_blank" rel="noopener"
>About storage drivers | Docker Documentation&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a class="link" href="https://docs.docker.com/storage/storagedriver/select-storage-driver/#suitability-for-your-workload" target="_blank" rel="noopener"
>Docker storage drivers | Docker Documentation&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>&lt;a class="link" href="https://docs.docker.com/storage/storagedriver/zfs-driver/#image-layering-and-sharing" target="_blank" rel="noopener"
>Use the ZFS storage driver | Docker Documentation&lt;/a>&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Archives</title><link>https://charlie0129.github.io/blog/archives/</link><pubDate>Sun, 06 Mar 2022 00:00:00 +0000</pubDate><guid>https://charlie0129.github.io/blog/archives/</guid><description/></item><item><title/><link>https://charlie0129.github.io/blog/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://charlie0129.github.io/blog/</guid><description>&lt;!-- ---
title: Links
links:
- title: GitHub
description: GitHub is the world's largest software development platform.
website: https://github.com
image: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
menu:
main:
weight: 4
params:
icon: link
comments: false
---
To use this feature, add `links` section to frontmatter.
This page's frontmatter:
```yaml
links:
- title: GitHub
description: GitHub is the world's largest software development platform.
website: https://github.com
image: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
- title: TypeScript
description: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
website: https://www.typescriptlang.org
image: ts-logo-128.jpg
```
`image` field accepts both local and external images. --></description></item><item><title>Search</title><link>https://charlie0129.github.io/blog/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://charlie0129.github.io/blog/search/</guid><description/></item></channel></rss>