> ## Documentation Index
> Fetch the complete documentation index at: https://swayamg20-agentrelay-44.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosting the AgentRelay relay server

> Deploy the AgentRelay relay for your team using Docker. Covers secret rotation, health verification, public exposure, and recommended hosting options.

The relay is the single shared service your whole team runs through — one deployment, done once by the team lead. It stores handoffs, enforces the block list, dispatches Slack notifications, and hosts everyone's Agent Cards. You only need Docker; there is no Node, pnpm, or build step required on the host machine.

<Warning>
  **Rotate your secrets before exposing the relay publicly.** The `.env.example` file ships with placeholder values that are safe for local testing but must not be used on a server accessible from the internet. Run the commands in Step 2 before you open any firewall or reverse-proxy rule.
</Warning>

## Prerequisites

* Docker (any recent version with Compose v2 — `docker compose`, not `docker-compose`)
* `openssl` available on your PATH (ships with macOS and most Linux distributions)
* A domain or tunnel to expose the relay to teammates on other laptops

***

## Setup

<Steps>
  <Step title="Clone the repo and copy the environment file">
    ```bash theme={null}
    git clone https://github.com/swayamg20/AgentRelay
    cd AgentRelay
    cp .env.example .env
    ```

    The `.env` file is read automatically by `docker compose`. All relay configuration — database credentials, secrets, public URL — lives here.
  </Step>

  <Step title="Rotate the secrets">
    The example file ships with stable dev-only placeholders. Replace every secret with a freshly generated random value before exposing the relay outside localhost.

    <Warning>
      Run these three commands exactly as shown. They overwrite the placeholder values in-place. `RELAY_PEPPER` hashes every API key — if you change it later, all existing API keys become invalid and every teammate must re-register.
    </Warning>

    ```bash theme={null}
    sed -i '' "s|^RELAY_PEPPER=.*|RELAY_PEPPER=$(openssl rand -hex 32)|" .env
    sed -i '' "s|^RELAY_ADMIN_TOKEN=.*|RELAY_ADMIN_TOKEN=$(openssl rand -hex 16)|" .env
    sed -i '' "s|^RELAY_ENCRYPTION_KEY=.*|RELAY_ENCRYPTION_KEY=$(openssl rand -hex 32)|" .env
    ```

    On Linux, drop the `''` after `-i`:

    ```bash theme={null}
    sed -i "s|^RELAY_PEPPER=.*|RELAY_PEPPER=$(openssl rand -hex 32)|" .env
    sed -i "s|^RELAY_ADMIN_TOKEN=.*|RELAY_ADMIN_TOKEN=$(openssl rand -hex 16)|" .env
    sed -i "s|^RELAY_ENCRYPTION_KEY=.*|RELAY_ENCRYPTION_KEY=$(openssl rand -hex 32)|" .env
    ```

    Stash the resulting `RELAY_ADMIN_TOKEN` value somewhere secure (see [Share the admin token](#share-the-admin-token) below) — your teammates need it to register.
  </Step>

  <Step title="Start the relay">
    ```bash theme={null}
    docker compose --profile selfhost up -d
    ```

    This brings up two containers:

    | Container             | Image                         | Port          | Role               |
    | --------------------- | ----------------------------- | ------------- | ------------------ |
    | `agentrelay-postgres` | `postgres:16-alpine`          | `5433` (host) | Persistent storage |
    | `agentrelay-relay`    | Built from `relay/Dockerfile` | `8080` (host) | Relay service      |

    The relay waits for Postgres to pass its health check, then runs Drizzle migrations on boot (idempotent — safe to re-run). Both containers restart `unless-stopped`, so they survive host reboots.
  </Step>

  <Step title="Verify the relay is healthy">
    ```bash theme={null}
    curl http://localhost:8080/healthz
    ```

    You should receive:

    ```json theme={null}
    {"status":"ok"}
    ```

    If the health check fails, check container logs:

    ```bash theme={null}
    docker compose logs relay --tail 50
    ```

    Common causes: missing or empty secrets (the relay's zod config validation rejects empty values with a descriptive error at startup), or Postgres not yet healthy.
  </Step>
</Steps>

***

## Expose to the internet

Teammates on other laptops need to reach the relay over HTTPS. Point a reverse proxy at port `8080` on the host running Docker, then set `RELAY_PUBLIC_URL` in `.env` to the public URL and restart:

```bash theme={null}
# Edit .env: set RELAY_PUBLIC_URL=https://relay.acme.dev
docker compose --profile selfhost up -d   # picks up the new env value
```

<Tabs>
  <Tab title="nginx">
    ```nginx theme={null}
    server {
        listen 443 ssl;
        server_name relay.acme.dev;

        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    ```
  </Tab>

  <Tab title="Caddy">
    ```caddyfile theme={null}
    relay.acme.dev {
        reverse_proxy localhost:8080
    }
    ```

    Caddy handles TLS automatically via Let's Encrypt.
  </Tab>

  <Tab title="Cloudflare Tunnel">
    ```bash theme={null}
    cloudflared tunnel --url http://localhost:8080
    ```

    For a persistent tunnel, configure it via the Cloudflare dashboard and set the resulting `*.trycloudflare.com` (or custom) URL as `RELAY_PUBLIC_URL`.
  </Tab>
</Tabs>

***

## Share the admin token

`RELAY_ADMIN_TOKEN` is what each teammate passes to `agentrelay register` to create their identity on the relay. Treat it like a password.

* Store it in **1Password**, your team's password manager, or another secrets vault.
* DM it to each teammate over a secure channel (not Slack public channels or email).
* Do not commit `.env` to version control.

***

## Recommended hosting

For teams that prefer not to manage their own Linux box, any container-capable PaaS works. The relay is a single stateless container; Postgres can be the managed database your host offers.

<CardGroup cols={2}>
  <Card title="Fly.io" icon="plane" href="https://fly.io">
    256 MB shared-cpu VM + Fly Postgres. Approximately **\$5–10/month** for a small team. Good free tier for testing.
  </Card>

  <Card title="Render" icon="server" href="https://render.com">
    Web Service + managed Postgres. Approximately **\$7–15/month**. Simple deploy from a Docker image.
  </Card>

  <Card title="Railway" icon="train-track" href="https://railway.app">
    Docker deploy + Railway Postgres. Approximately **\$5–12/month** depending on usage. Good developer experience.
  </Card>

  <Card title="Self-hosted VPS" icon="hard-drive">
    Any VPS (DigitalOcean, Hetzner, Linode) with Docker installed. Full control, lowest cost at scale.
  </Card>
</CardGroup>

<Note>
  For a small engineering team (under 50 developers, under 10k handoffs per day), the smallest paid tier on any of these platforms is sufficient. The relay's resource ceiling in v0.1 is the notification dispatcher's webhook throughput, not compute or memory.
</Note>

***

## What's running

After a successful `docker compose --profile selfhost up -d`, two containers are active:

* **`agentrelay-postgres`** — Postgres 16 on host port `5433`. Data is persisted in the `postgres-data` Docker volume. Credentials are set by `POSTGRES_PASSWORD` in `.env`.
* **`agentrelay-relay`** — The Hono relay service on host port `8080`. Built from `relay/Dockerfile` (multi-stage: pnpm build in the builder stage, slim runtime image). Connects to Postgres over the internal Docker network.

Both containers have `restart: unless-stopped`, so they come back automatically after a host reboot or a Docker daemon restart.

***

## Next step

Once the relay is running and reachable, share the relay URL and admin token with your teammates so they can complete [developer onboarding](/setup/developer-onboarding).
