Skip to content

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:

  1. Create a Dockerfile

    Create a Dockerfile in 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-alpine
    WORKDIR /app
    # Install system dependencies
    RUN apk add --no-cache \
    gcc \
    musl-dev \
    autoconf \
    make \
    icu-dev \
    postgresql-dev \
    mysql-client \
    nginx
    # Install PHP extensions
    RUN docker-php-ext-configure intl && \
    docker-php-ext-install -j$(nproc) \
    intl \
    pdo \
    pdo_mysql \
    pdo_pgsql \
    mbstring \
    curl \
    json \
    bcmath
    # Install Composer
    COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
    # Copy application files
    COPY . /app
    # Install PHP dependencies
    RUN composer install --no-dev --optimize-autoloader
    # Create writable directories
    RUN mkdir -p /app/writable && \
    chmod -R 755 /app/writable
    # Copy nginx configuration
    COPY docker/nginx.conf /etc/nginx/nginx.conf
    # Expose port
    EXPOSE 8080
    # Start PHP-FPM and nginx
    CMD ["sh", "-c", "php-fpm -D && nginx -g 'daemon off;'"]
  2. Configure Environment File

    Create a .env.example file with Castopod’s required configuration variables:

    # Application
    CI_ENVIRONMENT=production
    APP_BASEURL=https://example-app.klutch.sh
    # Database
    database.default.hostname=db.example.com
    database.default.database=castopod
    database.default.username=castopod_user
    database.default.password=secure_password_here
    database.default.DBDriver=MySQLi
    # Cache
    cache.default=redis
    cache.redis.host=redis.example.com
    cache.redis.port=6379
    # Email
    email.fromEmail=noreply@example-app.klutch.sh
    email.fromName=Castopod
    email.protocol=SMTP
    email.SMTPHost=smtp.sendgrid.net
    email.SMTPUser=apikey
    email.SMTPPass=your_sendgrid_api_key
    email.SMTPPort=587
    # Security
    encryption.key=
    encryption.driver=OpenSSL
    # Podcasting 2.0
    analytics.salt=your_analytics_salt
    # Fediverse
    activitypub.username=castopod
    activitypub.displayName=Castopod
  3. Set Up Nginx Configuration

    Create docker/nginx.conf for 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 headers
    add_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;
    }
    }
    }
  4. Create an Entrypoint Script

    Create docker/entrypoint.sh to handle initialization:

    #!/bin/bash
    set -e
    echo "Starting Castopod initialization..."
    # Copy env file if it doesn't exist
    if [ ! -f /app/.env ]; then
    cp /app/.env.example /app/.env
    echo "Environment file created from template"
    fi
    # Run database migrations
    php spark migrate
    # Clear caches
    php spark cache:clear
    # Start the services
    echo "Starting PHP-FPM and Nginx..."
    php-fpm -D
    exec nginx -g 'daemon off;'

    Make it executable:

    Terminal window
    chmod +x docker/entrypoint.sh
  5. Create .gitignore

    Ensure sensitive files aren’t committed to GitHub by creating .gitignore:

    .env
    .env.local
    writable/
    vendor/
    node_modules/
    .DS_Store
    *.log
    cache/
  6. Push to GitHub

    Commit and push your Castopod configuration to your GitHub repository:

    Terminal window
    git add Dockerfile docker/ .env.example .gitignore
    git commit -m "Add Klutch.sh deployment configuration"
    git push origin main
  7. Deploy on Klutch.sh

    1. Log in to your Klutch.sh dashboard at klutch.sh/app
    2. Click “Create New App” and select your GitHub repository containing Castopod
    3. Klutch.sh will automatically detect the Dockerfile in your repository
    4. Configure your deployment:
      • Set your custom domain (e.g., castopod.example.com)
      • Configure HTTP traffic settings
    5. Click “Deploy” to start the deployment process
    6. Monitor the deployment logs to ensure successful startup
  8. Configure Persistent Storage

    After your deployment is complete, attach persistent storage for Castopod’s data:

    1. From your app’s dashboard, navigate to the “Storage” or “Volumes” section
    2. Click “Add Volume”
    3. Set the mount path to /app/writable (where Castopod stores uploads, cache, and logs)
    4. Set the volume size to at least 100GB (adjust based on your podcast storage needs)
    5. Confirm and wait for the volume to be attached
    6. 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

  1. Navigate to your Castopod instance URL (e.g., https://castopod.example.com)
  2. You’ll be presented with the installation wizard
  3. Enter your site information:
    • Podcast Platform Name (e.g., “My Podcast Network”)
    • Administrator Email
    • Administrator Password
  4. Configure database connection (should already be set via environment variables)
  5. Set up email/SMTP settings for notifications

Creating Your First Podcast

  1. Log in to the admin dashboard with your administrator credentials
  2. Click “Create New Podcast”
  3. Enter podcast details:
    • Podcast Title
    • Podcast Description
    • Podcast Category
    • Podcast Language
  4. Upload podcast artwork (at least 1400x1400 pixels recommended)
  5. Configure monetization options if desired
  6. Submit your podcast for distribution

Configuring Distribution

  1. In the Podcast settings, navigate to “Distribution”
  2. Select the platforms where you want your podcast to appear (Apple Podcasts, Spotify, etc.)
  3. Castopod will generate submission URLs for each platform
  4. 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 Environment
CI_ENVIRONMENT=production
APP_BASEURL=https://your-domain.klutch.sh
# Database Configuration
database.default.hostname=your-database-host
database.default.database=castopod
database.default.username=castopod
database.default.password=your-secure-password
database.default.DBDriver=MySQLi
# Email Configuration
email.fromEmail=noreply@your-domain.klutch.sh
email.fromName=Your Podcast Network
email.protocol=SMTP
email.SMTPHost=smtp.sendgrid.net
email.SMTPUser=apikey
email.SMTPPass=your-sendgrid-api-key
email.SMTPPort=587
# Cache Configuration
cache.default=redis
cache.redis.host=your-redis-host
cache.redis.port=6379
# Encryption
encryption.key=generate-a-secure-key-here
encryption.driver=OpenSSL

Production Optimization

For production deployments, add these environment variables for enhanced performance and security:

# Performance
cache.redis.password=your-redis-password
cache.handler=redis
# Security
app.CSRFProtection=true
app.CSRFTokenRandomize=true
security.tokenRandomize=true
# Analytics
analytics.salt=generate-random-salt-here
analytics.enabled=true
# Logging
log.threshold=3
# File Upload Limits
file.sizeLimit=2048000
# Fediverse Configuration
activitypub.username=podcastnetwork
activitypub.displayName=Your Podcast Network
activitypub.summary=Your podcast network description

Code 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 directory
mkdir -p "$BACKUP_DIR"
# Backup database
echo "Backing up Castopod database..."
mysqldump \
-h "$DATABASE_HOST" \
-u "$DATABASE_USER" \
-p"$DATABASE_PASSWORD" \
"$DATABASE_NAME" > "$BACKUP_DIR/database_$TIMESTAMP.sql"
# Compress database backup
gzip "$BACKUP_DIR/database_$TIMESTAMP.sql"
# Backup podcast files
echo "Backing up podcast files..."
tar -czf "$BACKUP_DIR/podcasts_$TIMESTAMP.tar.gz" \
/app/writable/uploads/podcasts/
# Backup configuration
echo "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 responding
check_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 connectivity
check_database() {
# This would depend on your database setup
echo "✅ Database connectivity check"
}
# Check disk space
check_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 email
send_alert() {
MESSAGE="$1"
echo "$MESSAGE" | mail -s "Castopod Alert" "$ALERT_EMAIL"
}
# Run all checks
echo "Running Castopod health checks..."
check_health
check_database
check_disk_space
echo "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 .env file 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 .env file
  • 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 .env file
  • 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/writable exists 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_size in Nginx configuration
  • Use a CDN for podcast distribution

Update Instructions

To update Castopod to the latest version:

  1. Pull the Latest Changes

    Terminal window
    git fetch origin
    git pull origin main
  2. Update Dependencies

    Terminal window
    composer update
    npm install
    npm run build
  3. Run Database Migrations After redeploying on Klutch.sh, SSH into your container and run:

    Terminal window
    php spark migrate
  4. Clear Caches

    Terminal window
    php spark cache:clear
  5. Verify the Update Check the Castopod dashboard to confirm the new version is running

  6. 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

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