SkillAgentSearch skills...

HarborFM

Open source podcast creation tool designed as a modern replacement for Anchor.fm. Build episodes from segments: record or upload clips, pull in intros and bumpers from a library, trim and reorder, then export a single audio file and RSS feed.

Install / Use

/learn @LoganRickert/HarborFM
About this skill

Quality Score

0/100

Category

Design

Supported Platforms

Universal

README

HarborFM

HarborFM

Open source podcast creation tool designed as a modern replacement for Anchor.fm. Build episodes from segments: record or upload clips, pull in intros and bumpers from a library, trim and reorder, then export a single audio file and RSS feed.

The app has PWA, so you can add it to your home screen and connect to your server.

License: MIT

Home Page: https://harborfm.com/

Source: https://github.com/LoganRickert/harborfm

Demo Site: https://app.harborfm.com/

Swagger API Docs: https://harborfm.com/server/

Overview on Noted.lol https://noted.lol/harborfm/

Discord https://discord.gg/hSmstBzAJV

Table of contents

Overview

HarborFM lets you assemble podcast episodes from building blocks. Create a show, add episodes, and for each episode add segments: recorded clips (uploaded per episode) or reusable assets from your library (intros, outros, bumpers). Trim, split, remove silence, and reorder. The app concatenates segments with ffmpeg and produces the final episode audio. Generate RSS feeds and deploy to S3-compatible storage (e.g. Cloudflare R2) so listeners can subscribe. Optional: transcripts via Whisper ASR, LLM helpers (Ollama or OpenAI) for copy suggestions, and public feed pages for discovery.

Quick Start

The app expects two writable directories: /data (SQLite DB, uploads, processed audio, RSS, artwork, library) and /secrets (JWT and encryption keys). You do not need to mount /secrets if you pass the secrets in through environment variables.

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

Use nginx+letsencrypt to provide a secure connection.

If you are using http, you need to set COOKIE_SECURE=false as an environment variable.

Deploy with Terraform

Use Terraform to provision a VM (AWS EC2 or Vultr) that runs HarborFM via user-data (PM2 + nginx, with optional WebRTC and Let's Encrypt).

AWS (EC2)

  1. Install Terraform – see infrastructure/terraform/QUICKSTART.md (macOS, Debian, CentOS).

  2. Configure AWS – set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (or use aws configure).

  3. Apply from the AWS Terraform directory:

    cd infrastructure/terraform/aws
    cp 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 init
    ./run.sh apply
    
  4. Use the url output to open the app; if you set admin_email and admin_password, the admin is created on first boot.

Vultr

  1. Install Terraform – see infrastructure/terraform/QUICKSTART.md.

  2. Set VULTR_API_KEY in .env (copy from infrastructure/terraform/.env.example).

  3. Apply from the Vultr directory:

    cd infrastructure/terraform/vultr
    cp terraform.tfvars.example terraform.tfvars
    # Edit terraform.tfvars: deploy_type, region, os_id, plan, domain, etc.
    ./run.sh init
    ./run.sh apply
    

Getting Vultr OS IDs

List available OS images:

curl -s -H "Authorization: Bearer $VULTR_API_KEY" https://api.vultr.com/v2/os | jq '.os[] | {id, name}'

Common mappings: Debian 11 477, Debian 12 2136, Debian 13 2625; Ubuntu 22 1743, Ubuntu 24 2285, Ubuntu 25 2657; CentOS 9 542, CentOS 10 2467. Vultr derives the os variable from os_id via infrastructure/terraform/vultr/scripts/os-from-id.sh.

Getting AWS AMI IDs

Look up a Debian 12 AMI for your region (owner 136693071363 is Debian):

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

Change us-east-2 to your region. The Terraform os variable (e.g. debian-12) must match the image.

Full variable reference, optional persistent data volume (survives destroy+apply), and multi-environment (dev/prod) details: infrastructure/terraform/README.md.

WebRTC (group calls)

Group calls use a separate webrtc-service (mediasoup). The main app talks to it over HTTP; browsers connect via WebSocket.

Enabling:

  • Set WEBRTC_ENABLED=1 (or true) on the main app.
  • Configure WEBRTC_SERVICE_URL (internal, e.g. http://webrtc:3002) and WEBRTC_PUBLIC_WS_URL (public, e.g. wss://example.com/webrtc-ws). Nginx/Caddy proxy /webrtc-ws/ to the webrtc service.

Docker Compose: WebRTC runs under profile webrtc. Start with:

docker compose --profile nginx --profile webrtc up -d

(or caddy instead of nginx). Required .env: WEBRTC_ENABLED, WEBRTC_SERVICE_URL, WEBRTC_PUBLIC_WS_URL, WEBRTC_SERVICE_SECRET, RECORDING_CALLBACK_SECRET, and MEDIASOUP_ANNOUNCED_IP (when behind NAT).

PM2 / bare metal: Use ecosystem.config.cjs; it starts both harborfm and webrtc. Ensure the firewall allows UDP RTC_MIN_PORTRTC_MAX_PORT (webrtc-service default 40000–40200; Docker uses 41000–41100).

Debugging:

  • No "Record" or group-call UI: check WEBRTC_ENABLED and WEBRTC_SERVICE_URL / WEBRTC_PUBLIC_WS_URL.
  • Can't connect / no audio: verify firewall UDP ports; behind NAT, set MEDIASOUP_ANNOUNCED_IP to the server's public IP.
  • Logs: docker compose logs webrtc or pm2 logs webrtc.

Docker Compose Quick Start (Curl)

To run the full stack on a fresh machine (app, nginx, Let's Encrypt, Whisper, Fail2Ban) without cloning the repo:

curl -fsSL https://raw.githubusercontent.com/loganrickert/harborfm/main/install.sh | bash

The script downloads the compose file and configs, prompts for domain and cert email (unless non-interactive), then starts the stack. When not using Let's Encrypt, you can optionally use a self-signed certificate for HTTPS (browsers will show a warning). This script assumes you have docker and docker compose installed.

To auto-renew Let's Encrypt certificates, add a cron job (run crontab -e and add a line like the following, adjusting the path to your install directory):

0 3 * * * cd /path/to/harborfm-docker && docker compose run --rm --entrypoint certbot certbot renew

If you use the install.sh script, an update.sh script will also be added to the install directory. Run this script to pull the latest docker-compose files and renew the nginx certificate. Always run docker compose (and docker compose restart) from the install directory so volume paths such as nginx sites-enabled use the correct path from .env.

Adding additional domains (nginx)

If you use nginx and want to serve the same HarborFM app on extra domains or subdomains (e.g. demo.harborfm.com, podcast.example.com), use the included script from your install directory:

./nginx-add-domain.sh <domain>
# Example:
./nginx-add-domain.sh demo.harborfm.com

Before running:

  • Your .env must have REVERSE_PROXY=nginx, CERTBOT_EMAIL set, and INSTALL_DIR set to the install directory’s absolute path.
  • DNS for the new domain must already point to this server (A/AAAA to the same host as your main domain).

The script will: add an nginx config for the domain under sites-enabled, reload nginx, run Let’s Encrypt (certbot) to obtain a certificate for that domain, then switch the config to HTTPS and reload again. Your primary domain (the one in DOMAIN in .env) is already served by the main nginx config-do not add it with this script or you’ll get duplicate server name warnings. Certificate renewal (e.g. cron with docker compose run --rm --entrypoint certbot certbot renew) renews all certs, including ones added this way.

Guide and Screenshots

HarborFM

When creating a new instance, you will need to navigate to the correct setup link. The link will be written to the console and is unique to every instance.

For example,

Open this URL to initialize the server (runs once):

  /setup?id=oFwK--nBt8YloIVABKA4nOmYy_Kbx7PS

HarborFM

The initial setup will create an admin account. You will need to provide the admin email, a password, and you can enable or disable account registration and public feeds from here.

After you've finished the setup, you can sign into your new account.

HarborFM

Once signed in, you will see the dashboard which has a list of podcast shows.

HarborFM

For each show, you can configure the information on the show page.

HarborFM

From there you can view and create episodes on the episodes page.

HarborFM

The app provides the ability to 'build'

View on GitHub
GitHub Stars31
CategoryDesign
Updated9d ago
Forks0

Languages

TypeScript

Security Score

90/100

Audited on Mar 31, 2026

No findings