rdio.

Overview

Understand the pieces rdio needs before choosing a hosting platform.

rdio is easiest to deploy when you treat it as three concerns:

  • a web admin app
  • an API that owns station state and proxies the public stream
  • persistent radio data and audio services

The current production model bundles the API, Liquidsoap, and Icecast in one API container. The web app is deployed separately and talks to the API through /api.

rdio is not tied to a single hosting provider. The repository includes a Fly.io guide because that is the first maintained deployment path, but any platform can work if it can run the required services and expose the right ports.

What must persist

Production deployments need durable storage for station data and uploaded media.

API volume (/media)

Mount persistent storage at /media on the API container. rdio stores schedule state, playout pointers, fallback audio, and related radio files under this path.

Object storage (R2)

Uploaded media files live in Cloudflare R2, not on the API volume. Configure R2 before expecting media uploads to work. See Cloudflare R2.

Postgres

Auth, members, and application data live in Postgres.

Required services

A production deployment needs:

  • Postgres for auth and application data
  • the API container (API + Liquidsoap + Icecast)
  • the web container
  • persistent storage mounted into the API container at /media
  • Cloudflare R2 for media uploads
  • public HTTP access to the web app
  • public HTTP access to the API stream proxy at /live.mp3
  • reachable harbor ports for live source clients (8005 plain, 8443 TLS)

Required secrets

Set these for the API. See Environment variables for the full reference.

SecretPurpose
DATABASE_URLPostgres connection string
BETTER_AUTH_SECRETSigns Better Auth cookies and tokens
BETTER_AUTH_URLPublic API origin
WEB_ORIGINAllowed web admin origins (comma-separated)
ICECAST_SOURCE_PASSWORDPassword Liquidsoap uses to publish to Icecast and harbor
R2_ACCOUNT_IDCloudflare account ID
R2_ACCESS_KEY_IDR2 API token access key
R2_SECRET_ACCESS_KEYR2 API token secret
R2_BUCKETR2 bucket name
R2_PUBLIC_URLPublic base URL for media objects

For the web app, keep VITE_API_BASE_URL=/api when the web server proxies same-origin API traffic.

Update apps/web/nginx.conf to point at your API hostname if it differs from the default.

Hosting options

Choose hosting based on the parts you need to run.

API and radio services

The API container also runs Liquidsoap and Icecast. Host it somewhere that can:

  • run the API Dockerfile or an equivalent container image
  • attach persistent storage at /media
  • connect to Postgres
  • expose public HTTP for /api and /live.mp3
  • expose harbor ports for live source clients, especially 8005 and optionally 8443
  • store API secrets securely

Examples include Fly.io, Render, Railway, DigitalOcean App Platform, AWS ECS, or a VPS that runs Docker.

Web admin

The web app can be deployed with the API or hosted separately. It only needs to serve the built frontend and reach the API. Static or container-friendly hosts such as Vercel, Netlify, Cloudflare Pages, Fly.io, Render, or a regular web server can work.

Media storage

Uploaded media currently uses Cloudflare R2. Keep the Cloudflare R2 setup even if the API and web app run somewhere else.

Deployment checklist

Before the first public launch:

  • create the production database
  • generate a strong BETTER_AUTH_SECRET
  • configure WEB_ORIGIN with your web admin URL
  • set up Cloudflare R2 and add R2_* secrets
  • mount persistent storage at /media
  • place fallback audio at /media/fallback/v1-tone.mp3
  • run database migrations
  • verify GET /api/health
  • verify the public stream at /live.mp3
  • verify harbor responds on port 8005
  • complete first-admin setup in the web UI

Next steps

Use the maintained Fly.io guide if you want to follow the repository's existing deployment config:

If something fails after deploy, see Troubleshooting.

On this page