Deployment
Short guides for installing and running HarborFM. For full variable reference and advanced options, see the main README and the Terraform docs linked below.
Quick reference: The main README has a "Quick Start" (Docker one-liner), "Deploy with Terraform" (AWS and Vultr), "Docker Compose Quick Start (Curl)" (install.sh), and "Running without Docker" (PM2, manual build). Use this wiki for an overview; use the README and infrastructure/terraform/QUICKSTART.md for step-by-step and all variables.
Docker and Compose
Single Container (Docker)
The app needs writable data and optionally secrets directories. You can pass secrets via environment variables instead of mounting /secrets.
HARBORFM_SECRETS_KEY=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 32)
docker run --name harborfm -p 3001:3001 \
-v harborfm-data:/data \
-e HARBORFM_SECRETS_KEY="$HARBORFM_SECRETS_KEY" \
-e JWT_SECRET="$JWT_SECRET" \
ghcr.io/loganrickert/harborfm:latest
Open http://localhost:3001 (or your host and port). Get the one-time setup URL from docker logs harborfm. Use nginx or Caddy on the host for HTTPS. If you use plain http, set COOKIE_SECURE=false.
Build from source: From repo root, docker build -t harborfm . (or pnpm run docker:build).
Docker Compose (Full Stack)
The repo's docker-compose can run the app with Caddy (default) or nginx, Let's Encrypt, optional WebRTC, Whisper, and Fail2Ban. Quick start without cloning the repo:
curl -fsSL https://raw.githubusercontent.com/loganrickert/harborfm/main/install.sh | bash
Follow prompts for domain and reverse proxy. install.sh defaults to Caddy; nginx is optional (with Let's Encrypt or a self-signed cert). For WebRTC, enable it during install or use the webrtc compose profile with your proxy profile, for example:
docker compose --profile caddy --profile webrtc up -d
# or, if you chose nginx:
docker compose --profile nginx --profile webrtc up -d
Required .env for WebRTC: WEBRTC_ENABLED, WEBRTC_SERVICE_URL, WEBRTC_PUBLIC_WS_URL, WEBRTC_SERVICE_SECRET, RECORDING_CALLBACK_SECRET, and behind NAT, MEDIASOUP_ANNOUNCED_IP. After Hostname is set in Settings, WebRTC Settings values take precedence over WEBRTC_* env vars (env still seeds empty settings). See main README "WebRTC (group calls)."
Updating an Install.sh Deploy
install.sh also installs update.sh in the install directory. Run it from that directory to refresh compose configs and images. Configs and images are fetched while the stack stays up; downtime is only the brief recreate step. When WEBRTC_ENABLED=1, update.sh passes the webrtc profile to down / pull / up so the webrtc image is pulled and the container is recreated. Always run docker compose from the install directory so paths in .env (e.g. INSTALL_DIR) resolve correctly.
Certificate renewal for nginx+certbot is typically done via cron (e.g. docker compose run --rm --entrypoint certbot certbot renew). Caddy renews certificates itself.
Stripe Webhooks (Payments)
If show owners use Stripe payments, each credential pack needs a webhook URL pointed at your public host. See that page and the main README "Stripe payments" section. Locally you can use pnpm stripe:listen.
See Also
- Terraform (AWS / Vultr)
- Environment variables
- Stripe payments
- Main README - Docker, Docker Compose Quick Start, WebRTC
Terraform (AWS / Vultr)
Terraform can provision a VM (AWS EC2 or Vultr) that runs HarborFM via user-data (PM2, nginx, optional WebRTC and Let's Encrypt).
Prerequisites
- Terraform installed. See infrastructure/terraform/QUICKSTART.md for macOS, Debian, and CentOS.
- AWS:
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY(oraws configure). - Vultr:
VULTR_API_KEYin.env(copy frominfrastructure/terraform/.env.example).
AWS (EC2)
cd infrastructure/terraform/awscp terraform.tfvars.example terraform.tfvars- Edit
terraform.tfvars:deploy_type,ami_id(Debian 12 for your region),domain,admin_email,admin_password, etc. ./run.sh initthen./run.sh apply- Use the url output to open the app. If you set
admin_emailandadmin_password, the admin is created on first boot.
AMI lookup (Debian 12): Owner 136693071363. Example for us-east-2:
aws ec2 describe-images --region us-east-2 --owners 136693071363 \
--filters "Name=name,Values=debian-12-*" "Name=state,Values=available" \
--query "sort_by(Images, &CreationDate)[-1].ImageId" --output text
Vultr
cd infrastructure/terraform/vultrcp terraform.tfvars.example terraform.tfvars- Edit
terraform.tfvars:deploy_type,region,os_id,plan,domain, etc. ./run.sh initthen./run.sh apply
OS IDs: List with curl -s -H "Authorization: Bearer $VULTR_API_KEY" https://api.vultr.com/v2/os | jq '.os[] | {id, name}'. Common: Debian 12 2136, Ubuntu 22 1743, etc.
Full Variable Reference and Options
- infrastructure/terraform/README.md - Variables, optional persistent data volume, multi-environment (dev/prod).
- infrastructure/terraform/QUICKSTART.md - Step-by-step Terraform install and first apply.
See Also
- Docker and Compose
- Environment variables
- Main README - Deploy with Terraform
If you want to manage multiple instances from a web UI instead of running Terraform manually, use the Instance manager.
Environment Variables
HarborFM is configured mainly via environment variables. Below is a short overview; the full table is in the main README ("Docker environment variables").
Essential for Production
- JWT_SECRET - Required for signing sessions; use a long random string (e.g.
openssl rand -base64 32). - HARBORFM_SECRETS_KEY - Optional; used to encrypt export credentials and Stripe credential packs (base64/base64url). Set this if you use Podcast Delivery or Stripe payments.
- DATA_DIR - Where the DB, uploads, processed audio, RSS, artwork, and library live (Docker often
/data). - SECRETS_DIR - Where JWT and secrets key files are stored (Docker often
/secrets). Can be omitted if you pass secrets via env.
Server and Paths
- PORT (default
3001), HOST (default0.0.0.0). - PUBLIC_DIR - Static web app (e.g.
./public). Built app is usually copied here. - NODE_ENV - Set to
productionin production; affects CORS and cookie Secure default.
Cookies and HTTPS
- COOKIE_SECURE - Set to
truewhen using HTTPS. For http only, setCOOKIE_SECURE=false.
WebRTC (Group Calls)
- WEBRTC_ENABLED -
1ortrueto enable. - WEBRTC_SERVICE_URL - Internal URL to webrtc service (e.g.
http://webrtc:3002). - WEBRTC_PUBLIC_WS_URL - Public WebSocket URL for clients (e.g.
wss://example.com/webrtc-ws). - WEBRTC_SERVICE_SECRET, RECORDING_CALLBACK_SECRET - For app ↔ webrtc auth.
- MEDIASOUP_ANNOUNCED_IP - Set to the server's public IP when behind NAT.
Upload Limits, Binaries, and More
The README table includes: upload size limits (episode, segment, library, artwork), FFMPEG_PATH, FFPROBE_PATH, AUDIOWAVEFORM_PATH, GeoIP, Whisper, LLM, email, captcha, rate limits, and many optional tuning vars. Use the README as the canonical reference.
See Also
- Docker and Compose
- Terraform (AWS / Vultr)
- Instance manager - Managing multiple instances and its own env vars
- Main README - Docker environment variables (full table)