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.
The design that looked right
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.
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.
The tell was a field full of zeros
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.
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 /proc/net/bonding/bond0 by hand.
The fix was to stop being clever
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.
The gotcha on the way out
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 ifreload 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 ifdown bond0 && ifup bond0, so the device is recreated fresh and the mode is set before any slave attaches. That cost me half an hour.
What I took from it
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.

