Mesh VPN with Nebula [network/nebula]

I found Nebula at first. It works well, requiring no central server or public SSL certificate.

Conversely admins need to sign and distribute their own certificates with nebula-cert, and they are used to authorize and encrypt connections.

The minimal requirement to run a Nebula network is a machine with a public, static IP and a port available (a cheapest VPS would work). This machine will serve as a “lighthouse” which other nodes use to find each other. You can have multiple lighthouses and the network is available with at least one of them online, which is handy during migration.

Nebula will try its best at NAT traversal, but if that fails it try to relay connections through nodes configured to work as relays. I simply use lighthouses as relays because they are accessible from every node.1 A simple (NixOS-based, check official docs for regular ones) lighthouse configuration looks like this:

services.nebula.networks."<name>" = {
  ca = "/etc/nebula/ca.crt";        # it would be better to use agenix or sops-nix
  cert = "/etc/nebula/host.crt";    # but I'm too lazy to touch a working config
  key = "/etc/nebula/host.key";
  isLighthouse = true;
  isRelay = true;
  listen.port = 4242;
};

networking.firewall = {
  allowedTCPPorts = [ 4242 ];
  allowedUDPPorts = [ 4242 ];
};

And a client configuration is a bit more complicated than that, because it needs to know where to find a lighthouse, and to configure its firewall2 to accept connections.

services.nebula.networks."<name>" = {
  ca = "/etc/nebula/ca.crt";
  cert = "/etc/nebula/host.crt";
  key = "/etc/nebula/host.key";
  staticHostMap = {
    # nebula ip is the one you assigned to the machine while signing its cert.
    # you can check it with `nebula-cert print -path host.crt`
    "<lighthouse-nebula-ip>" = [ "<lighthouse-public-address>:<port>" ];
  };
  lighthouses = [ "<lighthouse-nebula-ip>" ];
  relays = [ "<lighthouse-nebula-ip>" ];
  # I only have trusted devices in the network,
  # if that's not true use a more strict config
  firewall = let any = [{
    host = "any";
    port = "any";
    proto = "any";
  }]; in {
    inbound = any;
    outbound = any;
  };
};

networking.firewall = {
  allowedTCPPorts = [ 4242 ];
  allowedUDPPorts = [ 4242 ];
};

The mobile app works similarly, just set the same information up with the GUI. Beware that the mobile client doesn’t allow any inbound connections and the firewall is not configurable as of now (2025-07).

1

I think it’s also possible to have port-forwarded relays inside LAN to provide public-facing access to all the nodes inside local network, without depending on NAT traversal. Disclaimer: I haven’t tried this in practice.

2

Note that the “lighthouse” is also a regular node in the network, and its firewall can be configured in the same way to allow inbound access with nebula.