Set up your relay

The relay carries a transfer when two devices cannot reach each other directly. Running it yourself keeps that traffic on your own infrastructure. It is free for personal use; a business license adds multiple relays, unlimited simultaneous transfers and usage metrics. You need a server with a public IP and Docker installed.

Installation

  1. 1

    Open UDP 49737

    Allow it in your firewall and in your provider's security group.

    sudo ufw allow 49737/udp
  2. 2

    Create docker-compose.yml

    services:
      relay:
        image: ghcr.io/altersend/relay:latest
        network_mode: host
        restart: unless-stopped
        environment:
          RELAY_HOST: ${RELAY_HOST:?set RELAY_HOST to this server's public IP}
          ALTERSEND_LICENSE: ${ALTERSEND_LICENSE:-}
        volumes:
          - relay-data:/etc/altersend-relay
    
    volumes:
      relay-data:
  3. 3

    Set your server address

    Compose reads .env on every command, so keep it in the file rather than passing it inline.

    echo 'RELAY_HOST=203.0.113.7' > .env
  4. 4

    Start the relay

    On first boot it generates a permanent identity and stores it in the volume.

    docker compose up -d
  5. 5

    Copy your relay code

    The logs print your code and a QR code for phones, followed by a stats line every 30 seconds once it is running.

    docker compose logs relay
    AlterSend relay listening
    relay public key: 6fad022df811a2bfbc734260df47c55499973409bc34b89f66ad791c24be2197
    
    add this relay in AlterSend — Settings → Connection:
    
      relay:6fad022df811a2bfbc734260df47c55499973409bc34b89f66ad791c24be2197@203.0.113.7
    
    [stats] egress 0GB | sessions=0 pairings=0 matched=0
  6. 6

    Add it in AlterSend

    Settings → Connection → Self-hosted relay. Paste the code and press send: it saves and tests the connection in one step.

    Only your own devices need the code. When you send to someone else, your app passes the relay details along with the connection.

Business licenses

Add your license to .env and restart. The relay now prints an org code instead of a relay code. Hand that one code to every device in your team; the servers behind it are managed centrally and can change without anyone re-pasting.

ALTERSEND_LICENSE=<your license>

# optional: POST bytes and timestamp per transfer, nothing else
REPORT_URL=https://example.com/hook
REPORT_TOKEN=<bearer token>
[selfhost] licensed to Acme GmbH until 2027-08-27
[selfhost] published org relay record (seq 1)

add this relay in AlterSend — Settings → Connection:

  org:7f6f2d059d8a549e30314fa436a98fbe1f62d314a5ec2a2b2be980075ef75226

The org code is the public key of a signed record on the network holding your relay list, addresses included. Devices read the list from it, which is why changing servers never changes the code.

Running several relays

Install the same way on each server, using the same license so none of them are capped. Get the new relay's public key, then publish the full list from the server that printed your org code. Other servers print org codes of their own on first boot; ignore those and only ever hand out the first one.

# on the new server
docker compose exec relay node key.js

# on the server that printed your org code
docker compose exec relay node conf/publish.js \
  <key1>@203.0.113.7@-5 \
  <key2>@198.51.100.4@8

Updates and backups

Updating pulls the new image and restarts; your identity and settings live in the volume, so codes stay valid.

docker compose pull
docker compose up -d

Troubleshooting

The logs say the variable RELAY_HOST is missing

Compose reads .env for every command, not just up. Make sure the file sits next to docker-compose.yml and contains RELAY_HOST.

The app says it cannot reach the relay

UDP 49737 is almost always the cause. Check both your firewall and your provider's security group, then confirm the container is running with docker compose ps.

Transfers still show as direct

Usually normal: a relay only carries a transfer when the two devices cannot reach each other directly, and the direct path is faster when available. If it never says relayed, check that RELAY_HOST is an IP rather than a domain, since the app matches the connection address to label the transfer.

Who can use my relay?

Anyone holding your relay code. It is not a shared secret you can rotate, so treat it like an invite: hand it to your own devices and people you work with. Transfers stay end-to-end encrypted either way.