# Run your own relay in 30 minutes

Stand up a team relay with Docker Compose on one Linux server, check its health, and point your Macs at it.

Silan's team relay carries sign-in, device bookings, approvals and the live picture between the Macs in your team. It is private: there is no shared relay, and each team runs its own. This guide uses the Docker Compose kit from the repo and ends with two Macs on the new server. For the systemd way and every setting, see the [self-hosting page](/self-host/).

Time: about 30 minutes, most of it waiting for DNS and your identity provider.

You need:

- A Linux server with 2 vCPU and 4 GB of memory, with Docker and Docker Compose on it.
- A DNS name that points at the server, for example `relay.example.com`, with ports 80 and 443 TCP and 443 UDP open.
- An OIDC client at your identity provider. A web app, confidential, with a client secret.
- The relay kit from the repo at `relay/deploy/self-host/`, copied to the server.

## Steps

1. Make the OIDC client at your identity provider. Set the redirect URI to `https://relay.example.com/signin/callback`, the grant to authorization code, and the scopes to `openid email profile offline_access`. The relay needs `email_verified` to be true in the token. On Entra ID, which does not send that claim, you will set `SILAN_RELAY_OIDC_TRUST_EMAIL=yes` in step 4.
2. Get the relay image onto the server. Either pull the one named in the kit's `env.example` as `silan-relay:<version>`, or build it from the repo:

   ```sh
   docker build -f relay/Dockerfile -t silan-relay:latest .
   ```

3. On the server, go into the kit folder and copy the two example files.

   ```sh
   cd relay/deploy/self-host
   cp env.example .env
   cp relay.env.example relay.env
   ```

   In `.env` set `SILAN_RELAY_DOMAIN` to your DNS name and `SILAN_RELAY_IMAGE` to the image from step 2.

4. Open `relay.env` and fill in every line marked `CHANGE`: the OIDC issuer, the client id, and on Entra ID the `SILAN_RELAY_OIDC_TRUST_EMAIL=yes` line. Leave the rest as it is.
5. Make the two secrets the relay reads from files, and give them to the relay's user inside the container.

   ```sh
   mkdir -p secrets
   head -c 32 /dev/urandom | base64 > secrets/seal_key
   printf '%s\n' 'THE CLIENT SECRET' > secrets/oidc_client_secret
   sudo chown 10001:10001 secrets/*
   sudo chmod 600 secrets/*
   ```

   Keep a copy of `seal_key` somewhere safe. It seals what the relay stores, and a new key cannot read old data.

6. Check the compose file reads cleanly, then start the relay. The kit's `Caddyfile` fetches the TLS certificate for your domain on the first start.

   ```sh
   docker compose config --quiet
   docker compose up -d
   docker compose ps
   ```

7. Watch the log for the line that says the hub is up, then ask the health endpoint from any machine.

   ```sh
   docker compose logs relay | grep "Relay hub listening"
   curl https://relay.example.com/health
   ```

   Health answers with JSON such as `{"macs":0,"videoMacs":0,"dataVersion":…,"signIn":"oidc"}`. `signIn` must read `oidc`.

<figure class="shot"><img src="/media/relay-test.webp" alt="Terminal: the relay and its sign-in service up in Docker on one Mac, and the health answer from the relay." width="1180" height="647" loading="lazy" /><figcaption>Terminal: the relay and its sign-in service up in Docker on one Mac, and the health answer from the relay.</figcaption></figure>

8. Decide who may sign up. The kit starts open behind your company sign-in, which is right for most teams. To close it to a list, set `SILAN_RELAY_SIGN_UPS=private` and `SILAN_RELAY_ALLOWED_EMAILS` in `relay.env`, then `docker compose up -d` again.
9. Turn on backups before you forget. Set `LITESTREAM_REPLICA_URL` to a bucket of yours and start with both files:

   ```sh
   docker compose -f compose.yaml -f compose.backup.yaml up -d
   ```

## Point your Macs at it

10. On each Mac, open Silan and go to **Settings › Account & Team**. Under **No team server yet**, type `relay.example.com` and click **Use this server**.
11. Click **Sign in**. The browser opens your identity provider, you sign in, and Silan shows **Server: relay.example.com** with your name.
12. Make a team, invite a teammate with the invite link, and share a phone. The [sharing guide](/guides/share-a-phone-with-a-teammate/) takes it from here. An invite link fills in the server address for the person who opens it, so only the first person types it.

To upgrade later, set the new image in `.env`, then `docker compose pull` and `docker compose up -d`. The relay keeps its teams across a restart.

## If something goes wrong

- **Health does not answer.** Check that ports 80 and 443 reach the server, and that the DNS name resolves to it. Caddy cannot get a certificate otherwise, and `docker compose logs caddy` says why.
- **Sign-in fails with an email error.** The token has no `email_verified: true`. Turn the claim on at the provider, or on Entra ID set `SILAN_RELAY_OIDC_TRUST_EMAIL=yes`.
- **The browser returns to a wrong page.** The redirect URI at the provider must be exactly `https://relay.example.com/signin/callback`.
- **The picture through the relay is choppy.** Open 443 UDP as well as TCP. The live picture uses it.

:::note[Proof]
Proved on an iPad Air on 2 Nov 2026 by a Silan worker against a test relay: run `20261102-134410-45232` covered sign-in, making a team, an invite, sharing, a booking, an approval, taps, and the picture through the relay (786 frames in 30 seconds), and a relay restart kept the team. Source: verify-silan feature map, team-relay, and the self-host README.
:::