Commit 2dfed1a747b8

Vincent Demeester <vincent@sbr.pm>
2026-02-02 09:49:05
fix(microvm): create bridge via systemd service
networking.bridges doesn't work reliably with all network setups. Use explicit systemd service to create the bridge interface. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 88ae628
Changed files (1)
modules
modules/microvm/default.nix
@@ -256,7 +256,7 @@ in
     # Enable microvm host
     microvm.host.enable = true;
 
-    # Create bridge for VM networking
+    # Keep declarative config for firewall/NAT
     networking.bridges.${cfg.bridge}.interfaces = [ ];
     networking.interfaces.${cfg.bridge} = {
       ipv4.addresses = [
@@ -316,8 +316,31 @@ in
       name: _vmCfg: "d ${cfg.stateDir}/${name} 0755 vincent users -"
     ) cfg.vms;
 
-    # Attach tap interfaces to bridge after they're created
-    systemd.services = mapAttrs' (
+    # Create bridge and attach tap interfaces
+    systemd.services = {
+      # Create bridge for VM networking
+      # (networking.bridges doesn't work reliably with all network setups)
+      "microvm-bridge-setup" = {
+        description = "Create MicroVM bridge";
+        wantedBy = [ "network.target" ];
+        before = [ "network.target" ];
+        after = [ "network-pre.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = pkgs.writeShellScript "create-microvm-bridge" ''
+            set -e
+            if ! ${pkgs.iproute2}/bin/ip link show ${cfg.bridge} &>/dev/null; then
+              ${pkgs.iproute2}/bin/ip link add name ${cfg.bridge} type bridge
+            fi
+            ${pkgs.iproute2}/bin/ip addr replace ${cfg.subnet}.1/24 dev ${cfg.bridge}
+            ${pkgs.iproute2}/bin/ip link set ${cfg.bridge} up
+          '';
+          ExecStop = "${pkgs.iproute2}/bin/ip link delete ${cfg.bridge}";
+        };
+      };
+    }
+    // mapAttrs' (
       name: _vmCfg:
       let
         tap = tapName name;
@@ -326,9 +349,12 @@ in
         description = "Attach MicroVM '${name}' tap to bridge";
         after = [
           "microvm-tap-interfaces@${name}.service"
-          "network-addresses-${cfg.bridge}.service"
+          "microvm-bridge-setup.service"
+        ];
+        requires = [
+          "microvm-tap-interfaces@${name}.service"
+          "microvm-bridge-setup.service"
         ];
-        requires = [ "microvm-tap-interfaces@${name}.service" ];
         before = [ "microvm@${name}.service" ];
         partOf = [ "microvm@${name}.service" ];
         serviceConfig = {