How to run a tunnel relay
Prerequisites, installation, payment channel setup, and operations for an ADNL tunnel node relay.
Prerequisites
Section titled “Prerequisites”- Go 1.24 or newer, available from the official Go downloads page
- Static public IP address
- UDP port 17330 open for tunnel traffic
- TCP port 18889 open temporarily for automatic IP detection
Install the relay binary
Section titled “Install the relay binary”Build from source:
git clone https://github.com/ton-blockchain/adnl-tunnelcd adnl-tunnelmake binaryThe binary is produced at build/tunnel-node. Precompiled binaries are available from the releases page.
Start the relay
Section titled “Start the relay”./tunnel-nodeOn first launch the relay:
- Generates
config.jsonwith four Ed25519 key pairs - Attempts to detect the external IP via a port checker (TCP 18889 to tonutils.com:9099)
- Publishes the node in the DHT overlay
- Prints the ADNL identity:
Tunnel started, listening on 0.0.0.0:17330 ADNL id is: <BASE64_ADNL_ID>Verify the configuration
Section titled “Verify the configuration”Open config.json and check:
ExternalIP: must contain the public IP. If empty, the port checker failed. Set it manually.TunnelListenAddr: defaults to0.0.0.0:17330. Change if a different bind address is needed.TunnelServerKey: the node identity key. Do not modify.
See the tunnel relay reference for the full list of configuration fields.
Enable payments
Section titled “Enable payments”-
Edit
config.json:{"PaymentsEnabled": true,"Payments": {"MinPricePerPacketRoute": 100,"MinPricePerPacketInOut": 200}} -
Restart the relay. In interactive mode (TTY), the relay prompts for the payment node key:
No active onchain payment channel found, please input payment node id (pub key) in base64 format, to deploy channel with:Enter the base64 public key of the payment node. In non-interactive mode (systemd, Docker), pass the key as a flag:
Terminal window ./tunnel-node --payment-node <HEX_PAYMENT_NODE_KEY> -
The relay deploys the payment channel contract on-chain (timeout 150 s), then waits for state exchange.
-
Verify the deployment with the
balanceinteractive command.
Generate a shared configuration
Section titled “Generate a shared configuration”To pin clients to this relay:
./tunnel-node -gen-shared-config <OUTPUT_PATH>This produces a JSON file containing the node public key and payment parameters. Distribute the file to clients, who reference it via NodesPoolConfigPath in their configuration.
Monitor the relay
Section titled “Monitor the relay”Prometheus metrics
Section titled “Prometheus metrics”Enable the metrics endpoint:
./tunnel-node -metrics-listen-addr 0.0.0.0:9100Metrics are exported under the tunnel_ namespace. The full metric list is in the tunnel relay reference.
Interactive commands
Section titled “Interactive commands”In TTY mode, the commands speed, stats, balance, capacity, wallet-ton-balance, and wallet-ton-transfer are available; each is documented in the interactive commands reference.
Run as a systemd service
Section titled “Run as a systemd service”Example unit file:
[Unit]Description=ADNL Tunnel RelayAfter=network-online.targetWants=network-online.target
[Service]Type=simpleUser=tunnelWorkingDirectory=/opt/tunnel-nodeExecStart=/opt/tunnel-node/tunnel-node -v 2 -metrics-listen-addr 127.0.0.1:9100Restart=on-failureRestartSec=5LimitNOFILE=65535
[Install]WantedBy=multi-user.targetTroubleshoot common issues
Section titled “Troubleshoot common issues”| Symptom | Cause | Fix |
|---|---|---|
ExternalIP is empty after startup | Port checker cannot reach TCP 18889 | Open TCP 18889 temporarily or set ExternalIP manually |
skipping dht because no external address known | ExternalIP not configured | Set ExternalIP in config.json |
tunnel looks disconnected | No control response for 45 seconds | Check network connectivity and peer availability |
more than 33% incoming packets lost | Excessive packet loss on the path | Investigate network quality or relay selection |
free packets exceeds rate limit | Unpaid traffic exceeds 10 packets per second | Enable payments or reduce traffic |
| Relay registers but clients cannot connect | Relay is behind NAT without port forwarding | Set ExternalIP manually in config.json AND forward UDP 17330 on the router |
Stop and update the relay
Section titled “Stop and update the relay”Send SIGTERM to stop the relay. The node commits virtual payment channels before exiting (timeout 15 s).
To update: stop the relay, replace the binary, and restart. Keys in config.json and state in payments-db/ persist across restarts.
Related pages
Section titled “Related pages”- Tunnel node: how garlic routing works
- Tunnel relay reference: all configuration fields, CLI flags, and metrics