Self-Hosting the Edge Services
This quickstart stands up the two services that improve connectivity between already-paired devices:
- Relay and AutoNAT (
lattice infrastructure run) — Circuit Relay v2 fallback and reachability probing. - Peer rendezvous (
lattice rendezvous run) — epoch-scoped WAN discovery.
Both are optional. They never hold vault plaintext, membership keys, or canonical state, and they do not need TLS certificates: they speak Noise-authenticated libp2p over TCP and QUIC.
The host must have a reachable public DNS name or IP address and inbound TCP
and UDP on the chosen ports (defaults 4001 for the relay, 4201 for
rendezvous). The services do not change firewall rules.
Quick path
Section titled “Quick path”Build the release binary:
cargo build --release --locked --bin latticeGenerate validated configuration and a stable identity for each service, then install them as per-user services:
packaging/install/lattice-self-hosted.sh install --user \ --host edge.example.net \ --bin target/release/latticeThe script defaults to a per-user installation under $HOME and enables the
lattice-edge.target (relay + rendezvous). It prints the peer ID and the exact
[network] client entry for each service, and creates the systemd user units.
For a host-wide installation with dedicated service accounts, pass --system
and run it as root.
Add the printed entries to the daemon configuration and reload:
lattice network add relay /dns4/edge.example.net/tcp/4001/p2p/<relay-peer-id>lattice network add rendezvous /dns4/edge.example.net/udp/4201/quic-v1/p2p/<rendezvous-peer-id>Manual path
Section titled “Manual path”Generate configs without installing services:
lattice infrastructure init --host edge.example.net --print-firewalllattice rendezvous init --host edge.example.net --print-firewallEach command writes a validated config to the default service path, creates the identity under lock, and prints the client entry and firewall examples. Then install the systemd units and sample config as described in Self-Hosted Availability Profile, or run the processes in the foreground:
lattice infrastructure run --config lattice-infrastructure.tomllattice rendezvous run --config lattice-rendezvous.tomlContainers
Section titled “Containers”The same services run under Docker, Compose, and rootless Podman quadlets:
export LATTICE_RELAY_HOST=edge.example.netexport LATTICE_RENDEZVOUS_HOST=edge.example.netdocker compose -f packaging/containers/compose.yaml up -d --buildSee Lattice Edge Containers for volumes, health checks, and the Podman quadlet path.
Discovery
Section titled “Discovery”Clients can resolve the services from DNS instead of pasting pinned peer IDs. Discovery combines two record types:
-
TXT records carry the peer ID and, optionally, the ports. Each record describes one service with a
type=field. Publish several TXT records at the same name, one per service:_lattice.edge.example.net. TXT "v=1;type=relay;p2p=<relay-peer-id>;tcp=4001;quic=4001"_lattice.edge.example.net. TXT "v=1;type=rendezvous;p2p=<rendezvous-peer-id>;tcp=4201;quic=4201"Per-service names also work, in which case
typeis optional:_lattice-relay.edge.example.net. TXT "v=1;type=relay;p2p=<relay-peer-id>;tcp=4001;quic=4001"_lattice-rendezvous.edge.example.net. TXT "v=1;type=rendezvous;p2p=<rendezvous-peer-id>;tcp=4201;quic=4201" -
SRV records optionally override the target host and port:
_lattice-relay._tcp.edge.example.net. SRV 0 0 4001 edge.example.net._lattice-rendezvous._udp.edge.example.net. SRV 0 0 4201 edge.example.net.An SRV record cannot carry the peer ID, so a matching TXT record is still required. SRV is useful when the service host or port differs from the domain, or for priority and weight based failover.
Discover every published service in one command:
lattice network discover --domain edge.example.net \ --pin-relay <relay-peer-id> \ --pin-rendezvous <rendezvous-peer-id>When _lattice.<domain> has no typed records, discovery falls back to the
per-service names. To restrict discovery to one service, pass
--service rendezvous|relay (with --pin <peer-id> for a strict single pin).
Record format:
- TXT fields are
;-separated:v=1(version, ignored),type=<relay|rendezvous>(service, optional for per-service names),p2p=<peer-id>(alsopeer/peer_id, required),tcp=<port>,quic=<port>. - Rendezvous is dialed over QUIC (
quic); relay over TCP (tcp).--portoverrides, and an SRV record takes precedence over the TXT port.
Use --dry-run to print what would be configured without writing. Discovery
without a matching --pin-<service> trusts the DNS answer on first use; pinned
configuration remains the security baseline.
Verification and operations
Section titled “Verification and operations”- Readiness:
curl --fail http://127.0.0.1:9474/readyz(relay) andhttp://127.0.0.1:9477/readyz(rendezvous). Metrics are on the same loopback listeners under/metrics. - Status and peer IDs:
packaging/install/lattice-self-hosted.sh status --user. - Log format: set
LATTICE_LOG_FORMAT=jsonfor structured logs; the default is human-readable text on stderr. - Back up each service identity key under its state directory. Losing it changes the service peer ID and requires updating every client.
- Upgrade with
packaging/install/lattice-self-hosted.sh upgrade --user(replace--userwith--system), or restart the container.
The packaged systemd units apply a restrictive sandbox, including an empty
capability bounding set, MemoryDenyWriteExecute, and a @system-service
syscall allowlist. Adjust or remove individual settings only after checking
systemd-analyze security <unit> and the service lifecycle scripts in xtask/.