Airwave

Configuration

The full .env reference for self-hosting Airwave — every variable, what it does, its default or example, and whether it's required.

Everything about a deployment is configured through a single .env file that docker-compose.yml reads. Copy .env.example to .env, fill it in, and deploy. This page documents every variable, grouped by concern.

Required variables have no safe default — the stack won't work without them. Everything else is optional and shown with its default.

Image

VariableWhat it doesDefault / exampleRequired
CG_IMAGEThe published image to run. Pin a version for reproducible deploys, or track latest.ghcr.io/quixomatic/airwave:latestNo

Public URLs (browser + TV facing)

These are the addresses your browser and TV actually use to reach the apps. They're baked into the admin build and drive auth + CORS, so they must be real, reachable addresses — a LAN IP or a domain with the published ports below. Do not use localhost unless you only ever browse from the host machine.

VariableWhat it doesDefault / exampleRequired
SERVER_PUBLIC_URLWhere the API/server is reachable (also the TV's server URL). Feeds BETTER_AUTH_URL and the admin's VITE_SERVER_URL.http://192.168.1.10:36020Yes
WEB_PUBLIC_URLWhere the admin web is reachable. Feeds the server's CORS_ORIGIN.http://192.168.1.10:36021Yes
EXTRA_CORS_ORIGINSExtra admin origins to allow-list for CORS + auth beyond WEB_PUBLIC_URL — a comma-separated list of exact origins (scheme + host + port, no trailing slash). Use it when the admin is reachable at more than one address (e.g. a public HTTPS domain plus a LAN IP).(empty)No

Published ports

Host-side ports mapped to the fixed container ports (3000 server, 3001 web). Each must match the port in the matching public URL above.

VariableWhat it doesDefault / exampleRequired
SERVER_PORTHost port for the server → container 3000.36020No (default 36020)
WEB_PORTHost port for the admin web → container 3001.36021No (default 36021)

Postgres

The database credentials. docker-compose.yml builds the server's DATABASE_URL from these and points it at the postgres service by name.

VariableWhat it doesDefault / exampleRequired
POSTGRES_USERDatabase user.channelguideYes
POSTGRES_PASSWORDDatabase password — change it.change-me-pleaseYes
POSTGRES_DBDatabase name.channelguideYes

Auth & security

VariableWhat it doesDefault / exampleRequired
BETTER_AUTH_SECRETSigning secret for sessions/tokens. Must be 32+ characters of random data — generate with openssl rand -base64 48.(random string)Yes
ADMIN_EMAILEmail for the first admin, seeded on first boot.admin@example.comNo (recommended)
ADMIN_PASSWORDPassword for that first admin. Set both to seed the account.change-meNo (recommended)

The admin seed runs on server startup: it creates the account once (if the email doesn't exist) and gives it the admin role, then on later boots just re-asserts the role. Leave both unset for a pure Plex/OAuth deployment — the seed becomes a no-op.

Runtime user & timezone

Control the uid/gid the container writes as (so files land as your host's owner) and the timezone. Important on TrueNAS datasets and other bind-mounted storage.

VariableWhat it doesDefault / exampleRequired
PUIDUser id the app process runs as (files are written as this owner).1000No (default 1000)
PGIDGroup id the app process runs as.1000No (default 1000)
UMASKFile-creation mask for the app process.022No (default 022)
TZTimezone (also applied to Postgres).UTCNo (default UTC)

Bumper ambient music (optional)

The bumper-music library persists via a volume mounted at the fixed container path /data/bumper-music. You choose only the host side.

VariableWhat it doesDefault / exampleRequired
BUMPER_MUSIC_VOLUMEHost side of the bumper-music mount. Unset = a Docker named volume (channelguide_bumpermusic). Set a bind path to manage the files from your host (drop tracks in, then "Scan folder" on the Bumpers page)./mnt/tank/apps/airwave/bumper-musicNo

TV web player (optional)

The 10-foot TV app served as an auth-gated browser web player. Off by default; enable it by adding tvweb to COMPOSE_PROFILES. See Roles & the single image. The installed webOS/Tizen apps need none of this (they're bearer-auth, origin-agnostic).

VariableWhat it doesDefault / exampleRequired
COMPOSE_PROFILESSet to tvweb to bring up the optional TV web player service.(empty)No
TV_WEB_PUBLIC_URLWhere the player is reachable in the browser. Also allow-listed on the server (as TV_APP_ORIGIN) for the TV login flow.http://192.168.1.10:36022Only if tvweb enabled
TV_WEB_PORTHost port for the player → container 3002.36022No (default 36022)
TV_SERVER_URLThe server URL baked into the player build. Defaults to SERVER_PUBLIC_URL; override to point the player at its own public domain (e.g. reverse-proxied with /api forwarded) so the server itself can stay unexposed.https://airwave-tv.example.comNo

Social OAuth (optional)

Set both the id and secret to enable a provider; leave blank to disable it.

VariableWhat it doesDefault / exampleRequired
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRETEnable Google sign-in.(empty)No
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRETEnable GitHub sign-in.(empty)No

Plex & AI workflow (optional)

VariableWhat it doesDefault / exampleRequired
PLEX_CLIENT_IDENTIFIERA stable X-Plex-Client-Identifier for the Plex login handshake.(empty)No
WORKFLOW_ENABLEDSet to 1 to enable the durable AI-lineup workflow engine (its Postgres schema is bootstrapped on start). Off by default.(empty)No

Values compose derives for you

You normally don't set these directlydocker-compose.yml builds them from the variables above and injects them per service. Listed here so you recognize them in logs and the compose file:

VariableWhere it comes from
CG_ROLEFixed per service (server / web / tvweb) — selects which app the image runs as.
DATABASE_URLBuilt from POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB, pointed at the postgres service.
BETTER_AUTH_URLSet to SERVER_PUBLIC_URL (also decides the auth cookie's SameSite/Secure from its scheme).
CORS_ORIGINSet to WEB_PUBLIC_URL.
VITE_SERVER_URLBaked into the admin build from SERVER_PUBLIC_URL (and the player build from TV_SERVER_URL).
TV_APP_ORIGINSet to TV_WEB_PUBLIC_URL when the tvweb service is enabled.
BUMPER_MUSIC_DIRFixed container path /data/bumper-music — leave it alone; pick the host side with BUMPER_MUSIC_VOLUME.
WORKFLOW_TARGET_WORLD / WORKFLOW_LOCAL_BASE_URL / WORKFLOW_POSTGRES_URLDefaulted / derived when WORKFLOW_ENABLED=1.

Source map

ConcernFile
Env reference (copy to .env).env.example
Compose (where each var is consumed / derived)docker-compose.yml
Server env schema (validated at boot)packages/env/src/server.ts
Auth cookie scheme derivationpackages/auth/src/index.ts

See also: Docker quick start · Roles & the single image · Updating

On this page