Skip to content

Self-Hosting

import { Steps } from ‘@astrojs/starlight/components’;

br\u016bhi Cloud is packaged as a single Docker image containing all services. Docker Compose is the recommended deployment method.

  1. Get the source

    Download and extract the brūhi Cloud release package, then navigate to the directory:

    Terminal window
    cd bruhi-cloud
  2. Create your environment file

    Terminal window
    cp .env.example .env

    Edit .env and configure your deployment settings:

    # Set Compose profiles (bundled-icecast, or bundled-icecast,proxy for Auto-HTTPS)
    COMPOSE_PROFILES=bundled-icecast
    # Domain for Caddy auto-HTTPS (if using proxy profile)
    DOMAIN=radio.yourdomain.com
    BRUHI_URL=https://radio.yourdomain.com
    BRUHI_RP_ID=radio.yourdomain.com
    CORS_ORIGINS=https://radio.yourdomain.com
  3. Start

    Terminal window
    docker compose up -d
  4. Verify

    Terminal window
    curl http://localhost:8000/healthz
    # {"status":"ok"}
VariableDefaultDescription
PORT8000API server & web dashboard host port
LOG_LEVELinfoLog verbosity (debug, info, warning, error)
BRUHI_ENVdevelopmentEnvironment mode (development or production)
BRUHI_URLhttp://localhost:8000Base public URL used for passkeys, reset links, etc.
COMPOSE_PROFILESbundled-icecastActive Compose profiles (bundled-icecast, proxy)
BRUHI_ICECAST_MODEbundledbundled (internal container) or external
ICECAST_HOST_PORT8010Host port mapped to bundled Icecast
ICECAST_SOURCE_PASSWORDhackmeSource connection password for Icecast
ICECAST_ADMIN_PASSWORDhackmeAdmin web UI password for Icecast
ICECAST_RELAY_PASSWORDhackmeRelay password for Icecast
BRUHI_DB/app/data/bruhi.dbPath to consolidated SQLite database inside container
AUDIO_DIR/app/audio_filesPath to uploaded audio files & WAV recordings
BRUHI_AUDIO_APIhttp://localhost:7700bruhi-audio Rust engine control daemon API URL
BRUHI_AUDIO_SOCKET_DIR/tmp/bruhi-audioUnix sockets shared between FastAPI and Rust engine
BRUHI_RP_IDlocalhostWebAuthn / Passkeys Relying Party ID (domain name)
BRUHI_RP_NAMEbrūhi CloudWebAuthn / Passkeys application display name
BRUHI_ADMIN_EMAILOptional initial admin/owner email for pre-seeding
BRUHI_ADMIN_PASSWORDOptional initial admin/owner password for pre-seeding
CORS_ORIGINS''Space-separated allowed CORS origins
API_TOKENBearer authorization token for administrative API calls
S3_ENDPOINT_URLS3-compatible storage endpoint URL (optional)
S3_ACCESS_KEYS3 access key
S3_SECRET_KEYS3 secret key
S3_BUCKETS3 bucket name
S3_REGIONus-east-1S3 region

Production Docker Compose (docker-compose.prod.yml)

Section titled “Production Docker Compose (docker-compose.prod.yml)”

The production deployment uses docker-compose.prod.yml with version-pinned images and auto-restart policies:

services:
icecast:
profiles: ["bundled-icecast"]
image: libretime/icecast:2.5.0-alpine
container_name: bruhi-icecast
ports:
- "${ICECAST_HOST_PORT:-8010}:8000"
environment:
ICECAST_SOURCE_PASSWORD: "${ICECAST_SOURCE_PASSWORD:?Required}"
ICECAST_ADMIN_PASSWORD: "${ICECAST_ADMIN_PASSWORD:?Required}"
ICECAST_RELAY_PASSWORD: "${ICECAST_RELAY_PASSWORD:?Required}"
ICECAST_HOSTNAME: "${ICECAST_HOSTNAME:-localhost}"
restart: always
bruhi-cloud:
image: "${IMAGE:-ghcr.io/bruhi-technologies/bruhi-cloud:latest}"
container_name: bruhi-cloud
depends_on:
icecast:
condition: service_started
required: false
ports:
- "${PORT:-8000}:8000"
volumes:
- bruhi_audio:/app/audio_files
- bruhi_playlists:/tmp/liquidsoap-playlists
- bruhi_db:/app/data
- bruhi_audio_sockets:/tmp/bruhi-audio
environment:
PORT: "${PORT:-8000}"
LOG_LEVEL: "${LOG_LEVEL:-warning}"
BRUHI_ENV: "production"
SESSION_COOKIE_SECURE: "true"
CORS_ORIGINS: "${CORS_ORIGINS:-}"
BRUHI_ICECAST_MODE: "${BRUHI_ICECAST_MODE:-bundled}"
BRUHI_URL: "${BRUHI_URL}"
BRUHI_RP_ID: "${BRUHI_RP_ID}"
BRUHI_RP_NAME: "${BRUHI_RP_NAME:-brūhi Cloud}"
BRUHI_DB: "/app/data/bruhi.db"
AUDIO_DIR: "/app/audio_files"
BRUHI_AUDIO_API: "http://localhost:7700"
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/healthz"]
interval: 60s
timeout: 10s
retries: 3
caddy:
profiles: ["proxy"]
image: caddy:2.8-alpine
container_name: bruhi-caddy
depends_on:
- bruhi-cloud
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
environment:
DOMAIN: "${DOMAIN:-localhost}"
restart: always
volumes:
bruhi_audio:
bruhi_playlists:
bruhi_db:
bruhi_audio_sockets:
caddy_data:
caddy_config:
VolumeMount pathContents
bruhi_audio/app/audio_filesUploaded audio files and WAV recordings
bruhi_playlists/tmp/liquidsoap-playlistsStation playout queues and temporary files
bruhi_db/app/dataConsolidated SQLite database (bruhi.db)
bruhi_audio_sockets/tmp/bruhi-audioshared Unix IPC sockets between Python and Rust
caddy_data/dataTLS certificates provisioned by Caddy

brūhi Cloud provides a zero-config reverse proxy setup powered by Caddy. When enabled, Caddy automatically obtains and renews free SSL certificates via Let’s Encrypt.

  1. Set COMPOSE_PROFILES=bundled-icecast,proxy in your .env file.
  2. Set DOMAIN=radio.yourdomain.com in your .env file.
  3. Ensure DNS records for radio.yourdomain.com point to your server’s IP address.
  4. Run docker compose up -d.

If you already operate an Nginx reverse proxy:

server {
listen 443 ssl;
server_name radio.yourdomain.com;
ssl_certificate /etc/ssl/bruhi.crt;
ssl_certificate_key /etc/ssl/bruhi.key;
location / {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}

The Upgrade and Connection headers are required for WebSockets (used for WebRTC signaling and real-time station state updates).

Before exposing brūhi Cloud to the internet:

  • Enable HTTPS via bundled Caddy (COMPOSE_PROFILES=bundled-icecast,proxy) or Nginx
  • Set BRUHI_RP_ID and BRUHI_URL to match your domain name
  • Set ICECAST_SOURCE_PASSWORD and ICECAST_ADMIN_PASSWORD to strong secrets
  • Set CORS_ORIGINS to your domain name (avoid *)
  • Configure firewall rules: allow ports 80, 443 (HTTPS), 8010 (Icecast), 8100+ (harbors)
  • Schedule regular volume backups for bruhi_db and bruhi_audio
  • Set LOG_LEVEL=warning for production
Terminal window
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

brūhi Cloud follows semantic versioning. Check the release notes before upgrading minor or major versions.

ProviderServiceStatus
AWSEC2, ECS, EKS✅ Verified
Google CloudGCE, GKE✅ Verified
DigitalOceanDroplets, K8s✅ Verified
HetznerCloud, Dedicated✅ Community tested