Environment variables
Complete reference — where each value comes from.
This is the quick reference plus a ready-to-copy .env.example.
Paste the block below into the variables tab of the service you picked. On Render or Railway, pasting all of it saves each key as its own table row!
# Discord OAuth — the first login (empty database) becomes ADMIN automatically.
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
NEXTAUTH_SECRET=
NEXTAUTH_URL=http://localhost:3000
# LiveKit
LIVEKIT_URL=
LIVEKIT_API_KEY=
LIVEKIT_API_SECRET=
# Postgres
DATABASE_URL=
# Storage (optional — without it, chat has no image attachments).
# R2 is the default; for any other S3 endpoint fill in R2_ENDPOINT/R2_REGION
# instead of R2_ACCOUNT_ID.
R2_ACCOUNT_ID=
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_BUCKET=
NEXT_PUBLIC_R2_PUBLIC_BASE_URL=
R2_ENDPOINT=
R2_REGION=
# Infra dashboard (optional, self-host only)
SERVER_METRICS_INGEST_SECRET=
VPS_SSH_HOST=
VPS_SSH_PORT=22
VPS_SSH_USER=
VPS_SSH_PRIVATE_KEY=
# General
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_apiBaseUrl=http://localhost:3000
# Optional
NEXT_PUBLIC_RTC_ENABLED=
APP_COMMIT_SHA=
GIPHY_API_KEY=Each group below is detailed on its own page.
Discord and session (details)
| Variable | Notes |
|---|---|
DISCORD_CLIENT_ID | |
DISCORD_CLIENT_SECRET | |
NEXTAUTH_SECRET | openssl rand -base64 32 |
NEXTAUTH_URL | The app's public URL |
The first account to sign in on an empty database becomes ADMIN — there is
no variable for it.
LiveKit (Cloud or self-host)
| Variable | Notes |
|---|---|
LIVEKIT_URL | wss://... — same format for cloud and self-host |
LIVEKIT_API_KEY | |
LIVEKIT_API_SECRET |
Postgres (details)
| Variable | Notes |
|---|---|
DATABASE_URL |
General
| Variable | Notes |
|---|---|
NEXT_PUBLIC_APP_URL | Same URL as NEXTAUTH_URL |
NEXT_PUBLIC_apiBaseUrl | Always the same as NEXT_PUBLIC_APP_URL — Server Actions and Server Components call the app's own API through this absolute URL |
Miss this variable and the admin panel breaks
Without NEXT_PUBLIC_apiBaseUrl the admin and user Server Actions fetch
undefined/api/... and fail. It is a NEXT_PUBLIC_* variable, so it is
baked into the bundle at build time — setting it later and restarting the
service does not fix it, it needs a new deploy/build.
Storage (details, optional)
Without these the app still boots — it just does not accept image attachments
in chat. The R2_ prefix names the slot, not the provider: the same variables
serve Cloudflare R2 and Neon Object Storage.
| Variable | Notes |
|---|---|
R2_BUCKET | |
R2_ACCESS_KEY_ID | |
R2_SECRET_ACCESS_KEY | |
NEXT_PUBLIC_R2_PUBLIC_BASE_URL | Goes into the client bundle, not a secret |
R2_ACCOUNT_ID | R2 only — the endpoint is derived from it |
R2_ENDPOINT | Any non-R2 S3 endpoint (Neon, MinIO). Setting it also switches the client to path-style |
R2_REGION | Only with R2_ENDPOINT. Defaults to auto, which is what R2 requires; Neon needs the project's real region |
Optional
| Variable | Notes |
|---|---|
GIPHY_API_KEY | Enables the GIF button in chat. Create an app at developers.giphy.com. Never NEXT_PUBLIC_* — the search goes through the server |
NEXT_PUBLIC_RTC_ENABLED | Set to false, 0 or off to disable voice/video for the whole deploy. Anything else (including unset) keeps it on |
APP_COMMIT_SHA | The deploy's commit. With it, tabs open during a new deploy get an "update the page" notice. On Railway use RAILWAY_GIT_COMMIT_SHA |
Infra dashboard (optional — self-host only)
| Variable | Notes |
|---|---|
SERVER_METRICS_INGEST_SECRET | The same value on the app and on the VPS |
VPS_SSH_HOST | Also the flag that turns on the admin's "Infra" tab |
VPS_SSH_PORT | Usually 22 |
VPS_SSH_USER | A restricted user, not root |
VPS_SSH_PRIVATE_KEY | PEM private key |
VULTR_API_KEY (if you use Vultr) does not go in the app — it lives only on
the VPS, in the bandwidth collector.