Where pilot-daemon runs.
Pick the right mode.
One daemon, two transports. UDP gives direct peer paths and the lowest latency. Compat tunnels everything through a single outbound TCP/443, through an HTTPS proxy if that is the only way out. With nothing configured, pilotctl daemon start uses auto: UDP when the beacon answers over UDP, otherwise compat.
UDP for direct paths.
Compat for everything else.
HTTPS_PROXY since v1.13.11. Same overlay; same Ed25519 trust; higher latency than a direct path (traffic relays through the beacon).Where it runs.
What flag to set.
-transport=compatcompat+proxy — compat through HTTPS_PROXY (v1.13.11+)wrong runtime — no daemonThese per-platform notes are our best-effort understanding of each vendor's network policy, not continuously tested guarantees — vendors change their rules, so verify against your platform's current docs and tell us if a cell is wrong.
Container hosting / PaaS
Deploy as a Dockerfile or buildpack. UDP availability varies — when in doubt, ship in compat mode.
UDP blocked. Single TCP/443 works as of v1.10.3.
Default-allow egress on both protocols.
UDP needs dedicated IPv4; even then app-level egress is flaky.
App-UDP undocumented; daemon re-registers fine across the ~24h dyno cycle.
Safer default — can't open SSH/SMTP ports.
UDP + TCP both first-class.
Self-hosted; inherits host firewall — open UDP/4000 manually.
Serverless / functions
Three out of four can't host a persistent process. Cloud Run is the exception when min-instances=1.
15-min hard timeout kills the tunnel; no persistent daemon. Run the daemon as a Lambda Extension only while warm.
Set min-instances=1 to keep warm. Inbound is HTTPS-only.
Ephemeral execution — no persistent process model.
No long-lived sockets, no UDP, no listen().
VMs / IaaS
Full control. UDP works. Use -public to advertise a public node.
Open UDP/4000 in security group.
Same model — managed VMs with full firewall control.
No restrictions beyond your own network policy.
Kubernetes
DaemonSet or sidecar. Default CNI allows UDP; corporate clusters with NetworkPolicy default-deny → compat.
Default CNI allows UDP egress; no special config needed.
Same model — sidecar pattern over /shared/pilot.sock.
Allow egress to TCP/443 only; compat mode covers the rest.
Coding-agent sandboxes
A mix. Modal/E2B/Daytona/Codespaces are open; Docker-AI-Sandbox-class (Replit Agent, Devin) blocks raw TCP and lets traffic out through an HTTP proxy, which compat mode uses since v1.13.11.
Add *.pilotprotocol.network + 34.71.57.205 to the egress allowlist.
Docker AI Sandbox — raw TCP/UDP blocked. Compat mode goes through HTTPS_PROXY (v1.13.11+).
Same Docker AI Sandbox model; compat through HTTPS_PROXY.
Default-allow. Long timeout for persistent run.
IP/CIDR rules only; Pilot endpoints allowed by default.
Default-allow; iptables-based.
IPv4 outbound works; 30-min idle stop pauses the daemon.
Configure .gitpod.yml task.
Inherits underlying provider (EC2 / k8s / Docker).
Default-allow with configurable egress rules.
UI generators / WebContainers — no POSIX sockets.
Desktop / local
install.sh wires up the right supervisor for each OS.
~/Library/LaunchAgents/network.pilotprotocol.pilot-daemon.plist written; load it with launchctl.
sudo install writes the pilot-daemon + updater units; enable/start them with systemctl.
Ad-hoc dev only — no auto-restart.
No Windows binary yet — use WSL2 (Linux mode).
Browser / WASM
The daemon needs a POSIX runtime; the SDKs need a local daemon over a Unix socket, so neither runs in a browser tab or WASM.
No raw sockets, and the JS SDK is Node-only (Unix-socket client of a local daemon) — neither works here.
Same model — daemon needs POSIX.
Egress models.
What works today.
How single-port-443 works.
Before v1.10.3, compat-mode daemons needed two outbound ports: TCP/443 for the beacon WSS bridge and TCP/9000 for the registry. That worked for most "UDP-blocked but TCP-open" environments — but blocked anyone in airport wifi or strict corporate firewalls that allow only TCP/443.
v1.10.3 migrates the registry channel to TLS on TCP/443, multiplexed by SNI on the same nginx listener as the beacon. nginx pre-reads the TLS ClientHello's SNI field and routes registry.pilotprotocol.network traffic to a TLS terminator that proxies plain bytes to the existing registry; beacon.pilotprotocol.network traffic goes to the WSS-aware vhost. A compat daemon's lsof now shows exactly one outbound port: 443.
End-to-end tunnel protections are unchanged. TLS protects the compat hop between daemon and rendezvous, Ed25519 authenticates peer identity, and X25519-derived AES-256-GCM protects tunnel payloads, as in UDP mode.
Six deployments.
Render free-tier worker — UDP blocked
# Dockerfile
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://pilotprotocol.network/install.sh | \
PILOT_EMAIL=you@example.com PILOT_ALLOW_ROOT=1 sh
ENTRYPOINT ["/root/.pilot/bin/pilot-daemon", "-transport=compat"]No port exposed. Render allows arbitrary outbound TCP/443. Zero UDP, zero TCP/9000.
AWS Lambda — don't
Lambda has a 15-minute hard timeout and freezes the execution environment between invocations. A Lambda Extension can host a sidecar process but only while the function is warm. Wrong runtime for pilot-daemon. The JS and Python SDKs are not a way around this — both are clients of a local pilot-daemon over a Unix socket, so they still need the daemon running. A Lambda Extension can host the daemon while the function is warm; there is no daemon-less SDK path.
Kubernetes sidecar — egress-only pod
apiVersion: v1
kind: Pod
metadata: { name: app-with-pilot }
spec:
containers:
- name: app
image: your/app:latest
- name: pilot
image: debian:bookworm-slim
command: ["/bin/sh", "-c"]
args:
- curl -fsSL https://pilotprotocol.network/install.sh | \
PILOT_EMAIL=pilot@example.com PILOT_ALLOW_ROOT=1 sh &&
exec /root/.pilot/bin/pilot-daemon -transport=compat \
-socket /shared/pilot.sock
volumeMounts: [{ name: pilot-sock, mountPath: /shared }]
volumes: [{ name: pilot-sock, emptyDir: {} }]Works under any NetworkPolicy that permits egress to TCP/443. App talks to daemon over /shared/pilot.sock.
macOS laptop — LaunchAgent
curl -fsSL https://pilotprotocol.network/install.sh | \ PILOT_EMAIL=you@example.com sh
Installer writes ~/Library/LaunchAgents/network.pilotprotocol.pilot-daemon.plist and prints the launchctl load command to start it. The transport defaults to auto: UDP where it works, compat over TCP/443 where it doesn't.
Forced-compat systemd unit — locked-down corp Linux
curl -fsSL https://pilotprotocol.network/install.sh | sh -s -- --transport compat
# already installed: the units read the transport from ~/.pilot/config.json
pilotctl config --set transport=compat
sudo systemctl restart pilot-daemonThe installer saves "transport": "compat" in ~/.pilot/config.json; the service units it writes take the transport from there.
Proxy-only agent sandbox — HTTPS_PROXY, UDP blocked
# HTTPS_PROXY=http://user:pass@proxy.internal:3128 is already set curl -fsSL https://pilotprotocol.network/install.sh | sh pilotctl daemon start # auto picks compat through the proxy # proxy credentials that rotate: a command that prints the current proxy URL pilotctl config --set proxy_cmd="bash -c 'printf %s \"\$https_proxy\"'"
Since v1.13.11 the daemon asks the proxy to CONNECT by hostname, so poisoned local DNS does not matter, and TLS stays end to end. With a proxy command it re-reads the credentials every 60 seconds and after a 407. On Linux without systemd, pilotctl daemon start sets one up by itself when HTTPS_PROXY carries credentials. See running a node through an HTTPS-only egress proxy.
One daemon. Two transport modes.
On a supported runtime, install Pilot and start the daemon: auto picks UDP or compat, through HTTPS_PROXY when there is one. Set transport=compat to skip the UDP probe. Check the platform notes above before deployment.