<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kanu Kingsley</title>
    <description>The latest articles on DEV Community by Kanu Kingsley (@captkay).</description>
    <link>https://dev.to/captkay</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1483498%2Fe5e9745c-345d-40cb-956d-4043b6c43944.jpeg</url>
      <title>DEV Community: Kanu Kingsley</title>
      <link>https://dev.to/captkay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/captkay"/>
    <language>en</language>
    <item>
      <title>The 333 GB I didn't know I was keeping</title>
      <dc:creator>Kanu Kingsley</dc:creator>
      <pubDate>Sun, 26 Jul 2026 19:04:50 +0000</pubDate>
      <link>https://dev.to/captkay/the-333-gb-i-didnt-know-i-was-keeping-39oi</link>
      <guid>https://dev.to/captkay/the-333-gb-i-didnt-know-i-was-keeping-39oi</guid>
      <description>&lt;p&gt;My main storage array is 839 GB of btrfs sitting on a mdadm RAID5. Last month it was reading half full and I couldn't tell you what half. That bothered me more than a full disk would have, because a full disk at least tells you where the space went.&lt;/p&gt;

&lt;p&gt;So I went looking. Twenty minutes later I had my answer: 333 GB of backups from 2024 that I had not touched since, plus a scatter of empty share definitions pointing at folders that no longer existed. I deleted the lot in about seven minutes. The interesting part is not the number. It's that the space had been invisible, and that OpenMediaVault makes the cleanup safe only if you do it the way OMV wants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inventory before you touch anything
&lt;/h2&gt;

&lt;p&gt;The rule I hold to on storage is boring on purpose: look before you delete, and write the looking down. Two commands did most of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nt"&gt;--max-depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 /srv/dev-disk-by-uuid-&lt;span class="k"&gt;*&lt;/span&gt;/ | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-40&lt;/span&gt;
find /srv &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first gives you the fat directories ranked by size, three levels deep, which is usually deep enough to name the culprit without drowning in output. The second finds the empty ones, which on an OMV box are often the tell-tale of a share whose data got moved or deleted while the share definition stayed behind.&lt;/p&gt;

&lt;p&gt;I put the output in a text file and went through it line by line, tagging each entry as one of three things. Active, meaning something reads or writes it and it stays. Stale, meaning it is real data nobody needs anymore, like those 2024 backups. Orphan stub, meaning OMV still has a shared-folder entry for it but the underlying directory is gone. The 333 GB was almost all stale. The orphan stubs were tiny on disk but they are the part that bites you later, so they went on the list too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I don't hand-edit OMV's config
&lt;/h2&gt;

&lt;p&gt;Here is the thing that catches people. OpenMediaVault keeps its entire configuration in one XML file at &lt;code&gt;/etc/openmediavault/config.xml&lt;/code&gt;, and it is right there, readable, editable. You can open it, find the share you want gone, delete the block, save. It will work until the next time OMV regenerates config from that file, which it does through its salt-based backend whenever you change almost anything in the web UI. Your hand edit either gets clobbered or, worse, leaves the running system and the config file disagreeing about what exists.&lt;/p&gt;

&lt;p&gt;The supported path is &lt;code&gt;omv-rpc&lt;/code&gt;, the same RPC interface the web UI drives. Anything the UI can do, &lt;code&gt;omv-rpc&lt;/code&gt; can do from the shell, and it goes through the same validation and the same salt apply. So the destruction gets scripted, and OMV stays the source of truth about its own state.&lt;/p&gt;

&lt;p&gt;Enumerate the shared folders first so you are working from UUIDs, not names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;omv-rpc &lt;span class="nt"&gt;-u&lt;/span&gt; admin &lt;span class="s1"&gt;'ShareMgmt'&lt;/span&gt; &lt;span class="s1"&gt;'enumerateSharedFolders'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.[] | [.uuid, .name, .reldirpath] | @tsv'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, per target I had already confirmed as stale or orphan, delete the share definition by its UUID and apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;omv-rpc &lt;span class="nt"&gt;-u&lt;/span&gt; admin &lt;span class="s1"&gt;'ShareMgmt'&lt;/span&gt; &lt;span class="s1"&gt;'delete'&lt;/span&gt; &lt;span class="s1"&gt;'{"uuid": "&amp;lt;the-uuid&amp;gt;"}'&lt;/span&gt;
omv-rpc &lt;span class="nt"&gt;-u&lt;/span&gt; admin &lt;span class="s1"&gt;'Config'&lt;/span&gt; &lt;span class="s1"&gt;'applyChanges'&lt;/span&gt; &lt;span class="s1"&gt;'{"modules": [], "force": false}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual bytes I removed with a plain &lt;code&gt;rm -rf&lt;/code&gt; on the confirmed-stale directories, one at a time, after the share definitions were gone. No script looped over a wildcard. Every deletion named one specific path I had already tagged by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sentinel UUID gotcha
&lt;/h2&gt;

&lt;p&gt;OMV uses one special UUID, &lt;code&gt;fa4b1c66-ef79-11e5-87a0-0002b3a176b4&lt;/code&gt;, as its "not set" reference. When a service, an NFS export, or an SMB share points at a shared folder, it stores that folder's UUID. Delete the folder definition out from under a still-referencing export and OMV does not always stop you, and the reference can fall back to that sentinel. You end up with an NFS export or an SMB share that resolves to nothing, and the symptom is a client mount that hangs or 404s rather than an error at delete time.&lt;/p&gt;

&lt;p&gt;The order that avoids it: remove the referencing service first (the NFS export, the SMB share), then the shared-folder definition, then the data. I got this backwards on one entry, saw an export left pointing at the sentinel, and had to walk it back.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually cost
&lt;/h2&gt;

&lt;p&gt;Thirty minutes of inventory. Seven minutes of deletion. The imbalance is the whole lesson. The dangerous work was fast and the safe work was slow, which is the correct ratio and the opposite of how it feels in the moment when you can see 333 GB you want back.&lt;/p&gt;

&lt;p&gt;I have put a reminder in the calendar to do this once a year. Not a cron job that deletes things, I am not automating &lt;code&gt;rm -rf&lt;/code&gt; against my own data on a schedule. Just the inventory half, the &lt;code&gt;du&lt;/code&gt; and the &lt;code&gt;find&lt;/code&gt;, so once a year I have to look at what the array is actually holding and decide, on purpose, what stays.&lt;/p&gt;

&lt;p&gt;Most homelab storage fills with ghosts. Old backups, half-finished migrations, shares for services you retired. The disk never complains until it is full, and by then the archaeology is harder. Cheaper to look every year while you still remember what the folders were for.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.iamkay.eu/blog/omv-cleanup?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=post-05-omv-cleanup" rel="noopener noreferrer"&gt;iamkay.eu&lt;/a&gt;, where I write about running production infrastructure: the failures, the fixes, and the tradeoffs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>storage</category>
      <category>homelab</category>
      <category>linux</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>The bond that negotiated with nobody</title>
      <dc:creator>Kanu Kingsley</dc:creator>
      <pubDate>Sun, 26 Jul 2026 19:03:30 +0000</pubDate>
      <link>https://dev.to/captkay/the-bond-that-negotiated-with-nobody-2mci</link>
      <guid>https://dev.to/captkay/the-bond-that-negotiated-with-nobody-2mci</guid>
      <description>&lt;p&gt;I planned a 2-gigabit bond for my Proxmox host. For weeks it quietly ran at 1, and nothing in the logs told me why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design that looked right
&lt;/h2&gt;

&lt;p&gt;The host has four network ports. My plan was textbook: pair them into two LACP bonds, then put an active-backup bond on top of both for redundancy. If a single link died, its bond would drop it. If a whole bond died, the top layer would fail over to the other. On a whiteboard it was clean.&lt;/p&gt;

&lt;p&gt;The problem was the switch. Mine is a small smart-managed TP-Link, and "smart-managed" sounds like it speaks LACP. It doesn't. It only does static link aggregation, the kind where you hard-assign ports to a group and trust that both ends agree. LACP is the dynamic version where the two ends negotiate the bundle between themselves, and this switch has no idea how to answer that conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tell was a field full of zeros
&lt;/h2&gt;

&lt;p&gt;Because the switch never answered, every LACP negotiation packet my host sent went nowhere, with no error and no log line to show for it. The failure surfaced in exactly one place: the bond's own status, where the partner MAC address read 00:00:00:00:00:00. All zeros is the network's way of saying nobody on the other end responded.&lt;/p&gt;

&lt;p&gt;The effect was that only one of the two links in each bond ever came up. I had built for 2 gigabits of aggregate bandwidth and was quietly running at 1, and I would not have caught it without reading &lt;code&gt;/proc/net/bonding/bond0&lt;/code&gt; by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix was to stop being clever
&lt;/h2&gt;

&lt;p&gt;I tore the nested design down to a single active-backup bond over two ports. One active link with one standby, and immediate failback to the primary when it recovers. The config got shorter, and the failover I actually wanted still works. The 2-gigabit number was never reachable with this switch, so collapsing the design lost me nothing real.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gotcha on the way out
&lt;/h2&gt;

&lt;p&gt;The change itself took one more fight. Linux will not switch a bond's mode while it still has slave interfaces attached. Try it and the kernel refuses the write with "Directory not empty," and &lt;code&gt;ifreload&lt;/code&gt; swallows that refusal rather than surfacing it, so the config reads as applied when it isn't. The bond keeps its old mode and you lose time wondering why. The fix is to tear the bond all the way down and bring it back up with &lt;code&gt;ifdown bond0 &amp;amp;&amp;amp; ifup bond0&lt;/code&gt;, so the device is recreated fresh and the mode is set before any slave attaches. That cost me half an hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took from it
&lt;/h2&gt;

&lt;p&gt;Match the topology to what your switch can actually do. LACP everywhere is a cargo-cult default that assumes 802.3ad-capable gear on both ends of every link. If yours only does static aggregation, a plain active-backup bond gives you reliable failover without chasing a bandwidth number you were never going to reach. And when a change looks applied but the behavior refuses to move, check whether the tool quietly ate an error for you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.iamkay.eu/blog/active-backup-bond?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=post-04-active-backup-bond" rel="noopener noreferrer"&gt;iamkay.eu&lt;/a&gt;, where I write about running production infrastructure: the failures, the fixes, and the tradeoffs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>homelab</category>
      <category>linux</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>The recovery path I never had</title>
      <dc:creator>Kanu Kingsley</dc:creator>
      <pubDate>Sun, 26 Jul 2026 19:02:31 +0000</pubDate>
      <link>https://dev.to/captkay/the-recovery-path-i-never-had-798</link>
      <guid>https://dev.to/captkay/the-recovery-path-i-never-had-798</guid>
      <description>&lt;p&gt;Last week I wrote about bringing an HP DL380 G7 back from fifteen months of cold storage. What I did not say was how close I came to bricking my own recovery.&lt;/p&gt;

&lt;p&gt;The homelab had a bootstrap network I had set up in 2024 called 10.99.110.x. Nothing lived on it except the host's secondary IP and the NAS guest's second interface. When I set up VLAN 100 tagged as the new SERVERS network, I retired 10.99.110.x. Between the cutover and the moment pfSense started routing VLAN 100 correctly, my only management path to the box was that dying network. If pfSense had glitched mid-cutover, I would have been at the chassis with a keyboard.&lt;/p&gt;

&lt;p&gt;That gap is what showed me what VLAN 200 MGMT is actually for. It also eventually pulled VLAN 300 DMZ out of me. Three VLANs today, each with a specific job.&lt;/p&gt;

&lt;h2&gt;
  
  
  SERVERS is where the work happens
&lt;/h2&gt;

&lt;p&gt;VLAN 100 is 10.99.99.0/24. Every LXC, every VM, the PVE host's management IP. Pi-hole, Traefik VRRP VIP at .29, Nextcloud, GitLab, Authelia, Vaultwarden, the observability stack. It is the network that all my other posts talk about.&lt;/p&gt;

&lt;p&gt;If SERVERS is broken, I need a way in that does not depend on SERVERS.&lt;/p&gt;

&lt;h2&gt;
  
  
  MGMT is where the recovery lives
&lt;/h2&gt;

&lt;p&gt;VLAN 200 is 192.0.2.0/24. Very few things live on it. The host has a secondary IP at 192.0.2.5. iLO 3 lives at 192.0.2.3. My laptop drops on one of the MGMT-tagged ports on the switch and gets a MGMT address via DHCP.&lt;/p&gt;

&lt;p&gt;Nothing on this VLAN has a default gateway. The whole point is that it does not route out. If SERVERS goes down, or if pfSense itself is broken, I can still direct-L2 to 192.0.2.5, log in over SSH, and see what happened.&lt;/p&gt;

&lt;p&gt;iLO 3 also deserves its own segment for a different reason. Its firmware last shipped in December 2020. It only speaks TLS 1.0, only accepts DSA-1024 SSH keys, and answers IPMI/DCMI on UDP/623. That stack has no business being reachable from the wider LAN. On MGMT, only my laptop and the host itself can talk to it, and the pfSense rules between SERVERS and MGMT make sure nothing on SERVERS can escalate through it.&lt;/p&gt;

&lt;h2&gt;
  
  
  DMZ is where the internet gets in
&lt;/h2&gt;

&lt;p&gt;VLAN 300 is 203.0.113.0/24. One IP is in use. 203.0.113.10 is my cloudflared LXC.&lt;/p&gt;

&lt;p&gt;Cloudflared holds the outbound TLS session to Cloudflare's edge. Requests to cloud.iamkay.eu, ha.iamkay.eu, and a handful of other public routes flow inbound through that tunnel, hit cloudflared, and cloudflared forwards them to the Traefik VRRP VIP at 10.99.99.29 on port 443. The pfSense rule that allows this flow is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src=203.0.113.10 -&amp;gt; dst=10.99.99.29 tcp/443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing else can cross from DMZ to SERVERS. Cloudflared cannot SSH into anything, cannot hit Vaultwarden directly, cannot touch Nextcloud's data volume. It has exactly one job.&lt;/p&gt;

&lt;p&gt;Enterprise DMZ patterns describe a /24 subnet full of load balancers, WAFs, and reverse proxies, all serving multiple internet-facing apps. That is the right model at that scale. At homelab scale, my entire DMZ is one process and one address. The rule set can be minimal because the surface is minimal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rules that tie it together
&lt;/h2&gt;

&lt;p&gt;pfSense is the only device that can route between VLANs. The rules I care about most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SERVERS to MGMT is blocked with no exceptions. I verified this six different ways before trusting it.&lt;/li&gt;
&lt;li&gt;DMZ to SERVERS allows exactly &lt;code&gt;203.0.113.10 -&amp;gt; 10.99.99.29&lt;/code&gt; on tcp/443. Nothing else.&lt;/li&gt;
&lt;li&gt;MGMT to anywhere is allowed, because MGMT is my hands. If someone is on MGMT they either have physical access to the switch or my laptop.&lt;/li&gt;
&lt;li&gt;SERVERS to the internet is allowed for outbound traffic: updates, DNS forwarding, mail relay, ACME challenges.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The switch itself is a TP-Link TL-SG108E v1. Static VLAN tagging, not 802.3ad. That kept me from doing LACP with pfSense, which is a whole other story I will get to. The host runs a single active-backup bond over two 1GbE NICs. VLAN tags terminate on vmbr0.100, vmbr0.200, and vmbr0.300 sub-interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tradeoffs I know I have
&lt;/h2&gt;

&lt;p&gt;Family devices are on the untagged VLAN 1 today. That is not a decision, it is an omission. When I move them to their own VLAN, the pfSense rules need to say family devices can reach cloud.iamkay.eu and nothing else on the homelab. That work is queued and not urgent. Right now the family is one household and I trust the trust boundary.&lt;/p&gt;

&lt;p&gt;I do not have a full L3 switch. The TL-SG108E does static VLAN tagging fine, but everything routes through pfSense. If pfSense is a single point, MGMT is the fallback. If both are down at once, I am at the chassis.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it comes back to
&lt;/h2&gt;

&lt;p&gt;Each of the three VLANs is named for the thing it does. SERVERS holds the workloads. MGMT is how I reach them when SERVERS is down. DMZ is one process talking to the internet. The structure was not something I designed up front. It came out of asking, at every layer, what happens if this network dies.&lt;/p&gt;

&lt;p&gt;The cold-storage recovery would have gone sideways if the box between my laptop and pfSense had died during the VLAN cutover. It did not, but that near-miss is what taught me the shape of MGMT. MGMT refuses to route anywhere. DMZ routes exactly one thing. Both segments exist to answer what happens when the primary path breaks.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.iamkay.eu/blog/vlan-segmentation?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=post-03-vlan-segmentation" rel="noopener noreferrer"&gt;iamkay.eu&lt;/a&gt;, where I write about running production infrastructure: the failures, the fixes, and the tradeoffs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>homelab</category>
      <category>pfsense</category>
      <category>security</category>
    </item>
    <item>
      <title>I never ran ESXi in production</title>
      <dc:creator>Kanu Kingsley</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:46:06 +0000</pubDate>
      <link>https://dev.to/captkay/i-never-ran-esxi-in-production-22e4</link>
      <guid>https://dev.to/captkay/i-never-ran-esxi-in-production-22e4</guid>
      <description>&lt;p&gt;Most "why Proxmox" content in 2025-2026 is a migration story driven by Broadcom's ESXi pricing changes. The author had a working VMware stack and got priced out. I'm not that author. I evaluated both, picked Proxmox in 2024, and built on it without ever running ESXi in production. Two years in, I'd make the same call.&lt;/p&gt;

&lt;p&gt;It reads as either incompetent or contrarian until the rest of the post lands. Here's the reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three reasons it was the easy call
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. LXC and KVM in one host
&lt;/h3&gt;

&lt;p&gt;Most workloads in this homelab are LXCs. Pi-hole, Vaultwarden, Authelia, Traefik, the monitoring stack, GitLab CE itself, all containers sharing the host kernel. A few things need full VM isolation (the NAS guest, Proxmox Backup Server, the Home Assistant OS appliance). Same hypervisor, same CLI, same web UI for both shapes of workload.&lt;/p&gt;

&lt;p&gt;The alternative is ESXi for the VMs and a separate toolchain (containerd, Docker, Kubernetes, take your pick) for the containers. That's two backup pipelines, two HA stories, two places for config drift to surprise you at 2 AM.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pct exec 254 systemctl status authelia&lt;/code&gt; and &lt;code&gt;qm start 189&lt;/code&gt; are the same shape. New hires don't have to learn one tool for containers and a different one for VMs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Proxmox Backup Server beats the free Veeam alternative
&lt;/h3&gt;

&lt;p&gt;Chunk-level deduplication. Backups across guests and across time share storage. A nightly backup of all 11 LXCs and 2 VMs runs in about ten minutes and adds a few hundred MB of new chunks, because most of the content is the same as yesterday.&lt;/p&gt;

&lt;p&gt;Cluster-scheduled. One job definition runs across every node in the cluster. No per-node cron, no manual rotation when a node moves.&lt;/p&gt;

&lt;p&gt;Restore to a different storage class. A backup taken from local-lvm on the G7 restores onto ZFS on a G5 cluster node without conversion gymnastics.&lt;/p&gt;

&lt;p&gt;Veeam Community Edition is the free comparison. It works. It also caps repository size, doesn't dedup at the chunk level, and lacks the cluster-aware scheduling that makes PBS feel like a built-in feature rather than a bolt-on.&lt;/p&gt;

&lt;p&gt;When backups are the load-bearing wall of a homelab, the part that lets me say yes to family data later, the free option that compromises least is the one that wins.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cluster and HA without vCenter
&lt;/h3&gt;

&lt;p&gt;ESXi clustering requires vCenter Server. vCenter is its own VM, its own database, its own patching cadence, its own license. The minimum production footprint to get HA is three ESXi hosts plus one vCenter plus shared storage. The vCenter VM itself also needs to be backed up off the cluster it manages.&lt;/p&gt;

&lt;p&gt;Proxmox does the same job from the per-node web UI. &lt;code&gt;pvecm create&lt;/code&gt;, &lt;code&gt;pvecm add&lt;/code&gt; from each new node, edit &lt;code&gt;corosync.conf&lt;/code&gt; if the auto-detect picks the wrong network, done. HA failover policy is &lt;code&gt;ha-manager add ct:256 --group g5-only&lt;/code&gt;. No separate management plane.&lt;/p&gt;

&lt;p&gt;Three-node cluster running. A real workload migrated to a different node on the night I shut down one of the cluster members. Total observability downtime: about a minute and a half.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest tradeoffs
&lt;/h2&gt;

&lt;p&gt;I want this post to read as opinionated, not as marketing.&lt;/p&gt;

&lt;p&gt;Ecosystem. VMware has decades of third-party integrations across storage vendors, backup vendors, networking overlays, and security tooling. Proxmox has fewer. If you need a specific compliance-certified storage appliance or a vendor-specific tooling chain, VMware is sometimes the only choice.&lt;/p&gt;

&lt;p&gt;Audit logging. Proxmox logs the basics. Deep audit trails with role hierarchy, per-action attestation, and tamper-evident logs are an ESXi + Aria stack feature that doesn't have a Proxmox equivalent. For SOC 2, HIPAA, or FedRAMP work this matters. For a homelab and a small team it doesn't.&lt;/p&gt;

&lt;p&gt;Support. The Proxmox Enterprise subscription exists and is reasonable, but the support contract isn't the same as VMware's. For 24/7 white-glove production support, ESXi still wins. For the small-team case, the community forums and the wiki are usually faster anyway.&lt;/p&gt;

&lt;p&gt;The honest summary: at small scale Proxmox wins on tooling unification, on free-tier capability, and on the speed at which you can change your mind. At enterprise scale the tradeoffs reverse.&lt;/p&gt;

&lt;h2&gt;
  
  
  What kept me here
&lt;/h2&gt;

&lt;p&gt;I haven't reconsidered in two years because the daily friction is low. The web UI does what I want, the CLI is consistent across LXC and VM, and the backup story stays one tool. Migrating a service between nodes is two commands; when a node falls over the cluster handles it.&lt;/p&gt;

&lt;p&gt;The homelab keeps growing. Every additional service is a new LXC or VM in the same pane of glass. The platform hasn't forced a new toolchain on me to keep up.&lt;/p&gt;

&lt;p&gt;The right tool depends on the scale you actually operate at. For mine, it's Proxmox.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.iamkay.eu/blog/why-proxmox?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=post-02-why-proxmox" rel="noopener noreferrer"&gt;iamkay.eu&lt;/a&gt;, where I write about running production infrastructure: the failures, the fixes, and the tradeoffs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>proxmox</category>
      <category>homelab</category>
      <category>selfhosted</category>
      <category>virtualization</category>
    </item>
  </channel>
</rss>
