Skip to content

Self-Hosting a Podcast with Castopod

Castopod is an open-source, self-hosted podcast host built on CodeIgniter 4 (PHP) with a MySQL/MariaDB backend. It replaces a hosted platform (Anchor, Buzzsprout, Podbean, etc.) with something you run yourself — full RSS control, no per-download fees, and built-in ActivityPub federation so episodes can post directly to Mastodon and the wider fediverse.

This article covers the operational shape of running it, not a step-by-step install — Castopod's own docs cover installation. What follows is what actually matters once it's live: where things live on disk, what's fragile, and where the fleet's own gotchas are documented.


Deployment shape (as run in this fleet)

  • Stack: LAMP (Apache + PHP + MySQL), not Docker — a deliberate choice for the one instance in this fleet, unlike most other self-hosted services here.
  • Install path: /var/www/html/castopod/
  • Web root: public/ under the install path (standard CodeIgniter layout)
  • Database: MySQL, one schema per instance (episodes, actors, activities, follows, etc. all live in Castopod's own tables — cp_episodes, cp_fediverse_posts, cp_fediverse_activities, cp_fediverse_follows, cp_fediverse_actors)
  • Admin UI: /cp-admin on the instance's own domain
  • Feed: /@<handle>/feed.xml — the handle is case-sensitive

Migrating onto it

Moving from a hosted platform is mostly a matter of RSS import — Castopod's admin can bulk-import an existing feed and it preserves the original episode <guid> values on import, which matters for podcast-app continuity (Apple Podcasts, Pocket Casts, etc. de-duplicate episodes by GUID; changing them on migration makes every episode look "new" to every subscriber). See a full worked migration in this vault at 30-Areas/MajorInfrastructure/Migrations/Anchor-to-Castopod-Migration.md for the specific mechanics (bulk RSS import window, feed continuity via <podcast:previousUrl>, DNS cutover order).

One thing that import path gets right that native uploads don't: see Castopod feed empty-GUID backfill — episodes created directly through the admin UI after migration can end up with an empty <guid>, which strict podcast clients treat as a single duplicate episode and silently drop.

Known gotchas, indexed

This fleet has hit several Castopod-specific issues worth knowing about before you hit them yourself:

Federation (ActivityPub)

Castopod broadcasts new episodes as ActivityPub Create activities to followers, via a per-minute scheduled task (php spark tasks:run, cron-driven). This is genuinely federated — a Castopod podcast actor can be followed from Mastodon, Pixelfed, or any other ActivityPub server, and posts appear in followers' home timelines the same as any other federated post. See the diagnostic article linked above for how delivery, follow state, and Mastodon's own notification rules interact — "I didn't see the post" has at least three distinct root causes and they're easy to conflate.