Connecting Virtual Layer 3 Networks

Intro This is the second article of a three part series which explains how to expose a self hosted service to the public Internet. I will discuss basic IP forwarding, routing on the host, and using nftables to implement firewall filters and Network Address Translation (NAT). Setup This post builds upon the first article describing how bridges are used to setup L2 networking. Scripts are provided to recreate the L2 environment on the host and to start the Alpine Linux guest. The guest VM startup script will need to be modified to match the downloaded Alpine image. These network commands will help with configuring the Alpine Linux guest VM for this article. ...

October 10, 2026 · 2736 words

Virtual L2 Networks From Scratch

Intro This is the first article in a three part series focused on self hosting publicly facing services. Virtualization stacks like libvirt, Proxmox, or Docker abstract network creation behind single CLI commands. The abstractions are convenient but hide the kernel primitives used to create virtualized networks. Knowing these primitives makes it easier to understand how virtual machines and containers connect to the host’s network. This post will manually construct an isolated Layer 2 network and connect user space processes using native iproute tooling. ...

September 18, 2026 · 2351 words

Self Hosting Publicly Facing Services

Motivation Online discussions around self hosting public services typically fall into two unhelpful groups. Alarmists will warn that exposing any service guarantees unwanted security intrusions and offer no path forward. The other group offers a recipe. It’s suggested that some Proxmox or Podman command will solve the issue, but what the command does isn’t explained. Both approaches leave the reader without a clear understanding of the network boundaries being created or why they provide protection. ...

September 17, 2026 · 552 words

Uh-oh. Is the router down?

This post is the README for my home network and something I can refer to later. It’s the story of how I replaced a underperforming consumer-grade router with a fully virtualized RouterOS instance using QEMU and PCI passthrough for the network. Like any software project, this project is source controlled and rebuildable from scratch. If the router dies, I simply deploy a new router instance. This is the reproducibility you’d expect in a datacenter, not a home office. ...

November 19, 2025 · 2794 words

Virtual Router Lab on macOS with QEMU

UTM and Multipass are great apps for virtualization on macOS. But I wanted a lighter-weight approach by invoking QEMU directly. Which meant I needed to understand how QEMU’s networking options interact with the vmnet virtualization API on macOS. This becomes especially important when dealing with VM-to-VM connections, network isolation, and bridging on macOS. In this post, I’ll walk through creating a simple QEMU-based networking lab. Set up RouterOS and Alpine Linux VMs using QEMU on macOS Connect VMs with Apple’s Hypervisor vmnet networking APIs Use unified logging to troubleshoot QEMU network issues on macOS Lab Setup Overview The network diagram shows the network topology used in this lab. Both VMs run on on the same macOS host and connected to virtual network interfaces using QEMU’s support for Apple’s vmnet virtualization API. ...

May 14, 2025 · 2097 words

Tips for working with qemu images

QEMU uses files to emulate storage devices, and the features available depend on how those files are created. While QEMU can emulate disks from Parallels and VirtualBox, I’m going to focus on the formats most commonly used in automation and scripting, raw and qcow2. The default format is raw and raw offers the fewest features. It’s just plain storage. The other format qcow2 supports compression, snapshots, and copy-on-write in addition to storage. ...

April 6, 2025 · 1358 words