Skip to content

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.

Build the release binary:

Terminal window
cargo build --release --locked --bin lattice

Generate validated configuration and a stable identity for each service, then install them as per-user services:

Terminal window
packaging/install/lattice-self-hosted.sh install --user \
--host edge.example.net \
--bin target/release/lattice

The 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:

Terminal window
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>

Generate configs without installing services:

Terminal window
lattice infrastructure init --host edge.example.net --print-firewall
lattice rendezvous init --host edge.example.net --print-firewall

Each 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:

Terminal window
lattice infrastructure run --config lattice-infrastructure.toml
lattice rendezvous run --config lattice-rendezvous.toml

The same services run under Docker, Compose, and rootless Podman quadlets:

Terminal window
export LATTICE_RELAY_HOST=edge.example.net
export LATTICE_RENDEZVOUS_HOST=edge.example.net
docker compose -f packaging/containers/compose.yaml up -d --build

See Lattice Edge Containers for volumes, health checks, and the Podman quadlet path.

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 type is 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:

Terminal window
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> (also peer/peer_id, required), tcp=<port>, quic=<port>.
  • Rendezvous is dialed over QUIC (quic); relay over TCP (tcp). --port overrides, 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.

  • Readiness: curl --fail http://127.0.0.1:9474/readyz (relay) and http://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=json for 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 --user with --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/.