Platform privileges
The Local .portzero.local overlay manipulates kernel networking state, which requires
elevated privileges. This page explains exactly what needs them and what happens when
they’re missing.
What requires privileges
The overlay performs four privileged operations when it starts:
- Create the virtual network interface. Opening the OS’s TUN device and bringing
up an interface needs root or the Linux
CAP_NET_ADMINcapability. - Install the route for the private overlay address range into that interface.
Modifying the routing table needs root /
CAP_NET_ADMIN. This step is best-effort and non-fatal — a missing route is logged, not aborted. - Bind the embedded DNS server to the overlay gateway on a privileged port.
Binding a port below 1024 needs root or
CAP_NET_BIND_SERVICE. Without it the DNS server exits with “Permission denied” and no*.portzero.localname resolves, even though the rest of the overlay is up. - Install the scoped resolver so
*.portzero.localqueries go to the embedded DNS server. Writing OS resolver configuration needs root.
Per platform
| Platform | Requirement |
|---|---|
| Linux | root, or the daemon binary granted both CAP_NET_ADMIN and CAP_NET_BIND_SERVICE (e.g. sudo setcap 'cap_net_admin,cap_net_bind_service+eip' $(which portzero)). |
| macOS | root, or the com.apple.developer.networking.networkextension entitlement. |
| Windows | wintun.dll present next to the binary / in PATH; admin for adapter setup. |
Linux
The simplest path for local development is to start the daemon with sudo:
sudo -E portzero start --foreground
-E preserves your environment.
Autostart (running the daemon privileged at boot)
To have the overlay come up automatically, the daemon must autostart with privileges — otherwise it would relaunch unprivileged and silently degrade to cloud/local-only mode (see below). Each platform uses its native mechanism for a privileged background service:
| Platform | Autostart mechanism |
|---|---|
| macOS | A root-level system daemon, so the virtual network interface, resolver config, and routes all succeed. |
| Linux | A user-level systemd unit; the binary itself carries the required capabilities, so the user-level unit is sufficient. |
| Windows | A scheduled task at logon (admin for adapter setup). |
Manage autostart with:
portzero autostart enable # install the system unit
portzero autostart disable # remove it
portzero autostart status # show whether it's installed
On macOS, enable/disable must be run with sudo; the command fails fast with that
hint when run without root.
macOS: why a system-level daemon
macOS has no equivalent of Linux’s setcap, so the binary can’t be granted networking
capabilities that way. A user-level agent runs as the logged-in user and is
unprivileged, so the overlay would never come up — the autostart installer therefore
writes a system-level unit, which the OS runs as root. Installing or removing this
system unit is a one-time privileged step and must be run with sudo; if you run
autostart install or uninstall without root, it fails fast with a message telling you
to re-run under sudo.
Graceful degradation without privileges
The daemon does not require root to run. Without sufficient privileges it degrades to cloud/local-only mode: it logs a line such as
continuing in cloud/local-only mode
and keeps running. Concretely:
- Virtual interface creation fails → the overlay doesn’t carry traffic.
- Scoped resolver install fails → a warning is logged but startup continues; the embedded DNS server still runs, just isn’t wired into the OS resolver.
- Route install fails → logged, non-fatal.
Cloud tunnels (<name>--<username>.tunnel.portzero.cloud) and process/container
discovery still work without root; only the Local overlay data path needs it.
Consequence: .portzero.local visibility
Because the overlay (virtual interface + scoped resolver) is what makes
.portzero.local names resolvable and routable, Local tunnels are only visible once
the overlay is running — i.e. when the daemon was started with root. If you run the
daemon unprivileged, curl http://hello.portzero.local/ will not resolve even though
the tunnel was discovered. See Troubleshooting.