Virt-manager (Error starting domain: Requested operation is not valid: network 'default' is not active) and what's the equivalent to (sudo virsh net-autostart --network default)

Hello fellow nix users :wave: .
I have just setup virt-manager in nixos, and everytime i reboot and open virt-manger and try to run any virtual machine i get this error:

Error starting domain: Requested operation is not valid: network 'default' is not active

I managed to solve this error by running: sudo virsh net-start default however this will work until you reboot the machine you will have to run it again, following this article, it explains that you have to run sudo virsh net-autostart --network default if you want to auto start that daemon automatically on startup.

The only reason i have not applied the second solution is because i am on nixos, and i am making the assumption that there will be a nixos config that does the same thing + if there is (that would be good since it will be declared in the config).

here is my nixos config:

  # virtualization
  programs.virt-manager.enable = true;
  users.groups.libvirtd.members = ["alice"];
  virtualisation.spiceUSBRedirection.enable = true;
  virtualisation.libvirtd.enable = true;
1 Like

To me sounds like according to my understanding of the article one needed to set virtualisation.libvirtd.allowedBridges to what is deemed ā€œdefaultā€ (ā€œvirbr0ā€ in the article again).

Some links around here discussing the bridge interface workings:

HTH

Got it working - Thanks to Claude!

  # Virt-manager
  programs.virt-manager.enable = true;
  virtualisation.libvirtd.enable = true;
  users.users.<myuser>.extraGroups = [ "libvirtd" ];
  virtualisation.spiceUSBRedirection.enable = true;
  networking.firewall.trustedInterfaces = ["virbr0"];
  systemd.services.libvirt-default-network = {
    description = "Start libvirt default network";
    after = ["libvirtd.service"];
    wantedBy = ["multi-user.target"];
    serviceConfig = {
      Type = "oneshot";
      RemainAfterExit = true;
      ExecStart = "${pkgs.libvirt}/bin/virsh net-start default";
      ExecStop = "${pkgs.libvirt}/bin/virsh net-destroy default";
      User = "root";
    };
  };

How it works

The systemd service automatically starts the libvirt default network on boot:

  • after = ["libvirtd.service"] ensures it only runs after libvirtd is ready
  • wantedBy = ["multi-user.target"] makes it start automatically at boot
  • Type = "oneshot" with RemainAfterExit = true means it runs once and systemd considers it active
  • ExecStart runs virsh net-start default when the service starts
  • ExecStop runs virsh net-destroy default on shutdown/stop for clean teardown

This is the NixOS equivalent of running sudo virsh net-autostart default on other distributions. The network will now automatically be available whenever you start virt-manager, eliminating the ā€œnetwork ā€˜default’ is not activeā€ error.

You can check the service status with:

sudo systemctl status libvirt-default-network
1 Like

wow, i was just trying to solve it yesterday. Thanks a lot.

just out of curiosity, since we declare the extraGroups elsewhereunder users in our config, currently i am forced to give that option in users line instead of declaring the libvirtd extragroups near the libvirt vonfig line. How to do this?
I want a method where it should work well like commenting the above whole virt-manager thing will revert my extragroups back to normal instead of going to users line and removing the libvirtd.

edit:
is this the neat way?
users.groups.libvirtd.members = ["alice"];

if so I generally wantto know other techniques.

Yes. As far as I understand, that is the neat way. Thanks for pointing it out, though. I’ll update the code in my post.(Edit: Ah… I can’t edit the post anymore)

Also, you can make the other approach (users.users.<myuser>.extraGroups = [ "libvirtd" ];) work by using nix modules. Tutorial here.

1 Like

Actually I also came to know about from from one of the above message in this thread. Thanks @pmnzt for pointing this option.

Hey, this almost worked. But I got a minor bug here, where I open virt-manager it stays on "Connecting to KVM/QEMU " something. And only when I close and reopen the app it works fine showing my guest OSs.
:thinking:

I’m okay with this now, but would love to have a solution

You must be missing:

  programs.dconf = {
    enable = true;
    profiles.user.databases = with lib.gvariant; [
      {
        lockAll = true; # prevents overriding
        settings = with lib.gvariant; {
          "org/virt-manager/virt-manager/connections" = {
            autoconnect = ["qemu:///system"];
            uris = ["qemu:///system"];
          };
        };
      }
    ];
  };

in your dconf settings.

Which channel are you using? I’m on nixpkgs-unstable and it looks like the connection setting is already included as part of the virt-manager, so I don’t need to redefine it in my config.

Here’s my full virt-manager module, if you’re curious:

  # Virt-manager
  programs.virt-manager.enable = true;
  users.groups.libvirtd.members = ["avisek"];
  virtualisation.libvirtd.enable = true;
  virtualisation.libvirtd.qemu.vhostUserPackages = with pkgs; [virtiofsd];
  virtualisation.spiceUSBRedirection.enable = true;
  networking.firewall.trustedInterfaces = ["virbr0"];
  systemd.services.libvirt-default-network = {
    description = "Start libvirt default network";
    after = ["libvirtd.service"];
    wantedBy = ["multi-user.target"];
    serviceConfig = {
      Type = "oneshot";
      RemainAfterExit = true;
      ExecStart = "${pkgs.libvirt}/bin/virsh net-start default";
      ExecStop = "${pkgs.libvirt}/bin/virsh net-destroy default";
      User = "root";
    };
  };
  programs.dconf = {
    enable = true;
    profiles.user.databases = with lib.gvariant; [
      {
        lockAll = true;
        settings = with lib.gvariant; {
          # Already included with virt-manager in nixpkgs-unstable
          "org/virt-manager/virt-manager/connections" = {
            autoconnect = ["qemu:///system"];
            uris = ["qemu:///system"];
          };

          "org/virt-manager/virt-manager" = {
            xmleditor-enabled = true;
          };
          "org/virt-manager/virt-manager/confirm" = {
            delete-storage = false;
          };
          "org/virt-manager/virt-manager/console" = {
            resize-guest = mkInt32 1;
            scaling = mkInt32 0;
          };
          "org/virt-manager/virt-manager/stats" = {
            enable-memory-poll = true;
            enable-disk-poll = true;
            enable-net-poll = true;
          };
          "org/virt-manager/virt-manager/vmlist-fields" = {
            host-cpu-usage = true;
            memory-usage = true;
            disk-usage = true;
            network-traffic = true;
          };
        };
      }
    ];
  };

I am using unstable package as like this:

what does this dconf actually do? it’s purpose?

Does that solve your ā€œConnecting to KVM/QEMUā€¦ā€ issue?

Google it. It’s like a database for storing settings.

1 Like

Only the first time I open the virt-manager app it shows that, I just close and open it again, it works fine.

@avisek what does this lib.gvariant do?
I couldn’t find a function in that exact name in noogle.dev all ends with some other name. So what does this do? Can I just use {} without wrapping it with the gvariant like you did?

gvariant is a set, thats why you can use it with with.

Though why it is in the snippet, I can’t say, as the given snippet doesn’t use any bindings at all in the scope introduced by the with.

Nether of the 2 do… (except for lib.gvariant which isn’t overwritten by any of the with either anyway, if the lib got introduced into scope idiomatically).

Before I start explaining, I need to do a small correction to my above code snippet:
There should only be one ā€œwith lib.gvariant;ā€ (Thanks to @NobbZ for pointing it out)


It’s like with pkgs; for environment.systemPackages:

 # with `with pkgs;`
 environment.systemPackages = with pkgs; [
   git
   neovim
 ]

 # without `with pkgs;`
 environment.systemPackages = [
   pkgs.git
   pkgs.neovim
 ]

In our case:

  # with `lib.gvariant;`
    profiles.user.databases = with lib.gvariant; [
      {
        settings = {
          # .. snip ..
          "org/virt-manager/virt-manager/console" = {
            resize-guest = mkInt32 1;
            scaling = mkInt32 0;
          };
          # .. snip ..
        }
      }
    ]

  # without `lib.gvariant;`
    profiles.user.databases = [
      {
        settings = {
          # .. snip ..
          "org/virt-manager/virt-manager/console" = {
            resize-guest = lib.gvariant.mkInt32 1;
            scaling = lib.gvariant.mkInt32 0;
          };
          # .. snip ..
        }
      }
    ]

lib.gvariant provides type conversion functions for GVariant, the serialization format used by GSettings/dconf. It ensures values are properly typed for the configuration system.

lib.gvariant.mkInt32 converts nix integer to dconf-specific 32-bit signed integer.


Can you omit gvariant conversions? No, not reliably. Plain Nix values may not serialize correctly to GVariant. For example:

 # This might not work correctly
 resize-guest = 1;  # Could be interpreted as different integer type

 # This ensures correct GVariant typing
 resize-guest = lib.gvariant.mkInt32 1;

However, some simple types like strings and booleans often work without explicit conversion, but integers frequently need mkInt32 to avoid type mismatches that can cause dconf to reject the settings.

Best practice: Keep using lib.gvariant functions to ensure your dconf settings are applied correctly.