Relay Setup
Complete guide to deploying the relay server on a fresh VPS (Ubuntu 22.04 / 24.04).
1. Initial VPS Setup
SSH into your fresh VPS and run the install script:
ssh root@YOUR_VPS_IP
# Short URL
curl -sSL get.shurli.io | sh
# Or use the full GitHub URL directly
curl -sSL https://raw.githubusercontent.com/shurlinet/shurli/dev/tools/install.sh | shChoose “Relay server” when prompted. The script handles everything: downloads the binary, verifies checksums, creates a secure service user, configures firewall, installs systemd service, and starts the relay.
For pre-release builds, set the environment variable before sh:
curl -sSL <URL> | SHURLI_DEV=1 shAlternative: clone repo and run setup script manually
apt update && apt upgrade -y
apt install -y git ufw
ufw allow OpenSSH
ufw default deny incoming
ufw default allow outgoing
ufw enable
git clone https://github.com/shurlinet/shurli.git
cd shurli
bash tools/relay-setup.shWhat the setup does
It will:
- Ask you to select an existing user or create a new one
- New user: creates the account, sets a password, copies your SSH keys, locks down home directory (700), and offers to harden SSH (disable password auth + root login)
- Existing user: audits sudo group, password, SSH keys, directory permissions, and SSH daemon config, and offers to fix any issues
- Continues with the full setup: builds binary, installs to
/usr/local/bin/shurli, creates data directory at/etc/shurli/relay/, installs systemd service, starts the relay in foreground for first-time identity setup (create new or recover from existing seed phrase), enables the service, configures firewall, tunes QUIC buffers
If creating a new user, test SSH access in a separate terminal before closing the root session:
ssh newuser@YOUR_VPS_IP
sudo whoami # should print: rootConfigure
Relay commands auto-detect the config at /etc/shurli/relay/ and auto-escalate to the service user when needed, so you can run them from any directory as any sudo-capable user:
# These work from any directory, as any user with sudo access
shurli relay info
shurli relay invite create --ttl 24h
# Edit config if needed (defaults are good - port 7777, gating enabled)
sudo nano /etc/shurli/relay/relay-server.yamlThen restart the service to pick up config changes (config-level changes like ports, transport, and ZKP enablement require a restart):
sudo systemctl restart shurli-relayNote: Auth changes (
shurli relay authorize/shurli relay deauthorize) apply immediately via live reload - no restart needed.
Add peers to the relay
Option A: Invite codes (recommended)
Generate invite codes and share them with your peers:
# Generate an invite code (valid for 24 hours)
shurli relay invite create --ttl 24h
# Each person joins with one command on their device:
shurli join <invite-code> --relay <IP:PORT>
# Peer ID: <shown in invite output>The join --relay command handles everything on a fresh device: creates identity (new or recover from seed phrase), sets password, writes config, connects to relay, starts daemon, and offers systemd service installation on Linux.
Invite codes handle authorization automatically. Everyone who joins through the same relay is mutually authorized and can verify each other with shurli verify <name>.
# List active invites
shurli relay invite list
# Revoke an invite
shurli relay invite revoke <group-id>Option B: Manual authorization
If you already know the peer IDs, add them directly:
# Using the CLI (works from any directory)
shurli relay authorize <peer-id> --comment "home-node"
shurli relay authorize <peer-id> --comment "client-node"
# Or edit the file directly (one peer ID per line)
sudo nano /etc/shurli/relay/relay_authorized_keys12D3KooWARqzAAN9es44ACsL7W82tfbpiMVPfSi1M5czHHYPk5fY # home-node
12D3KooWNq8c1fNjXwhRoWxSXT419bumWQFoTbowCwHEa96RJRg6 # client-nodeAfter manual edits, trigger a live reload:
shurli relay authorize <any-peer-id> # triggers reload of the full file
# Or restart if you prefer:
sudo systemctl restart shurli-relay2. Verify
Run the health check anytime:
cd ~/shurli
bash tools/relay-setup.sh --checkYou should see all [OK] items:
Binary:
[OK] shurli binary exists at /usr/local/bin/shurli
[OK] shurli is executable
[OK] Data directory permissions: 700
Configuration:
[OK] relay-server.yaml exists
[OK] Connection gating is ENABLED
[OK] relay-server.yaml permissions: 600
...
Service:
[OK] shurli-relay service is enabled (starts on boot)
[OK] shurli-relay service is running
[OK] Service runs as non-root user: peerup
...
=== Summary: 21 passed, 1 warnings, 0 failures ===
All good, but review [WARN] items for best security.3. Uninstall
To remove the systemd service, firewall rules, and system tuning:
cd ~/shurli
bash tools/relay-setup.sh --uninstallThis removes:
- The systemd service (stopped, disabled, file deleted)
- Firewall rules for port 7777
- QUIC buffer tuning from
/etc/sysctl.conf - Journald log rotation settings
It does not delete config, keys, or source code. To fully clean up:
sudo rm -rf /etc/shurli/relay # Remove relay data
sudo rm /usr/local/bin/shurli # Remove binary
rm -rf ~/shurli # Remove source (only if you want to)4. Useful Commands
# Service management
sudo systemctl status shurli-relay
sudo systemctl restart shurli-relay
sudo systemctl stop shurli-relay
# Follow logs
sudo journalctl -u shurli-relay -f
# Recent logs (last 50 lines)
sudo journalctl -u shurli-relay -n 50
# Check log disk usage
sudo journalctl --disk-usage
# Update relay server (after code changes)
cd ~/shurli
git pull
make install-relay
sudo systemctl restart shurli-relay5. Security Checklist
| Item | How to verify |
|---|---|
| Non-root user | whoami (should NOT be root) |
| SSH key-only login | grep PasswordAuthentication /etc/ssh/sshd_config - no |
| Root login disabled | grep PermitRootLogin /etc/ssh/sshd_config - no |
| Firewall active | sudo ufw status - active, default deny incoming |
| Only needed ports open | sudo ufw status - 22/tcp (SSH) + 7777/tcp+udp |
| Connection gating on | grep enable_connection_gating /etc/shurli/relay/relay-server.yaml - true |
| Data directory permissions | ls -ld /etc/shurli/relay/ - drwx------ (700, owner only) |
| Key file permissions | ls -la /etc/shurli/relay/relay_node.key - -rw------- (600) |
| Log rotation | grep SystemMaxUse /etc/systemd/journald.conf - 500M |
| System updates | sudo apt update && sudo apt upgrade |
Or just run: bash tools/relay-setup.sh --check
File Layout
After setup, the relay data and binary are installed to system paths:
/usr/local/bin/shurli # Binary (single binary for all modes)
/etc/shurli/relay/ # Data directory (700, owner only)
relay-server.yaml # Config (created by shurli relay setup)
relay_node.key # Identity key (auto-generated on first run)
relay_vault.json # Sealed vault (auto-created on first run)
relay_authorized_keys # Allowed peer IDs
.session # Session token (machine-bound)
.relay-admin.sock # Admin Unix socket (runtime)
.relay-admin.cookie # Admin auth cookie (runtime)
.relay-server.last-good.yaml # Self-healing: last valid config backupSource code (where you cloned the repo):
~/shurli/
tools/relay-setup.sh # Setup + health check script
deploy/shurli-relay.service # systemd service file
configs/relay-server.sample.yaml # Sample configurationManual Installation (Any Linux/Unix)
The tools/relay-setup.sh script targets Debian/Ubuntu with apt and ufw. If you run a different distribution, or prefer to set things up yourself, here’s everything the script does broken into individual steps.
Prerequisites
Go (version from go.mod, currently 1.26.0+):
# Download from https://go.dev/dl/
wget https://go.dev/dl/go1.26.0.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.26.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/binNative mDNS library (optional, enables LAN peer discovery):
| Distribution | Package | Install |
|---|---|---|
| Debian/Ubuntu | libavahi-compat-libdnssd-dev | sudo apt install libavahi-compat-libdnssd-dev |
| Fedora/RHEL/CentOS | avahi-compat-libdns_sd-devel | sudo dnf install avahi-compat-libdns_sd-devel |
| Arch Linux | avahi | sudo pacman -S avahi |
| Alpine Linux | avahi-compat-libdns_sd | sudo apk add avahi-compat-libdns_sd avahi-dev |
| openSUSE | avahi-compat-mDNSResponder-devel | sudo zypper install avahi-compat-mDNSResponder-devel |
| FreeBSD | avahi-libdns | sudo pkg install avahi-libdns |
| macOS | Built-in | No install needed (dns_sd is in libSystem) |
Without this library, Shurli falls back to pure-Go mDNS (works but less reliable on systems with a running mDNS daemon). Build with CGO_ENABLED=0 to explicitly use the fallback.
QR code tool (optional, for relay-setup.sh --check display):
| Distribution | Package |
|---|---|
| Debian/Ubuntu | qrencode |
| Fedora/RHEL | qrencode |
| Arch | qrencode |
| Alpine | libqrencode |
| macOS | brew install qrencode |
Build and install
git clone https://github.com/shurlinet/shurli.git
cd shurli
# Build and install to system paths
make install-relay
# This does:
# 1. Builds the binary with version embedding
# 2. Installs to /usr/local/bin/shurli
# 3. Creates /etc/shurli/relay/ with config files
# 4. Installs the systemd service (does not enable or start)Configure
# Edit if needed (defaults: port 7777, gating enabled)
sudo nano /etc/shurli/relay/relay-server.yamlService user (if not using the setup script)
The relay runs as whatever user you choose. You can use your existing SSH user or create a dedicated one:
# Option A: Use your existing user (recommended for single-admin VPS)
# No extra setup needed - make install-relay defaults to your user
# Option B: Create a dedicated user
sudo useradd -r -m -s /bin/bash shurli
sudo chown -R shurli:shurli /etc/shurli/relay
sudo chmod 700 /etc/shurli/relay
make install-relay SERVICE_USER=shurliNetwork tuning (QUIC performance)
# Increase UDP buffer sizes for QUIC
echo 'net.core.rmem_max=7500000' | sudo tee -a /etc/sysctl.conf
echo 'net.core.wmem_max=7500000' | sudo tee -a /etc/sysctl.conf
sudo sysctl -pFirewall
Open port 7777 for TCP and UDP:
| Firewall | Commands |
|---|---|
| ufw (Ubuntu) | sudo ufw allow 7777/tcp && sudo ufw allow 7777/udp |
| firewalld (Fedora/RHEL) | sudo firewall-cmd --permanent --add-port=7777/tcp && sudo firewall-cmd --permanent --add-port=7777/udp && sudo firewall-cmd --reload |
| iptables | sudo iptables -A INPUT -p tcp --dport 7777 -j ACCEPT && sudo iptables -A INPUT -p udp --dport 7777 -j ACCEPT |
| nftables | Add tcp dport 7777 accept and udp dport 7777 accept to your input chain |
| pf (FreeBSD/macOS) | Add pass in proto { tcp, udp } to port 7777 to /etc/pf.conf |
Also check your VPS provider’s security groups/firewall rules in their web console.
Install systemd service (manual)
sudo cp deploy/shurli-relay.service /etc/systemd/system/shurli-relay.service
# Update User/Group to match your service user (default in file is "shurli")
sudo sed -i 's/^User=.*/User=YOUR_USER/' /etc/systemd/system/shurli-relay.service
sudo sed -i 's/^Group=.*/Group=YOUR_USER/' /etc/systemd/system/shurli-relay.service
sudo systemctl daemon-reload
sudo systemctl enable --now shurli-relayFor non-systemd systems (Alpine with OpenRC, FreeBSD with rc.d, etc.), create a service script that runs:
/usr/local/bin/shurli relay serve --config /etc/shurli/relay/relay-server.yamlThe binary is self-contained. It reads config from the working directory or the path specified by --config.
Log rotation
For systemd systems, limit journal size:
sudo mkdir -p /etc/systemd/journald.conf.d
echo -e "[Journal]\nSystemMaxUse=500M\nMaxFileSec=7day" | sudo tee /etc/systemd/journald.conf.d/shurli-relay.conf
sudo systemctl restart systemd-journaldVerify
# Check service is running
sudo systemctl status shurli-relay
# Check it's listening
ss -tlnp | grep 7777
# Check logs
sudo journalctl -u shurli-relay -n 20
# Or run the health check script (even on non-Ubuntu, it reports useful info)
bash tools/relay-setup.sh --checkTroubleshooting
| Issue | Solution |
|---|---|
| Service fails to start | sudo journalctl -u shurli-relay -n 30 for error logs |
| “Permission denied” on key file | chmod 600 /etc/shurli/relay/relay_node.key |
| Peers can’t connect | Use shurli relay invite create to generate invite codes, or check relay_authorized_keys has their peer IDs |
| Random peers connecting | Verify enable_connection_gating: true in config |
| High log disk usage | sudo journalctl --vacuum-size=200M to trim now |
| Port not reachable | sudo ufw status and check VPS provider firewall/security group |
| “Permission denied” on relay commands | Relay commands auto-escalate to the config owner via sudo. Ensure your user has passwordless sudo or enter the password when prompted. |
| Service runs as root | bash tools/relay-setup.sh --uninstall then re-run bash tools/relay-setup.sh (as root it will guide you through user setup) |
dns_sd.h: No such file or directory | Install the avahi compat library for your distro (see Prerequisites above) |
| CGo build fails / not wanted | Build with CGO_ENABLED=0 go build ... to use pure-Go fallback (no native mDNS) |