Nebula is a scalable overlay networking tool by Slack that creates a private mesh VPN between your devices — no central server, no complex IPsec configuration. This guide walks through generating a certificate authority, setting up a Lighthouse node on Linux, and connecting Mac and iPhone clients. By the end, you'll have an encrypted 192.168.100.0/24 network your devices can reach each other on, wherever they are.

Generating a Certificate Authority (CA)

./nebula-cert ca -name "my-nebula-mesh" -ips "192.168.100.0/24"

Signing Device Certificates

./nebula-cert sign -name "lighthouse" -ip "192.168.100.1/24"
./nebula-cert sign -name "mac" -ip "192.168.100.2/24"

Configuring the Lighthouse Server (Linux)

The Lighthouse is the rendezvous point that helps Nebula nodes find each other. It doesn't route traffic — nodes connect directly peer-to-peer after the initial handshake.

Downloading the Lighthouse Binary

Go download the Lighthouse binary from here

In my case, I configured Lighthouse on a Linux-based server, so the filename is nebula-linux-amd64.tar.gz.

Unpack the Lighthouse binary:

tar -xzf nebula-linux-amd64.tar.gz

You should place the binary in a suitable location, such as /usr/local/bin/nebula, but for simplicity, I placed it in the root directory.

Creating the Systemd Service

Create a systemd service file so Nebula starts on boot and restarts automatically if it crashes:

# /etc/systemd/system/nebula.service

[Unit]
Description=Nebula Mesh VPN
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/root/nebula -config /etc/nebula/config.yaml

# Restart automatically if it exits
Restart=always
RestartSec=5

# Security (optional but recommended)
User=root
Group=root

# Increase file descriptor limit if needed
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Now re-read the systemd service file: sudo systemctl daemon-reload

Lighthouse Configuration

sudo mkdir -p /etc/nebula

Copy ca.crt, lighthouse.crt, and lighthouse.key to /etc/nebula/.

Here's the Lighthouse configuration — it binds to all interfaces on the standard Nebula port and declares itself as a lighthouse. The firewall rules allow all traffic within the overlay network:

# /etc/nebula/config.yaml

pki:
  ca: /etc/nebula/ca.crt
  cert: /etc/nebula/lighthouse.crt
  key: /etc/nebula/lighthouse.key

static_host_map: {}

listen:
  host: 0.0.0.0
  port: 4242

lighthouse:
  am_lighthouse: true

punchy:
  punch: true
  respond: true

relay:
  am_relay: true

firewall:
  outbound:
    - port: any
      proto: any
      cidr: 192.168.100.0/24
  inbound:
    - port: any
      proto: any
      cidr: 192.168.100.0/24
tun:
  dev: nebula1
  drop_local_broadcast: true
  drop_multicast: true

Enabling and Starting the Service

Now enable and start the service, then verify it's running:

sudo systemctl enable --now nebula

Check the service status and logs:

sudo systemctl status nebula
sudo journalctl -u nebula -n 50 --no-pager

You should see the service running and no errors in the logs.

Configuring Nebula on macOS

Install Nebula via Homebrew, then create the config. The key difference from the Lighthouse config: am_lighthouse is false, we point hosts at the Lighthouse, and we enable punchy for NAT traversal:

# /opt/homebrew/etc/nebula/config.yaml

pki:
  ca: /opt/homebrew/etc/nebula/ca.crt
  cert: /opt/homebrew/etc/nebula/m1.crt
  key: /opt/homebrew/etc/nebula/m1.key

static_host_map:
  "192.168.100.1": ["<LIGHTHOUSE_PUBLIC_IP>:4242"]

lighthouse:
  am_lighthouse: false
  hosts:
    - "192.168.100.1"

listen:
  host: 0.0.0.0
  port: 0

relay:
  relays:
    - 192.168.100.1
  use_relays: true

punchy:
  punch: true
  respond: true

firewall:
  outbound:
    - port: any
      proto: any
      cidr: 192.168.100.0/24
  inbound:
    - port: any
      proto: any
      cidr: 192.168.100.0/24

tun:
  dev: utun9
  disabled: false
  drop_local_broadcast: true
  drop_multicast: true

Install Nebula and start it as a background service:

brew install nebula
sudo brew services start nebula

Check the logs to confirm the connection succeeded:

tail -100 /opt/homebrew/var/log/nebula.log

Verify the service is running:

sudo brew services list

Name          Status  User File
nebula        started root /Library/LaunchDaemons/homebrew.mxcl.nebula.plist

Test connectivity to the Lighthouse:

ping 192.168.100.1

Configuring Nebula on iPhone

Install the Mobile Nebula iOS client from the App Store, then prepare your config file.

# config.yaml for iPhone

pki:
  ca: ca.crt
  cert: iphone.crt
  key: iphone.key

static_host_map:
  "192.168.100.1": ["<LIGHTHOUSE_PUBLIC_IP:4242"]

lighthouse:
  am_lighthouse: false
  hosts:
    - "192.168.100.1"

# portable devices is set to listen on all interfaces and port 0 (random)
listen:
  host: 0.0.0.0
  port: 0

relay:
  relays:
    - 192.168.100.1
  use_relays: true

punchy:
  punch: true
  respond: true

firewall:
  outbound:
    - port: any
      proto: any
      cidr: 192.168.100.0/24
  inbound:
    - port: any
      proto: any
      cidr: 192.168.100.0/24

tun:
  disabled: false

Copy config.yaml together with ca.crt to a shared folder or cloud storage where your iPhone can reach it.

In the Nebula iOS app, tap +, choose "from file," and select your config.yaml. In my case, it couldn't parse the CA certificate automatically, so I had to manually point it to ca.crt from the same shared folder.

The app will then display a public key. Transfer this key back to the machine where you have nebula-cert and the CA files, then sign a certificate for the iPhone:

./nebula-cert sign -name "iphone" -ip "192.168.100.3/24" -in-pub device.pub

The device.pub file is the public key exported from the iPhone Nebula client.

Copy the resulting iphone.crt back to your iPhone (the private key stays on the device — no .key file is generated by this flow). Point the Nebula app to the certificate and it should accept it. The iPhone should now connect to the Nebula network.

Configuring Nebula on Synology NAS

Running Nebula directly on DSM can break during OS updates, so running it inside a Docker/Container Manager container is highly recommended.

Sign a certificate for your Synology NAS:

./nebula-cert sign -name "synology" -ip "192.168.100.4/24"

Configure your config.yaml for the Synology NAS (update the pki paths to /config/):

# config.yaml for the Synology NAS

pki:
  ca: /config/ca.crt
  cert: /config/synology.crt
  key: /config/synology.key

static_host_map:
  "192.168.100.1": ["<LIGHTHOUSE_PUBLIC_IP>:4242"]

lighthouse:
  am_lighthouse: false
  hosts:
    - "192.168.100.1"

# since Synology is a stationary device behind a NAT,
# listen on 0.0.0.0 and defined port to avoid possible port conflicts
# in local network
listen:
  host: 0.0.0.0
  port: 4244

relay:
  relays:
    - 192.168.100.1
  use_relays: true

punchy:
  punch: true
  respond: true
  delay: 1s

firewall:
  outbound:
    - port: any
      proto: any
      cidr: 192.168.104.0/24
  inbound:
    - port: any
      proto: any
      cidr: 192.168.104.0/24

tun:
  dev: nebula1
  disabled: false
  drop_local_broadcast: true
  drop_multicast: true
  • Open Container Manager on your Synology NAS.
  • Create a folder on your NAS file system (e.g., /volume1/docker/nebula/) and drop ca.crt, synology.crt, synology.key, and your customized config.yml inside it. Paths in the pki section of your config.yml should start with /config/.
  • Download the official nebulaoss/nebula image from the registry.
  • When configuring the container settings, you must apply these options:
    • Network: Set Network Mode to Host.
    • Volumes / File Mapping: Map your NAS folder /volume1/docker/nebula/ to /config/ inside the container.
    • Capabilities / Execution: You must check the option to Run container with high privilege (Execute container using high privilege / --cap-add=NET_ADMIN). This gives Docker the permission to spin up the virtual tun network adapter on your DSM kernel.
    • Execution Command: Set the launch command invocation argument to: -config /config/config.yml
  • Start the container. Your Synology NAS is now joined to the mesh network.

Certificates rotation

By default, certificates are valid for one year. You can find more details on rotating certificates in the Nebula documentation.

You can add -duration 8760h to the nebula-cert sign command to set the certificate duration to 8760 hours (365 days) explicitly; or you can set it to 87600h to set the duration to 10 years.


You now have a working Nebula mesh: Lighthouse on Linux, clients on Mac and iPhone, all reachable at 192.168.100.x. To add more devices, sign additional certificates with nebula-cert sign. The overlay network grows without touching the Lighthouse config.