Skip to content

NAT traversal & relay

This page sets up the server’s reachability. A public server relays for peers behind NATs out of the box. A server that is itself behind a NAT needs a few settings in the YAML file to reach the network through another relay.

Feature Key Default What it does
Relay transport features.enable_relay on Lets the host use Circuit Relay v2 at all. The three below require it.
Relay service features.enable_relay_service on Relays connections for other peers that reserve a slot on this server.
AutoRelay features.enable_auto_relay off Reserves slots on static relays so peers can reach this server through them.
Hole punching features.enable_hole_punching off DCUtR: upgrades a relayed connection to a direct one when both sides can.
AutoNAT features.enable_autonat on AutoNAT v2: asks peers to test which of this server’s addresses are reachable.
External addresses server.external_addresses none Addresses the server advertises in addition to its interface addresses.
Bootstrap peers server.bootstrap_peers none DHT bootstrap peers, and the relay candidates for AutoRelay.

The server refuses to start if enable_auto_relay, enable_hole_punching or enable_relay_service is on while enable_relay is off.

Your server Setup
Has a public IP, UDP port open Defaults, plus external_addresses if the interface address is not the public one
Behind a NAT you control, with the UDP port forwarded Defaults, plus external_addresses set to the public IP and forwarded port
Behind a NAT you cannot forward through AutoRelay and hole punching through another Ricochet server you run on a public address
Public, but should not carry traffic for others Turn the relay service off

Nothing to turn on: the relay service is on by default. Advertise the public address so clients can reconnect:

/etc/ricochet/config.yaml
server:
external_addresses:
- /ip4/203.0.113.10/udp/55223/udx

On the .deb install, set EXTERNAL_IP=203.0.113.10 in /etc/ricochet/env instead; run.sh passes it as --external-addrs.

Forward UDP 55223 (or your chosen port) from the router to the server, and set external_addresses to the router’s public IP and the forwarded port. From the network’s point of view this is a public server.

The server needs a relay with a public address: another Ricochet server you run, with the relay service on. List it in bootstrap_peers with its full multiaddr, including /p2p/<peer-id>:

/etc/ricochet/config.yaml
features:
enable_relay: true
enable_relay_service: false # see the note below
enable_auto_relay: true
enable_hole_punching: true
server:
bootstrap_peers:
- /ip4/198.51.100.7/udp/55223/udx/p2p/12D3KooW...

AutoRelay reserves a slot on the static relays it is given, which are your bootstrap peers, and the server then advertises a /p2p-circuit address through that relay. Hole punching lets peers that reach it through the relay try to upgrade to a direct connection. AutoRelay does nothing unless bootstrap_peers holds at least one valid address with a peer ID.

The relay must be reachable over UDX, since that is the only transport either side runs.

/etc/ricochet/config.yaml
features:
enable_relay_service: false

The server still uses the relay transport for its own connections; it just stops accepting reservations.

When the relay service is on, go-libp2p’s default limits apply: 128 reservations server-wide, 16 circuits per reservation, 1 reservation per peer, 8 per IP and 32 per ASN, reservations lasting one hour, and each relayed connection cut after 2 minutes or 128 KiB in each direction. These are deliberately small: relayed connections are meant for signalling and hole punching, not bulk traffic.

Override them only if this server carries relay traffic for others and you have measured what it costs:

/etc/ricochet/config.yaml
relay_limits:
max_reservations: 256
max_circuits: 16
buffer_size: 2048 # bytes
max_reservations_per_peer: 1
max_reservations_per_ip: 8
max_reservations_per_asn: 32
reservation_ttl_min: 60 # minutes
connection_duration_sec: 120 # seconds
connection_data: 131072 # bytes per direction

A zero or omitted value keeps the go-libp2p default. The units are in the key names. reservation_ttl and connection_duration without the suffix are not keys, and the server refuses to start on them.

At startup the server logs:

  • relay configuration, with enable_relay, enable_relay_service, enable_auto_relay and enable_hole_punching as it parsed them.
  • relay service created, with max_reservations and max_circuits, when the relay service is on.
  • registered protocols. With the relay service on, the list includes /libp2p/circuit/relay/0.2.0/hop.
  • advertising external addresses, with the number of external addresses accepted.
  • connected to bootstrap peer or failed to connect to bootstrap peer, once per bootstrap peer.
  • host advertised addresses, with the addresses advertised at that moment.
Terminal window
grep -E 'relay configuration|relay service created|advertis|bootstrap peer' /var/log/ricochet/stdout.log

An AutoRelay reservation is made after startup, so the host advertised addresses line usually predates it. For more detail, start the server with --debug-dht, which also turns the relay and relaysvc loggers to DEBUG. GOLOG_LOG_LEVEL sets the level for all libp2p subsystems.

The final check is from outside: a client on another network connecting with the address you published.

  • Clients connect once, then fail to reconnect: the server advertises only private addresses. Set external_addresses (or EXTERNAL_IP).
  • invalid config: enable_auto_relay requires enable_relay (or the same for hole punching or the relay service): turn enable_relay back on.
  • A NAT’d server with AutoRelay never becomes reachable: check that the relay service is off on it, that bootstrap_peers entries end in /p2p/<peer-id>, and that the log shows connected to bootstrap peer. The relay itself must have its relay service on.
  • parse config file: ... field reservation_ttl not found: use reservation_ttl_min and connection_duration_sec.
  • Relayed connections drop after two minutes: that is the default per-connection limit. Hole punching, where both sides support it, moves the peers to a direct connection.