Troubleshooting
curl: (6) Could not resolve host: <name>.portzero.local
The overlay isn’t active. .portzero.local names are only visible once the overlay
is running, which requires the daemon to be started with root / elevated privileges.
Start it with sudo:
sudo -E portzero start --foreground
If you started the daemon unprivileged it logged continuing in cloud/local-only mode
and the overlay is inactive. See Platform privileges.
My process was never discovered
PZ_TUNNEL must be set before the process starts — the daemon reads the frozen
environment captured at process start; a value set after launch (in os.environ /
process.env / os.Setenv) is invisible to it.
- Verify before launching:
echo $PZ_TUNNEL. - Use direnv, or export the variable in the same shell that execs the process.
- Confirm the value is a full domain with a recognized suffix (
.portzero.localor*.<username>.tunnel.portzero.cloud); nothing is appended implicitly. See Tunnels & PZ_TUNNEL.
macOS: a process run under a system interpreter is never discovered
On recent macOS the kernel hides the environment of SIP-protected system binaries
from every other process — including the daemon, and including root. So if you tag a
process whose executable is a system-shipped interpreter, its PZ_TUNNEL is invisible
and the process is silently not discovered. Affected executables include:
/usr/bin/python3(the Xcode/Command-Line-Tools python)- system
/usr/bin/perl,/usr/bin/ruby /bin/sh,/bin/bash(the system copies)
Run the tagged process under a non-system runtime instead — anything not under
/usr/bin or /bin//sbin is fine: Homebrew, nvm, pyenv, rbenv, asdf, or a
user-compiled binary. Quick check: PZ_TUNNEL is readable iff
ps -p <pid> -wwwE -o command= | tr ' ' '\n' | grep PZ_TUNNEL prints it. If that shows
nothing, the process is under a SIP binary — switch runtimes.
Linux and Windows have no such restriction, so system interpreters are discovered there normally.
The literal {branch} appears in status
Template resolution failed. For native processes, ensure you’re on a real git branch
(not detached HEAD) within the repo. For containers, the daemon resolves
{branch}/{worktree} from bind mounts / compose labels — if discovery of those fails,
the placeholder is left literal. See Tunnels & PZ_TUNNEL.
Wrong path taken (cloud vs. local)
The suffix decides the target, not any flag:
.portzero.local→ Local overlay*.<username>.tunnel.portzero.cloud→ Cloud tunnel
Double-check the suffix in your PZ_TUNNEL value.
Virtual interface creation fails even with sudo (Linux)
Ensure /dev/net/tun exists and the tun module is loaded (modprobe tun). Inside
containers, the device must be made available (--device /dev/net/tun --cap-add NET_ADMIN).
DNS resolves but the connection hangs / refuses
The name resolved to a virtual IP but the proxy couldn’t reach the backend. Check that
the backend process or Docker container is still listening on its ephemeral port and
that portzero status shows the expected real address. Restarting it re-registers the
same stable virtual IP.
.portzero.local names don’t resolve in a cloud sandbox or container
The overlay (virtual interface + embedded DNS server) can be up and healthy while
*.portzero.local still doesn’t resolve, if the OS-level scoped resolver never got
wired up. On Linux that wiring needs either systemd-resolved or a dnsmasq fallback;
container/cloud sandboxes — including remote coding-agent sessions — frequently have
neither. This step logs a warning and does nothing further; it’s best-effort and
non-fatal by design, and doesn’t affect the overlay itself.
Fix: add the dashboard/tunnel hosts entries yourself (portzero setup suggests the
exact line; portzero doctor re-checks it), or skip name resolution and use portzero url / portzero env / portzero wait to get the concrete tunnel URL for scripts —
see CI and scripts, which uses the same pattern
because CI runners have the identical no-systemd shape.
Before editing /etc/hosts yourself, know that a plain permission check isn’t enough
to tell whether an edit will actually work: a Linux immutable file attribute rejects
writes with a bare “permission denied” that looks like a normal permissions problem;
some distros symlink /etc/hosts to a generated, declaratively-rebuilt path where a
direct edit is pointless; and inside an actual container, /etc/hosts is typically its
own bind mount that the runtime overwrites on every restart, so an edit “succeeds” but
doesn’t survive one. portzero setup/portzero doctor check for all three before
writing or recommending a write, rather than assuming from indirect signals.
portzero login hangs in a remote/headless session
Older versions of portzero login’s default flow bound a local TCP listener and
waited for a browser page’s JavaScript to post credentials back to it. In a
remote/headless session there’s no local browser to open, and opening the printed URL
from your own laptop’s browser didn’t help either — the dashboard page ran in your
laptop’s browser, whose 127.0.0.1 was your laptop’s loopback, not the sandbox’s.
This is fixed: the printed URL now carries a session code instead of relying on a local
callback port. The CLI polls the API in the background and picks up the completed
login automatically, so nothing needs to be typed back into the sandbox at all. The URL
can be opened on any device. portzero login --interactive (email one-time code, no
browser or network callback of any kind) also works, if you’d rather not open a browser
at all. See FAQ for the full
remote-session setup.
Brave shows ERR_CERT_AUTHORITY_INVALID for a *.portzero.local name
If this only happens in the Snap package of Brave, use the native Brave package instead. Port Zero installs its local CA into the Linux system trust store and browser NSS stores, but some Snap Brave/Chromium builds still ignore those local trust anchors and report the Port Zero CA as an unknown issuer. Non-Snap Brave uses the installed CA correctly. See Known limitations for the full writeup (and other browser/engine CA-trust gaps).
Seeing the daemon’s own logs
~/.portzero/daemon/daemon.log(background mode).- Or run
--foregroundto see logs on the console. portzero statusshows the currently discovered Local tunnels and routes.