Sang's Blog

Building infrastructure from bare metal

A rack of servers arrives. Four nodes, each with two CPUs, 128GB of RAM, four network ports, and four hard drives. The task is to turn them into a private cloud: virtual machines for databases, Kubernetes nodes, storage, monitoring, and development environments. There is no cloud provider to abstract the hardware, no API to provision resources, no managed service to handle the network. Everything must be built from scratch, one layer at a time, and each layer constrains the layers above it.

The most common mistake in building infrastructure from bare metal is treating the hypervisor as the center of the design. Proxmox is installed first because it is the visible platform — the web interface where VMs are created and managed. But the hypervisor is not the hard part. The hard part is the network, and the network must be designed before the hypervisor is installed, because Proxmox inherits its network configuration from the Linux kernel, and changing it after VMs are running requires either downtime or live migration.

The network design starts at the physical layer. Each server has four network ports. Two are bonded for management and VM traffic, and two are reserved for storage traffic. Bonding (also called NIC teaming or link aggregation) combines multiple physical ports into a single logical interface. The bond mode determines how traffic is distributed. LACP (802.3ad) provides active-active load balancing across both links, but it requires the switch to support LACP and be configured with the corresponding port channel. Active-backup provides failover without switch configuration — one link carries all traffic, the second takes over when the first fails. For a homelab or small production cluster, active-backup is simpler and more reliable than LACP, because a misconfigured LACP port channel causes complete connectivity loss that is harder to diagnose than a failed link.

The bonded interface becomes the bridge that all virtual machines connect through. Proxmox creates a Linux bridge (vmbr0) on top of the bond. The bridge behaves like a virtual switch: VMs plug into it, and traffic is forwarded to the physical network through the bond. The host itself gets an IP address on the bridge, not on the bond directly. This is an important distinction — the bond handles physical redundancy, the bridge handles virtual switching, and the host IP is just another device on the bridge. Any VM on the same bridge can communicate with the host and with other VMs without traversing the physical network.

VLANs segment the network into logical zones without requiring separate physical switches. Each VLAN is a separate broadcast domain. Traffic between VLANs must pass through a router, which is where access control is enforced. A typical design has three VLANs: a management VLAN for Proxmox hosts and hypervisor access (10.0.0.0/24), a server VLAN for VM traffic (10.0.10.0/24), and a storage VLAN for replication and backup traffic (10.0.20.0/24). The management VLAN is the most restricted — only administrators should reach it, and only through VPN. The server VLAN is where production workloads live. The storage VLAN is isolated from all other traffic because storage replication is bandwidth-sensitive and should not compete with VM traffic.

Proxmox supports VLAN-aware bridges. When a bridge is marked as VLAN-aware, each VM can be assigned a VLAN tag, and the bridge tags the traffic automatically. The host’s management IP stays on the native VLAN (untagged), while VMs are tagged with their respective VLAN IDs. This eliminates the need for separate bridge interfaces per VLAN and keeps the network configuration manageable as the cluster grows.

pfSense runs as a virtual machine on one of the Proxmox nodes. It has two virtual NICs: one connected to the upstream network (the WAN side, where the internet connection terminates) and one connected to the internal bridge (the LAN side, where all VLANs live). pfSense handles routing between VLANs, NAT for outbound traffic, firewall rules that control which traffic is allowed between zones, and VPN termination for remote access. Virtualizing pfSense instead of running it on dedicated hardware saves a physical server, but it introduces a dependency: if the Proxmox node hosting pfSense goes down, the entire network loses its gateway and firewall. The fix is a second pfSense instance in a high-availability pair with CARP (Common Address Redundancy Protocol), or a hardware fallback that provides basic connectivity when the VM is unavailable.

The firewall rules in pfSense follow a permit-by-exception model. By default, traffic between VLANs is blocked. Rules are added explicitly: the server VLAN can reach the internet through NAT, the management VLAN can reach the server VLAN on SSH and HTTPS ports, the storage VLAN cannot reach any other VLAN, and only the VPN subnet can reach the management VLAN. This model is simple to describe but requires discipline to maintain. Every time a new service is deployed that needs cross-VLAN access, a rule must be added and documented. Without documentation, the firewall rule set becomes a pile of exceptions that no one understands, and the security model degrades over time.

VPN access provides the secure channel through which administrators reach the management network. WireGuard is the simplest option for site-to-site and remote access VPN. It uses a single UDP port, is stateless (no handshake daemon, no TLS negotiation), and performs well on low-power hardware. The configuration consists of a private key and a list of allowed peers. Each remote administrator gets a unique client configuration that grants access only to the management subnet. WireGuard runs on the pfSense VM or on a separate lightweight VM. The design choice is whether to terminate VPN on pfSense (simpler, one point of management) or on a dedicated jump host (defense in depth — compromising the VPN does not give direct access to the firewall management interface).

Storage is the layer that determines how the cluster behaves under failure. Each server has local disks configured as a ZFS pool. ZFS provides checksumming, snapshots, compression, and built-in replication. The storage VLAN carries ZFS send/receive traffic for backup replication between nodes. Ceph, which Proxmox supports natively for hyperconverged storage, runs on a dedicated storage network to avoid saturating the VM traffic links. The decision between local ZFS and Ceph depends on the workload. Databases benefit from local ZFS with its low latency and snapshot-based backup. General-purpose VMs benefit from Ceph with its live migration and self-healing. Mixing both is common: local ZFS for databases and stateful services, Ceph for stateless workloads and Kubernetes nodes that need to move between hosts.

The physical server itself is the last layer to consider, not the first. CPU and RAM determine how many VMs can run on a node, but they are rarely the bottleneck in a correctly provisioned cluster. The bottleneck is almost always storage IOPS or network bandwidth. A server with 128GB of RAM can run twenty 4GB VMs. A server with four spinning disks can sustain roughly 400 random IOPS. The mismatch means that storage is exhausted long before compute is saturated, and the only fix is to add more spindles or migrate to SSDs. Every infrastructure built from bare metal encounters this imbalance, and the teams that plan for it from the start avoid the expensive mid-life crisis of retrofitting storage capacity.

Building infrastructure from bare metal is an exercise in layering. The physical ports are bonded into a redundant link. The bond becomes a bridge for the hypervisor. The bridge carries VLANs that segment traffic into security zones. pfSense routes between zones and enforces the firewall policy. WireGuard provides the encrypted tunnel through which administrators reach the control plane. Each layer depends on the layer below it, and a mistake at any layer propagates upward. A misconfigured VLAN tag locks VMs out of the network. A missing firewall rule blocks a backup job. A saturated storage link causes database replication to stall. The insight is that the hypervisor is not the infrastructure — it is just the first VM hoster. Everything that matters happens in the network between the host, the switch, and the firewall, and understanding those connections is what separates a stable cluster from one that breaks on every firmware update.

← Prev Post Next Post →