Deploying Castopod
Castopod is a free and open-source podcast hosting platform designed for podcasters who want to engage and interact with their audience while maintaining complete control over their content. Built on PHP with modern web technologies, Castopod provides everything you need to manage, distribute, and monetize your podcasts without relying on third-party platforms.
Introduction to Castopod
Castopod empowers podcasters with a self-hosted solution that puts your podcast and audience entirely in your hands. Whether you’re running a solo show, a podcast network, or managing multiple shows, Castopod provides a feature-rich environment to handle all aspects of podcast management and distribution.
The platform is built on the CodeIgniter 4 framework and leverages PHP to deliver a reliable, scalable podcasting infrastructure. Castopod enables you to reach listeners on major platforms like Apple Podcasts, Spotify, Google Podcasts, Deezer, and many others through a unified RSS feed management system.
With Castopod, you maintain ownership of your content, listener data, and analytics while engaging with your audience through the fediverse—a decentralized social network that allows your podcast to be shared, liked, and commented on without intermediaries.
Key Features
Castopod is packed with powerful features for podcast creators:
- Decentralized Audience Engagement: Interact with listeners through ActivityPub integration on the fediverse without relying on centralized social media platforms
- Multi-Podcast Support: Manage multiple podcasts from a single Castopod instance
- Comprehensive Analytics: Built-in analytics following IABv2 guidelines with complete anonymization and GDPR compliance
- Broad Distribution: Automatically submit your podcast to Apple Podcasts, Spotify, Google Podcasts, Deezer, Podcast Addict, and other major platforms
- Monetization Options: Support multiple revenue streams including premium subscriptions, micropayments, tips, and ads
- Podcasting 2.0 Support: Full implementation of Podcasting 2.0 features with support for chapters, transcripts, persons, and locations
- Video Clip Creation: Generate and share video clips and soundbites from episodes across social media
- Episode Management: Full editorial control with scheduling, publishing, and episode metadata
- Subscription Management: Premium content delivery and subscriber management
- Social Media Tools: Built-in tools for promoting episodes across all social platforms
- Theme Customization: Customize your podcast’s appearance and branding
- Multi-Language Support: Support for multiple languages to reach global audiences
- RSS Feed Management: Complete RSS feed control with validation and optimization
- Listener Analytics: Track listener demographics, engagement metrics, and download patterns
- Email Notifications: Send notifications to listeners about new episodes
- Podcast Network Support: Manage podcast networks with multiple creators
- API Access: Full RESTful API for custom integrations and automation
- Backup and Restore: Built-in backup functionality for your podcast data
- Performance Optimization: Caching and optimization for fast podcast delivery
- Security First: AGPL v3 open-source license with transparent security practices
Prerequisites
Before deploying Castopod on Klutch.sh, ensure you have the following:
- A Klutch.sh account and dashboard access at klutch.sh/app
- A GitHub repository (your fork or copy of Castopod source code)
- Basic knowledge of Docker and containerization
- A custom domain for your Castopod instance (recommended)
- Understanding of environment variables and configuration management
- Familiarity with PHP applications and their deployment requirements
Important Considerations
Deployment Steps
Follow these steps to deploy Castopod on Klutch.sh:
Create a Dockerfile
Create a
Dockerfilein the root of your repository. Castopod is a PHP application built with CodeIgniter 4, so we’ll use a PHP-FPM base image combined with a web server:FROM php:8.4-fpm-alpineWORKDIR /app# Install system dependenciesRUN apk add --no-cache \gcc \musl-dev \autoconf \make \icu-dev \postgresql-dev \mysql-client \nginx# Install PHP extensionsRUN docker-php-ext-configure intl && \docker-php-ext-install -j$(nproc) \intl \pdo \pdo_mysql \pdo_pgsql \mbstring \curl \json \bcmath# Install ComposerCOPY --from=composer:latest /usr/bin/composer /usr/bin/composer# Copy application filesCOPY . /app# Install PHP dependenciesRUN composer install --no-dev --optimize-autoloader# Create writable directoriesRUN mkdir -p /app/writable && \chmod -R 755 /app/writable# Copy nginx configurationCOPY docker/nginx.conf /etc/nginx/nginx.conf# Expose portEXPOSE 8080# Start PHP-FPM and nginxCMD ["sh", "-c", "php-fpm -D && nginx -g 'daemon off;'"]Configure Environment File
Create a
.env.examplefile with Castopod’s required configuration variables:# ApplicationCI_ENVIRONMENT=productionAPP_BASEURL=https://example-app.klutch.sh# Databasedatabase.default.hostname=db.example.comdatabase.default.database=castopoddatabase.default.username=castopod_userdatabase.default.password=secure_password_heredatabase.default.DBDriver=MySQLi# Cachecache.default=rediscache.redis.host=redis.example.comcache.redis.port=6379# Emailemail.fromEmail=noreply@example-app.klutch.shemail.fromName=Castopodemail.protocol=SMTPemail.SMTPHost=smtp.sendgrid.netemail.SMTPUser=apikeyemail.SMTPPass=your_sendgrid_api_keyemail.SMTPPort=587# Securityencryption.key=encryption.driver=OpenSSL# Podcasting 2.0analytics.salt=your_analytics_salt# Fediverseactivitypub.username=castopodactivitypub.displayName=CastopodSet Up Nginx Configuration
Create
docker/nginx.conffor serving Castopod:user www-data;worker_processes auto;pid /var/run/nginx.pid;events {worker_connections 1024;}http {include /etc/nginx/mime.types;default_type application/octet-stream;access_log /var/log/nginx/access.log;error_log /var/log/nginx/error.log;sendfile on;tcp_nopush on;tcp_nodelay on;keepalive_timeout 65;types_hash_max_size 2048;client_max_body_size 512M;gzip on;gzip_vary on;gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss;upstream php {server 127.0.0.1:9000;}server {listen 8080;server_name _;root /app/public;index index.php;# Security headersadd_header X-Frame-Options "SAMEORIGIN" always;add_header X-Content-Type-Options "nosniff" always;add_header X-XSS-Protection "1; mode=block" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;location / {try_files $uri $uri/ /index.php?$query_string;}location ~ \.php$ {fastcgi_pass php;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;include fastcgi_params;fastcgi_buffering off;fastcgi_request_buffering off;}location ~ /\. {deny all;}}}Create an Entrypoint Script
Create
docker/entrypoint.shto handle initialization:#!/bin/bashset -eecho "Starting Castopod initialization..."# Copy env file if it doesn't existif [ ! -f /app/.env ]; thencp /app/.env.example /app/.envecho "Environment file created from template"fi# Run database migrationsphp spark migrate# Clear cachesphp spark cache:clear# Start the servicesecho "Starting PHP-FPM and Nginx..."php-fpm -Dexec nginx -g 'daemon off;'Make it executable:
Terminal window chmod +x docker/entrypoint.shCreate .gitignore
Ensure sensitive files aren’t committed to GitHub by creating
.gitignore:.env.env.localwritable/vendor/node_modules/.DS_Store*.logcache/Push to GitHub
Commit and push your Castopod configuration to your GitHub repository:
Terminal window git add Dockerfile docker/ .env.example .gitignoregit commit -m "Add Klutch.sh deployment configuration"git push origin mainDeploy on Klutch.sh
- Log in to your Klutch.sh dashboard at klutch.sh/app
- Click “Create New App” and select your GitHub repository containing Castopod
- Klutch.sh will automatically detect the Dockerfile in your repository
- Configure your deployment:
- Set your custom domain (e.g.,
castopod.example.com) - Configure HTTP traffic settings
- Set your custom domain (e.g.,
- Click “Deploy” to start the deployment process
- Monitor the deployment logs to ensure successful startup
Configure Persistent Storage
After your deployment is complete, attach persistent storage for Castopod’s data:
- From your app’s dashboard, navigate to the “Storage” or “Volumes” section
- Click “Add Volume”
- Set the mount path to
/app/writable(where Castopod stores uploads, cache, and logs) - Set the volume size to at least 100GB (adjust based on your podcast storage needs)
- Confirm and wait for the volume to be attached
- Your Castopod instance will automatically use the persistent volume for storage
Initial Setup and Configuration
After your Castopod deployment is live, complete the initial setup:
Accessing the Installation Wizard
- Navigate to your Castopod instance URL (e.g.,
https://castopod.example.com) - You’ll be presented with the installation wizard
- Enter your site information:
- Podcast Platform Name (e.g., “My Podcast Network”)
- Administrator Email
- Administrator Password
- Configure database connection (should already be set via environment variables)
- Set up email/SMTP settings for notifications
Creating Your First Podcast
- Log in to the admin dashboard with your administrator credentials
- Click “Create New Podcast”
- Enter podcast details:
- Podcast Title
- Podcast Description
- Podcast Category
- Podcast Language
- Upload podcast artwork (at least 1400x1400 pixels recommended)
- Configure monetization options if desired
- Submit your podcast for distribution
Configuring Distribution
- In the Podcast settings, navigate to “Distribution”
- Select the platforms where you want your podcast to appear (Apple Podcasts, Spotify, etc.)
- Castopod will generate submission URLs for each platform
- Submit your podcast through each platform’s submission process
Environment Variables
Basic Configuration
These are the essential environment variables needed for Castopod to function:
# Application EnvironmentCI_ENVIRONMENT=productionAPP_BASEURL=https://your-domain.klutch.sh
# Database Configurationdatabase.default.hostname=your-database-hostdatabase.default.database=castopoddatabase.default.username=castopoddatabase.default.password=your-secure-passworddatabase.default.DBDriver=MySQLi
# Email Configurationemail.fromEmail=noreply@your-domain.klutch.shemail.fromName=Your Podcast Networkemail.protocol=SMTPemail.SMTPHost=smtp.sendgrid.netemail.SMTPUser=apikeyemail.SMTPPass=your-sendgrid-api-keyemail.SMTPPort=587
# Cache Configurationcache.default=rediscache.redis.host=your-redis-hostcache.redis.port=6379
# Encryptionencryption.key=generate-a-secure-key-hereencryption.driver=OpenSSLProduction Optimization
For production deployments, add these environment variables for enhanced performance and security:
# Performancecache.redis.password=your-redis-passwordcache.handler=redis
# Securityapp.CSRFProtection=trueapp.CSRFTokenRandomize=truesecurity.tokenRandomize=true
# Analyticsanalytics.salt=generate-random-salt-hereanalytics.enabled=true
# Logginglog.threshold=3
# File Upload Limitsfile.sizeLimit=2048000
# Fediverse Configurationactivitypub.username=podcastnetworkactivitypub.displayName=Your Podcast Networkactivitypub.summary=Your podcast network descriptionCode Examples
PHP - Podcast Management API
Here’s a PHP example for managing podcasts programmatically:
<?php
namespace App\Controllers;
use App\Models\PodcastModel;use App\Models\EpisodeModel;
class PodcastManager extends BaseController{ public function createPodcast() { $podcastModel = new PodcastModel();
$podcastData = [ 'title' => 'My Awesome Podcast', 'description' => 'Discussing topics that matter', 'category' => 'Technology', 'language' => 'en', 'email' => 'contact@podcast.local', 'artwork' => 'path/to/artwork.jpg', ];
try { $podcastId = $podcastModel->insert($podcastData); return $this->response->setJSON(['success' => true, 'podcast_id' => $podcastId]); } catch (\Exception $e) { return $this->response->setJSON(['error' => $e->getMessage()], 500); } }
public function publishEpisode($podcastId) { $episodeModel = new EpisodeModel();
$episodeData = [ 'podcast_id' => $podcastId, 'title' => 'Episode 1: Getting Started', 'description' => 'In this episode, we explore podcast fundamentals', 'audio_url' => 'https://example.com/episode1.mp3', 'duration' => 1800, 'published_at' => date('Y-m-d H:i:s'), ];
try { $episodeId = $episodeModel->insert($episodeData); return $this->response->setJSON(['success' => true, 'episode_id' => $episodeId]); } catch (\Exception $e) { return $this->response->setJSON(['error' => $e->getMessage()], 500); } }
public function getPodcastStats($podcastId) { $podcastModel = new PodcastModel(); $podcast = $podcastModel->find($podcastId);
return $this->response->setJSON([ 'title' => $podcast['title'], 'total_episodes' => $podcast['episodes_count'], 'total_downloads' => $podcast['downloads_total'], 'subscriber_count' => $podcast['subscriber_count'], 'average_episode_length' => $podcast['average_episode_duration'], ]); }}Bash - Podcast Backup Script
Automate regular backups of your Castopod instance:
#!/bin/bash
BACKUP_DIR="/backups/castopod"TIMESTAMP=$(date +"%Y%m%d_%H%M%S")DATABASE_HOST="your-database-host"DATABASE_NAME="castopod"DATABASE_USER="castopod"
# Create backup directorymkdir -p "$BACKUP_DIR"
# Backup databaseecho "Backing up Castopod database..."mysqldump \ -h "$DATABASE_HOST" \ -u "$DATABASE_USER" \ -p"$DATABASE_PASSWORD" \ "$DATABASE_NAME" > "$BACKUP_DIR/database_$TIMESTAMP.sql"
# Compress database backupgzip "$BACKUP_DIR/database_$TIMESTAMP.sql"
# Backup podcast filesecho "Backing up podcast files..."tar -czf "$BACKUP_DIR/podcasts_$TIMESTAMP.tar.gz" \ /app/writable/uploads/podcasts/
# Backup configurationecho "Backing up configuration..."tar -czf "$BACKUP_DIR/config_$TIMESTAMP.tar.gz" \ /app/.env \ /app/app/Config/
# Clean up old backups (keep last 30 days)find "$BACKUP_DIR" -type f -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR/backup_$TIMESTAMP/"Shell - Castopod Health Check
Monitor your Castopod instance with a health check script:
#!/bin/bash
CASTOPOD_URL="https://castopod.example.com"ALERT_EMAIL="admin@example.com"
# Check if Castopod is respondingcheck_health() { HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$CASTOPOD_URL/health")
if [ "$HTTP_CODE" != "200" ]; then echo "❌ Castopod is DOWN (HTTP $HTTP_CODE)" send_alert "Castopod Health Check Failed: HTTP $HTTP_CODE" return 1 else echo "✅ Castopod is UP" return 0 fi}
# Check database connectivitycheck_database() { # This would depend on your database setup echo "✅ Database connectivity check"}
# Check disk spacecheck_disk_space() { DISK_USAGE=$(df /app/writable | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 90 ]; then echo "⚠️ Disk usage is high: ${DISK_USAGE}%" send_alert "Castopod disk usage is at ${DISK_USAGE}%" else echo "✅ Disk usage: ${DISK_USAGE}%" fi}
# Send alert emailsend_alert() { MESSAGE="$1" echo "$MESSAGE" | mail -s "Castopod Alert" "$ALERT_EMAIL"}
# Run all checksecho "Running Castopod health checks..."check_healthcheck_databasecheck_disk_spaceecho "Health check completed"Best Practices
When deploying and managing Castopod on Klutch.sh, follow these best practices:
- Regular Backups: Implement automated daily backups of your database and podcast files to prevent data loss
- Update Regularly: Keep Castopod updated to the latest version for security patches and new features
- Monitor Storage: Monitor your persistent volume usage and scale up as your podcast library grows
- Use Strong Passwords: Ensure all administrative accounts and database credentials are strong and unique
- Enable HTTPS: Always use HTTPS for your Castopod instance to protect listener data
- Configure Email: Set up reliable SMTP for transactional emails to your audience
- Optimize Images: Compress podcast artwork before uploading to reduce storage requirements
- Cache Strategy: Implement Redis caching for improved performance under heavy load
- Database Optimization: Regularly optimize your database and remove old analytics data you no longer need
- Security Headers: Ensure security headers are configured in your Nginx reverse proxy
- Rate Limiting: Implement rate limiting on your API endpoints to prevent abuse
- Log Monitoring: Monitor application and access logs for suspicious activity
Troubleshooting
Common Issues and Solutions
Issue: White screen after deployment
- Check the application logs: The logs should be in
/app/writable/logs/ - Verify your
.envfile is properly configured with database credentials - Ensure all required PHP extensions are installed in your Docker image
- Clear the cache by running
php spark cache:clear
Issue: Database connection errors
- Verify your database host, username, and password in the
.envfile - Check that your database service is running and accessible
- Ensure the database user has proper privileges
- Verify network connectivity between your Castopod container and database
Issue: Email notifications not being sent
- Verify SMTP credentials in the
.envfile - Check that your SMTP server allows outgoing connections on the specified port
- Review email logs in
/app/writable/logs/for error messages - Test SMTP settings using a simple PHP mail test script
Issue: Persistent storage not accessible
- Confirm that the volume is properly attached in the Klutch.sh dashboard
- Verify that the mount path
/app/writableexists in your container - Check file permissions on the mounted volume
- Restart the deployment to ensure the volume mounts correctly
Issue: High memory usage
- Optimize your database queries
- Increase PHP memory limits in your PHP configuration
- Implement caching more aggressively
- Consider scaling up your Klutch.sh deployment
Issue: Slow podcast uploads
- Check your network connection and bandwidth
- Optimize large audio files before uploading
- Increase the
client_max_body_sizein Nginx configuration - Use a CDN for podcast distribution
Update Instructions
To update Castopod to the latest version:
-
Pull the Latest Changes
Terminal window git fetch origingit pull origin main -
Update Dependencies
Terminal window composer updatenpm installnpm run build -
Run Database Migrations After redeploying on Klutch.sh, SSH into your container and run:
Terminal window php spark migrate -
Clear Caches
Terminal window php spark cache:clear -
Verify the Update Check the Castopod dashboard to confirm the new version is running
-
Rollback if Needed If issues occur, revert to the previous version in your Git repository and redeploy
Use Cases
Independent Podcasters
Castopod is ideal for independent podcast creators who want complete control over their content and audience. Host your podcast independently without relying on third-party hosting services, maintain ownership of your listener data, and use built-in analytics to understand your audience better.
Podcast Networks
Manage multiple podcasts from different creators within a single Castopod instance. Support a network of podcasters with shared infrastructure while maintaining individual podcast branding and monetization options. Offer premium content to subscribers across multiple shows.
Organizations and Businesses
Use Castopod to launch internal podcasts for employee communications or external shows for brand building and audience engagement. Leverage the fediverse integration to reach broader audiences and participate in the decentralized podcast ecosystem.
Educational Institutions
Create podcast channels for educational content, lectures, and academic discussions. Distribute educational podcasts to students and the broader community while maintaining control over course material and educational content.
Podcast Networks with Monetization
Implement various monetization strategies including premium subscriptions, micropayments, and advertisements. Use Castopod’s built-in monetization tools to generate revenue from your podcast content while maintaining audience trust through transparent, open-source technology.
Additional Resources
- Official Castopod Documentation
- Castopod GitHub Repository
- CodeIgniter 4 Framework Guide
- Podcasting 2.0 Standards
- ActivityPub Protocol Reference
- Castopod Discord Community
- Nginx Configuration Guide
- PHP-FPM Configuration
Conclusion
Deploying Castopod on Klutch.sh gives you a powerful, self-hosted podcast hosting platform with complete control over your content and audience. By following this guide, you’ve set up a production-ready Castopod instance that can scale with your podcast network.
Castopod’s open-source nature, combined with its comprehensive feature set for podcast management, distribution, and monetization, makes it an excellent choice for podcasters who want independence and control. The integration with the fediverse opens new possibilities for audience engagement without relying on centralized social media platforms.
Remember to maintain regular backups, keep your instance updated, monitor your storage usage, and follow the best practices outlined in this guide. With Castopod running on Klutch.sh, you have a reliable, scalable platform ready to grow with your podcast ambitions.
For additional support and to connect with the Castopod community, visit the official documentation or join the Castopod Discord server.
Author: Davaughn White