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.
Quick Deploy
Section titled “Quick Deploy”-
Get the source
Download and extract the brūhi Cloud release package, then navigate to the directory:
Terminal window cd bruhi-cloud -
Create your environment file
Terminal window cp .env.example .envEdit
.envand 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.comBRUHI_URL=https://radio.yourdomain.comBRUHI_RP_ID=radio.yourdomain.comCORS_ORIGINS=https://radio.yourdomain.com -
Start
Terminal window docker compose up -d -
Verify
Terminal window curl http://localhost:8000/healthz# {"status":"ok"}
Environment Variables
Section titled “Environment Variables”| Variable | Default | Description |
|---|---|---|
PORT | 8000 | API server & web dashboard host port |
LOG_LEVEL | info | Log verbosity (debug, info, warning, error) |
BRUHI_ENV | development | Environment mode (development or production) |
BRUHI_URL | http://localhost:8000 | Base public URL used for passkeys, reset links, etc. |
COMPOSE_PROFILES | bundled-icecast | Active Compose profiles (bundled-icecast, proxy) |
BRUHI_ICECAST_MODE | bundled | bundled (internal container) or external |
ICECAST_HOST_PORT | 8010 | Host port mapped to bundled Icecast |
ICECAST_SOURCE_PASSWORD | hackme | Source connection password for Icecast |
ICECAST_ADMIN_PASSWORD | hackme | Admin web UI password for Icecast |
ICECAST_RELAY_PASSWORD | hackme | Relay password for Icecast |
BRUHI_DB | /app/data/bruhi.db | Path to consolidated SQLite database inside container |
AUDIO_DIR | /app/audio_files | Path to uploaded audio files & WAV recordings |
BRUHI_AUDIO_API | http://localhost:7700 | bruhi-audio Rust engine control daemon API URL |
BRUHI_AUDIO_SOCKET_DIR | /tmp/bruhi-audio | Unix sockets shared between FastAPI and Rust engine |
BRUHI_RP_ID | localhost | WebAuthn / Passkeys Relying Party ID (domain name) |
BRUHI_RP_NAME | brūhi Cloud | WebAuthn / Passkeys application display name |
BRUHI_ADMIN_EMAIL | — | Optional initial admin/owner email for pre-seeding |
BRUHI_ADMIN_PASSWORD | — | Optional initial admin/owner password for pre-seeding |
CORS_ORIGINS | '' | Space-separated allowed CORS origins |
API_TOKEN | — | Bearer authorization token for administrative API calls |
S3_ENDPOINT_URL | — | S3-compatible storage endpoint URL (optional) |
S3_ACCESS_KEY | — | S3 access key |
S3_SECRET_KEY | — | S3 secret key |
S3_BUCKET | — | S3 bucket name |
S3_REGION | us-east-1 | S3 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:Volumes
Section titled “Volumes”| Volume | Mount path | Contents |
|---|---|---|
bruhi_audio | /app/audio_files | Uploaded audio files and WAV recordings |
bruhi_playlists | /tmp/liquidsoap-playlists | Station playout queues and temporary files |
bruhi_db | /app/data | Consolidated SQLite database (bruhi.db) |
bruhi_audio_sockets | /tmp/bruhi-audio | shared Unix IPC sockets between Python and Rust |
caddy_data | /data | TLS certificates provisioned by Caddy |
Auto-HTTPS with Bundled Caddy
Section titled “Auto-HTTPS with Bundled 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.
- Set
COMPOSE_PROFILES=bundled-icecast,proxyin your.envfile. - Set
DOMAIN=radio.yourdomain.comin your.envfile. - Ensure DNS records for
radio.yourdomain.compoint to your server’s IP address. - Run
docker compose up -d.
Nginx Reverse Proxy (Alternative)
Section titled “Nginx Reverse Proxy (Alternative)”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).
Production Checklist
Section titled “Production Checklist”Before exposing brūhi Cloud to the internet:
- Enable HTTPS via bundled Caddy (
COMPOSE_PROFILES=bundled-icecast,proxy) or Nginx - Set
BRUHI_RP_IDandBRUHI_URLto match your domain name - Set
ICECAST_SOURCE_PASSWORDandICECAST_ADMIN_PASSWORDto strong secrets - Set
CORS_ORIGINSto your domain name (avoid*) - Configure firewall rules: allow ports 80, 443 (HTTPS), 8010 (Icecast), 8100+ (harbors)
- Schedule regular volume backups for
bruhi_dbandbruhi_audio - Set
LOG_LEVEL=warningfor production
Updating
Section titled “Updating”docker compose -f docker-compose.prod.yml pulldocker compose -f docker-compose.prod.yml up -dbrūhi Cloud follows semantic versioning. Check the release notes before upgrading minor or major versions.
Verified Platforms
Section titled “Verified Platforms”| Provider | Service | Status |
|---|---|---|
| AWS | EC2, ECS, EKS | ✅ Verified |
| Google Cloud | GCE, GKE | ✅ Verified |
| DigitalOcean | Droplets, K8s | ✅ Verified |
| Hetzner | Cloud, Dedicated | ✅ Community tested |