VPN NetworkManager & wg-quick both are broke with KDE UI

I’m running NixOS (KDE Plasma, NetworkManager enabled) with WireGuard configured via sops-nix for the private key.

I’m still a noob on Linux and NixOS, but I’m trying to build my own configuration.

I initially used NetworkManager’s WireGuard integration, but I ran into issues with the KDE UI: I could disconnect, but reconnect failed with a “secret” error.

Because of that, I switched to wg-quick (wg-quick / networking.wg-quick.interfaces). The situation got worse: after disconnecting, the VPN no longer appears correctly in the UI, and I have to restart it manually using sudo systemctl restart/start/stop wg-quick-<name>.

Is this expected with NetworkManager and or wg-quick, is there a known correct way on NixOS to make this work cleanly?

The KDE UI requires that your connection is made through NetworkManager, since it’s a NetworkManager front-end. If you want nice UI stuff, you’ll have to stick to NetworkManager.

NetworkManager erroring out on a wireguard connection, on the other hand, is not expected. Most likely that’s a mistake on your end. It’s debuggable, though.

You can see logs for NetworkManager with journalctl -eu NetworkManager. The message you get in the UI doesn’t seem very useful, so I’d suggest starting there.

I’m not 100% sure those logs will be sufficient, NetworkManager likes throwing logs into the journal with obscure topics. You might have to run journalctl -f during the connection attempt, or check with nmcli to get the connection’s topic string.

I retested using NetworkManager, and it works correctly. However, I initially ran into the same issue: the VPN connection did not appear properly in KDE Plasma’s network widget.

The solution was to use networking.networkmanager.ensureProfiles instead of defining the WireGuard interface directly through networking.wireguard.interfaces. Using the profile registered with NetworkManager, so it integrates correctly with Plasma’s UI.

Here is the configuration that worked for me:

{
  config,
  ...
}:

{
  sops = {
    secrets = {
      "wireguard/private_key" = {
        owner = "root";
        mode = "0400";
      };
    };

    templates."my-private-key-wg.env" = {
      owner = "root";
      mode = "0400";
      content = ''
        WG_PRIVATE_KEY=${config.sops.placeholder."wireguard/private_key"}
      '';
    };
  };

  networking = {
    networkmanager.ensureProfiles = {
      environmentFiles = [
        config.sops.templates."my-private-key-wg.env".path
      ];

      profiles.EXAMPLE_VPN = {
        connection = {
          id = "EXAMPLE_VPN";
          type = "wireguard";
          interface-name = "wg-example";
          autoconnect = "false";
        };

        wireguard = {
          private-key = "$WG_PRIVATE_KEY";
        };

        "wireguard-peer.<PUBLIC_KEY>" = {
          endpoint = "vpn.example.com:51820";
          persistent-keepalive = "25";
          allowed-ips =
            "12.0.0.0/24;172.16.0.0/16;";
        };

        ipv4 = {
          method = "manual";
          address1 = "12.0.0.2/32";
          dns = "12.0.0.1;";
          dns-search = "~.;";
          dns-priority = "-1";
        };

        ipv6.method = "ignore";
      };
    };
  };
}

Hope this helps anyone else for VPNs that use sops secrets and need good integration with DNS and UI.