Self-hosting
Installer options, Docker Compose, building from source, HTTPS and environment variables.
Installer options
Options go in front of sh:
curl -fsSL runelay.com/install \
| PUBLIC_URL=https://runelay.example.com RUNELAY_DIR=/opt/runelay RUNELAY_VERSION=v0.1.0 sh| Variable | Default | Description |
|---|---|---|
RUNELAY_DIR | ~/runelay | Install folder. |
RUNELAY_VERSION | latest | Release to install. |
RUNELAY_PORT | 4312 | Port the server listens on. |
PUBLIC_URL | http://localhost:<port> | Public URL written to .env. |
Docker Compose by hand
services:
runelay:
image: ghcr.io/andsu-dev/runelay:latest
restart: unless-stopped
env_file: .env
environment:
RUNELAY_UPDATER_URL: http://updater:8080
labels:
com.centurylinklabs.watchtower.enable: "true"
ports:
- "127.0.0.1:4312:4312"
volumes:
- ./data:/data
updater:
image: containrrr/watchtower:1.7.1
restart: unless-stopped
command: --http-api-update --label-enable --cleanup
environment:
WATCHTOWER_HTTP_API_TOKEN: ${RUNELAY_UPDATER_TOKEN:-}
DOCKER_API_VERSION: "1.44"
volumes:
- /var/run/docker.sock:/var/run/docker.sockThe updater service is optional. It powers the one-click update in the panel and only touches containers labeled for it. Leave it out if you do not want a container with access to the Docker socket.
PUBLIC_URL=https://runelay.example.com
RUNELAY_SECRET=$(openssl rand -hex 32)
ALLOW_SIGNUP=false
RUNELAY_UPDATER_TOKEN=$(openssl rand -hex 24)Build from source
git clone https://github.com/Andsu-dev/runelay.git && cd runelay
cp .env.example .env
docker compose up -d --buildSet PUBLIC_URL and RUNELAY_SECRET in .env. bun run dev is only for working on runelay itself; to run it, use the image.
HTTPS
Put HTTPS in front of the 127.0.0.1:4312 binding.
runelay.example.com {
reverse_proxy 127.0.0.1:4312
}ingress:
- hostname: runelay.example.com
service: http://127.0.0.1:4312
- service: http_status:404OAuth callbacks go to PUBLIC_URL/v1/oauth/callback, so PUBLIC_URL must be the public address.
Environment variables
| Variable | Default | Description |
|---|---|---|
PUBLIC_URL | http://localhost:4312 | Public URL of the panel and API. Used for OAuth callbacks and cookies. |
RUNELAY_SECRET | required, 32+ chars | Encrypts connection configs, OAuth tokens and recoverable API keys. Losing it means reconnecting everything. |
RUNELAY_SETUP_TOKEN | generated | One-time token required to create the first account. |
RUNELAY_UPDATER_TOKEN | generated | Shared with the updater container. Owners and admins can then update from the panel. |
RUNELAY_UPDATER_URL | set by compose.yaml | Where the updater listens. |
ALLOW_SIGNUP | false | Keep signups open after the first account. |
RUNELAY_ENABLE_EXECUTE | true | Enables the sandboxed execute tool. See Security. |
RUNELAY_ENABLE_STDIO | false | Enables local MCP servers by command. See Security. The Docker image ships without Node, so commands like npx need the desktop app or a custom image. |
RUNELAY_ALLOW_PRIVATE_URLS | false | Allows connections to private network addresses. |
RUNELAY_RATE_LIMIT_PER_MINUTE | 240 | Requests per minute allowed for each API key. |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | empty | Enables "Sign in with GitHub" on the panel. |
RUNELAY_DATA | /data in Docker | Where the SQLite database lives. |
PORT / HOST | 4312 / 0.0.0.0 in Docker | Where the server listens. |