Most podcast hosting platforms charge per-download, limit your episode storage, or take a cut if you monetize. If you have a cloud server, you can host your own podcast for the cost of the server — unlimited episodes, no per-download fees, your RSS feed lives on your domain, and you own every byte of your audience data.
Castopod is an open-source podcast hosting platform built specifically for self-hosting. It handles episode uploads, RSS feed generation, episode pages, a built-in player, podcast analytics, and even ActivityPub support so your podcast can be followed on Mastodon and other fediverse platforms.
I host my podcast on Tencent Cloud Lighthouse. The 2 GB RAM / 2 vCPU plan handles Castopod comfortably. Two Lighthouse features matter particularly for podcast hosting: CBS cloud disk expansion for audio file storage (a year of weekly 60-minute episodes at 128kbps is roughly 3 GB — small now, but it grows), and the bandwidth allowance for episode downloads. Lighthouse's fixed monthly bandwidth means listener download traffic doesn't generate variable per-GB charges.
- Key Takeaways
Running Castopod on your own server gives you:
podcast.yourdomain.com/feed.xml, not a third-party platform URL| Requirement | Details |
|---|---|
| Server | Ubuntu 22.04, 2 GB+ RAM |
| Domain | Required — podcast feed URL and episode pages |
| PHP | 8.1+ |
| MySQL | 8.0+ |
| Storage | Plan for episode file storage (60–100 MB per hour of audio) |
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
sudo apt install -y mysql-server
sudo systemctl enable mysql
sudo mysql_secure_installation
Create a database for Castopod:
sudo mysql -u root -p
CREATE DATABASE castopod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'castopod'@'localhost' IDENTIFIED BY 'your-secure-password';
GRANT ALL PRIVILEGES ON castopod.* TO 'castopod'@'localhost';
FLUSH PRIVILEGES;
EXIT;
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install -y php8.1-fpm php8.1-mysql php8.1-xml php8.1-mbstring php8.1-curl php8.1-zip php8.1-gd php8.1-intl php8.1-redis
Configure PHP for uploads:
sudo nano /etc/php/8.1/fpm/php.ini
Update these values:
upload_max_filesize = 512M
post_max_size = 512M
max_execution_time = 300
memory_limit = 512M
Restart PHP-FPM:
sudo systemctl restart php8.1-fpm
cd /var/www
sudo wget https://code.castopod.org/adaures/castopod/-/releases/permalink/latest/downloads/castopod-package.zip
sudo apt install -y unzip
sudo unzip castopod-package.zip -d castopod
sudo rm castopod-package.zip
sudo chown -R www-data:www-data /var/www/castopod
sudo find /var/www/castopod -type d -exec chmod 755 {} \;
sudo find /var/www/castopod -type f -exec chmod 644 {} \;
sudo cp /var/www/castopod/.env.example /var/www/castopod/.env
sudo nano /var/www/castopod/.env
Update the database settings:
app.baseURL="https://podcast.yourdomain.com"
database.default.hostname=localhost
database.default.database=castopod
database.default.username=castopod
database.default.password=your-secure-password
sudo nano /etc/nginx/sites-available/castopod
server {
listen 80;
server_name podcast.yourdomain.com;
root /var/www/castopod/public;
index index.php;
# Podcast feed and media files
client_max_body_size 512M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
}
# Cache static assets
location ~* \.(mp3|mp4|ogg|m4a|jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Deny .htaccess access
location ~ /\.ht {
deny all;
}
}
sudo ln -s /etc/nginx/sites-available/castopod /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d podcast.yourdomain.com
Navigate to https://podcast.yourdomain.com/cp-install in your browser.
The wizard will:
Fill in:
.env if not pre-filled)Click Install Castopod.
After setup, go to https://podcast.yourdomain.com/cp-admin.
Log in with your admin credentials.
In the admin panel:
Prepare your episode:
In Castopod:
Your podcast RSS feed is at:
https://podcast.yourdomain.com/@yourpodcast/feed.xml
This is the URL you submit to podcast directories.
Paste the feed URL into podcastpage.io/podcast-validator to check for errors.
Once your feed has at least one episode:
| Platform | Submission URL |
|---|---|
| Apple Podcasts | podcastsconnect.apple.com |
| Spotify | podcasters.spotify.com |
| Amazon Music | music.amazon.com/podcasts/submit |
| Google Podcasts | podcastsmanager.google.com |
| Pocket Casts | Automatically picks up from Apple Podcasts |
Most directories verify your feed within 24–72 hours. Apple Podcasts is the most important submission — many other apps pull from Apple's directory.
For each submission, you'll provide:
After publishing my first episode, the episode page loaded fine, but the audio player wasn't working — clicking play did nothing, and the browser console showed a CORS error.
The cause: my audio files were being served from the same server, but I hadn't set CORS headers in Nginx. Some podcast players and the Castopod web player use JavaScript to fetch audio, which triggers CORS checks.
The fix: Add CORS headers to the Nginx config for audio files:
location ~* \.(mp3|mp4|ogg|m4a)$ {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, HEAD, OPTIONS';
expires 1y;
add_header Cache-Control "public";
}
Reload Nginx:
sudo systemctl reload nginx
The player started working immediately. If you plan to embed the player on other websites, CORS headers are essential.
| Issue | Likely Cause | Fix |
|---|---|---|
| 500 error on install | PHP extension missing | Check php -m for required extensions |
| Upload fails | File size limit | Increase upload_max_filesize in php.ini |
| Audio player not working | CORS headers missing | Add Access-Control-Allow-Origin: * for audio files |
| RSS feed returns empty | No published episodes | Publish at least one episode |
| Apple Podcasts rejected | Feed validation error | Test feed at podcastpage.io/podcast-validator |
| Admin panel inaccessible | Wrong base URL | Update app.baseURL in .env |
| Database connection failed | Wrong credentials | Check .env database settings match MySQL user |
✅ What you built:
Total ongoing cost: the price of your cloud server, regardless of how many episodes you publish or how many people listen.
What video formats does Castopod podcast hosting support for streaming?
Modern media servers support most common formats. Direct Play (native format support by client) is preferred over transcoding. H.264 video in MKV or MP4 containers has the widest client support.
What's the difference between Direct Play and Transcoding?
Direct Play streams the original file to the client, requiring no server CPU. Transcoding converts the video in real-time to a compatible format, requiring significant server CPU.
How much bandwidth do I need for video streaming?
Blu-ray quality: 25–40 Mbps. 1080p: 8–25 Mbps. 720p: 3–8 Mbps. Lighthouse bandwidth packages support these rates. For multiple simultaneous streams, multiply accordingly.
How do I add media files to the server?
Upload via SCP/SFTP from your local machine, rsync from a NAS, or download directly on the server. The guide covers the recommended directory structure for automatic library scanning.
👉 Get started with Tencent Cloud Lighthouse
👉 View current pricing and launch promotions
👉 Explore all active deals and offers