You've successfully subscribed to Castopod Blog
Great! Next, complete checkout for full access to Castopod Blog
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
Success! Your billing info is updated.
Billing info update failed.

Introducing the Castopod Installation Script

Install Castopod with a single command. The official installation script handles all dependencies, SSL certificates, database configuration, and security hardening automatically.

Benjamin Bellamy
Benjamin Bellamy

Introducing the Castopod Installation Script

curl -fsSL https://castopod.org/install | sudo bash

That's it. One command. The script will guide you through the rest. Go make coffee. Come back to a fully configured Castopod installation.

castopod-install-01


Tested Platforms

The installation script has been tested on:

  • Ubuntu 24.04
  • Debian 13
  • Fedora 43

Recommended: Ubuntu LTS or Debian stable on a freshly installed server to avoid dependency conflicts.

Note: This is the very first public release of this installation script. It may contain bugs or unexpected behavior on certain configurations. If you encounter any issues, please report them. Contributions are welcome—feel free to submit a merge request to help improve the script.


Traditional Installation Process

Installing a self-hosted podcasting platform traditionally required multiple manual steps:

  1. SSH into your server
  2. Install and configure Nginx or Apache web server
  3. Install PHP with the correct version and required extensions
  4. Set up MariaDB database server
  5. Configure Redis cache server
  6. Configure PHP-FPM settings
  7. Write virtual host configuration
  8. Obtain SSL certificates with Certbot
  9. Set up certificate auto-renewal
  10. Create cron jobs for scheduled tasks
  11. Set proper file permissions and ownership
  12. Troubleshoot configuration issues

For multiple podcast instances on one server, each step needed to be repeated with careful attention to avoid conflicts between installations.

The New Way: One Command

We got tired of this. So we built a script that does it all. SSL certificates, database configuration, cron jobs, security hardening—all automated. You can be podcasting in minutes instead of hours.

⚠️ Security Disclaimer

Never blindly run scripts from the internet with sudo. That command above downloads and executes code with root privileges on your server. Only run it if:

  1. You trust the source - This script is maintained by the Castopod team and open source
  2. You've reviewed the code - Download it first and read what it does: wget https://castopod.org/install-castopod.sh
  3. You understand the scope of the modifications - The script upgrades system packages, installs software (FrankenPHP, MariaDB, Redis/Valkey, FFmpeg), modifies system configurations, and creates databases

We strongly recommend downloading the script first, reviewing it, and then running it:

# Download and review the script
wget https://castopod.org/install-castopod.sh
less install-castopod.sh  # Read through it

# Make it executable
chmod +x install-castopod.sh

# Run it after you're comfortable with what it does
sudo ./install-castopod.sh

The script is extensively commented (almost every line has an explanation) to make auditing easier.

What Makes This Special?

🏎️ Powered by FrankenPHP and Caddy

This installation script uses a modern, streamlined stack: FrankenPHP with Caddy.

What is FrankenPHP?

FrankenPHP is a modern PHP application server built on top of Caddy. It's a single binary that includes:

  • PHP runtime - No separate PHP-FPM installation needed
  • Caddy web server - Production-ready, high-performance web server
  • Worker mode - Keeps your PHP application in memory for blazing-fast responses

Why it's cool: Traditional setups require Nginx + PHP-FPM + careful socket configuration. FrankenPHP is just one binary. Download it, point it at your PHP app, done. No more debugging "502 Bad Gateway" errors because PHP-FPM crashed.

Performance: FrankenPHP can run in worker mode, keeping your application in memory between requests. This eliminates the PHP bootstrap overhead on every request—no more loading frameworks, parsing configs, and establishing database connections for each visitor. The result? Significantly faster response times and lower server load. For Castopod, this means snappier admin panels and faster feed generation.

FrankenPHP Castopod

What is Caddy?

Caddy is a modern web server that handles HTTPS automatically. Its killer feature: automatic SSL certificates.

Why it's cool: With Nginx, you need to install Certbot, run it manually, set up cron jobs for renewal, and debug certificate issues. With Caddy, you just specify your domain name. Caddy automatically:

  • Obtains certificates from Let's Encrypt
  • Renews them before they expire
  • Handles HTTPS redirects
  • Manages certificate storage

No Certbot. No cron jobs. No manual renewal. It just works.

🚀 Truly Automated

The script doesn't just install packages. It:

  • Upgrades your entire system first (security matters!)
  • Installs ALL dependencies with the correct versions (FrankenPHP with embedded PHP, MariaDB, Redis/Valkey, FFmpeg)
  • Creates unique databases for each instance with randomly generated 32-character passwords
  • Manages Redis database numbers automatically (even expands the database pool when needed!)
  • Generates Caddy configurations optimized for Castopod
  • Obtains SSL certificates automatically via Caddy's built-in ACME support (no Certbot needed!)
  • Sets up cron jobs for scheduled tasks and session cleanup
  • Configures file permissions correctly (because 777 is not the answer)
  • Locks down databases to localhost only (security by default)
  • Generates a detailed installation log with every password, URL, and config path you'll need

castopod-install-02

🎯 Interactive by Default, Silent When You Need It

Run it without arguments and you get a friendly guided experience:

sudo ./install-castopod.sh

For automation, CI/CD, or when you just want to get things done:

sudo ./install-castopod.sh -y podcast1.com podcast2.com podcast3.com

Zero prompts. Pure automation.

🏢 Multiple Instances? No Problem.

Want to host multiple podcasts on one server? Just list the domains:

sudo ./install-castopod.sh -y \
  comedy-show.com \
  tech-talk.fm \
  cooking-podcast.org

Each gets:

  • Its own directory (/var/www/castopod/DOMAIN/)
  • Its own MariaDB database and user
  • Its own Redis database number
  • Its own Caddy configuration block
  • Its own SSL certificate (automatically managed)
  • Its own cron jobs
  • Its own access and error logs

No conflicts. No shared credentials. Clean separation.

🔒 Secure by Default

We didn't just automate the installation—we hardened it:

Database Isolation

# MariaDB: Locked to localhost
bind-address = 127.0.0.1

# Redis: Locked to localhost
bind 127.0.0.1
protected-mode yes

What this means: Your databases aren't exposed to the internet. Attackers can't even reach them, let alone brute-force them.

Random Credentials Everywhere

  • Database passwords: 32 random characters
  • Installation keys: 6 random characters
  • Analytics salts: 64 random characters

No more "password123" or "castopod" defaults.

Principle of Least Privilege

FrankenPHP runs as a dedicated castopod user—not root. If the web server is ever compromised, the attacker doesn't get root access to your system.

File ownership follows the same principle:

/var/www/castopod/domain.com/
├── app/                 # owned by root (read-only)
├── public/
│   ├── index.php        # owned by root (read-only)
│   └── media/           # owned by castopod (uploads go here)
├── writable/            # owned by castopod (logs, sessions)
└── .env                 # owned by root (read-only config)

What this means: The web application can only write where it absolutely needs to. An attacker can't modify your application code, even if they find an exploit.

SSL Certificates: Zero Configuration

With Caddy, SSL certificates are truly automatic:

  1. Caddy detects your domain from the configuration
  2. Obtains certificates from Let's Encrypt on first request
  3. Stores them securely in /var/lib/castopod/caddy/
  4. Renews them automatically before expiry

No Certbot. No cron jobs. No manual commands. Just add a domain and Caddy handles everything.

Your certificates renew themselves. You'll never think about SSL again.

📦 Install Specific Versions

Need to install a specific Castopod version? Easy:

# Find available versions at:
# https://code.castopod.org/adaures/castopod/-/releases

sudo ./install-castopod.sh -y podcast.com \
  --package-url https://code.castopod.org/-/project/2/uploads/.../castopod-1.13.7.zip

In interactive mode, the script even shows you the URL to the releases page.

📝 Logging Done Right

Every installation creates two types of logs:

1. Main Installation Log

/var/log/castopod-install-20260119_143022.log

Contains:

  • Every command executed
  • All generated passwords
  • System configuration changes
  • Error messages (if any)

2. Per-Instance Logs

/var/www/castopod/podcast.example.com/installation.log

Contains:

  • Installation URL: https://podcast.example.com/ABC123-install
  • Database credentials
  • Redis configuration
  • FrankenPHP/Caddy paths
  • Cron job details
  • Debugging commands

castopod-install-03

Need to know your database password in 6 months? It's in the log. Need to find your Nginx config? It's in the log. Everything is documented.

🔧 Troubleshooting Built In

Each instance log includes a complete debugging section:

# Check application errors
sudo tail -f /var/www/castopod/podcast.com/writable/logs/*.log

# Check web server / FrankenPHP logs
journalctl -u frankenphp -f

# Check Caddy access logs
sudo tail -f /var/log/caddy/podcast.com-access.log

# Check Redis
redis-cli -h 127.0.0.1 ping

# Test scheduled tasks
sudo -u castopod /usr/local/bin/frankenphp php-cli /var/www/castopod/podcast.com/spark tasks:run

# Reload FrankenPHP configuration
sudo systemctl reload frankenphp

Copy-paste ready. No guessing about file paths.

How It Works

Step 1: System Preparation

The script detects your Linux distribution (Debian, Ubuntu, CentOS, RHEL, or Fedora) and upgrades all packages:

# Debian/Ubuntu
apt-get dist-upgrade -y

# CentOS/RHEL/Fedora
yum upgrade -y

Why? Security. You want the latest patches before installing a public-facing web application.

Step 2: Dependency Installation

Installs everything Castopod needs:

  • FrankenPHP - Modern PHP application server with built-in Caddy web server
  • MariaDB - Database server
  • Redis/Valkey - Cache server (Valkey on Fedora 41+, Redis elsewhere)
  • FFmpeg - Media processing

That's it. No separate PHP installation, no Certbot, no complex configurations. FrankenPHP bundles PHP with all required extensions and handles HTTPS automatically.

Step 3: Per-Instance Setup

For each domain you specified:

  1. Downloads Castopod (latest version or your specified URL)
  2. Extracts to /var/www/castopod/DOMAIN/
  3. Creates database with random credentials
  4. Allocates Redis database number (auto-expands pool if needed)
  5. Generates .env file with all configuration
  6. Adds domain to Caddyfile with optimized settings
  7. Sets up cron jobs in /etc/cron.d/
  8. Configures permissions (root owns files, castopod owns only writable/ and public/media/)

SSL certificates? Caddy handles that automatically when the first request comes in. No extra steps needed.

Step 4: You're Done!

The script shows you the installation URLs:

🌐 podcast.example.com
   https://podcast.example.com/ABC123-install

castopod-install-04

Open that URL, create your admin account, and start podcasting.

castopod-install-06

Real-World Examples

Example 1: Single Podcast for Personal Use

# Download the script
wget https://castopod.org/install-castopod.sh
chmod +x install-castopod.sh

# Run it (interactive mode)
sudo ./install-castopod.sh

# Follow the prompts:
# - Confirm you want to proceed
# - Enter your domain: mypodcast.com
# - Press ENTER to skip the .env file option

# Done! Open the installation URL and set up your podcast

Example 2: Podcast Network with 10 Shows

# Create a list of domains
DOMAINS="
  show1.podcast-network.com
  show2.podcast-network.com
  show3.podcast-network.com
  tech-show.podcast-network.com
  comedy-show.podcast-network.com
  interview-show.podcast-network.com
  news-show.podcast-network.com
  sports-show.podcast-network.com
  music-show.podcast-network.com
  culture-show.podcast-network.com
"

# Install all at once (non-interactive)
sudo ./install-castopod.sh -y $DOMAINS

Each show gets complete isolation. Each has its own admin panel, database, and configuration.

[SCREENSHOT: Terminal showing multiple successful installations]

Example 3: Automated Deployment with Default Settings

# Create default.env with network-wide settings
cat > default.env <<'EOF'
app.storageLimit=100
app.legalNoticeURL="https://YOUR_WEBSITE/LEGAL_PAGE"
email.protocol="smtp"
email.SMTPHost="YOUR_SMTP_GATEWAY"
email.SMTPPort=587
email.SMTPUser="SMTP_USER_NAME"
email.fromEmail="SMTP_USER_ADDRESS"
email.SMTPPass="SMTP_USER_PASSWORD"
EOF

# Deploy with defaults
sudo ./install-castopod.sh -y \
  new-show.podcast-network.com \
  --env-file default.env

The script appends default.env to each instance's .env file. Consistent configuration across all shows.

The Details That Matter

Videoclips Ready Out of the Box

Castopod lets you generate shareable videoclips from your podcast episodes—perfect for promoting your content on social media. This feature requires FFmpeg, which the script installs automatically. No extra configuration needed. Just upload your episode, select a clip, and Castopod handles the rest.

Large File Uploads: Handled

The script configures both Caddy and PHP for 512MB uploads:

# Caddyfile
request_body {
    max_size 512MB
}
# PHP (via /etc/frankenphp/conf.d/99-castopod.ini)
upload_max_filesize = 512M
post_max_size = 512M
memory_limit = 512M
max_execution_time = 300

Upload your 3-hour podcast episodes without worrying about file size limits.

Redis Database Auto-Expansion

Redis defaults to 16 databases (0-15). Installing your 17th podcast?

Traditional approach: Manually edit /etc/redis/redis.conf, increase databases, restart Redis, hope nothing breaks.

Our script: Detects you're out of databases, backs up the config, increases by 16, restarts Redis, continues installation.

You: Don't even notice.

[ILLUSTRATION: Visual showing Redis database pool automatically expanding from 16 to 32 databases]

Cron Jobs That Actually Work

We learned this the hard way: user crontabs and system cron have different formats.

The script creates cron files in /etc/cron.d/ with the correct format:

# /etc/cron.d/castopod-podcast-example-com

# Run scheduled tasks every minute (only errors are logged)
* * * * * castopod /usr/local/bin/frankenphp php-cli /var/www/castopod/podcast.example.com/spark tasks:run 2>> /var/www/castopod/podcast.example.com/writable/logs/cron-errors.log

# Clean up old sessions daily at 3 AM (only errors are logged)
0 3 * * * castopod find /var/www/castopod/podcast.example.com/writable/session/ -type f -mtime +7 -delete 2>> /var/www/castopod/podcast.example.com/writable/logs/cron-errors.log

Each instance gets its own cron file. Errors are logged to cron-errors.log for easy debugging without filling up disk space.

Separate Caddy Logs Per Domain

Ever tried to debug one site when logs from 10 sites are mixed together? It's painful.

Each instance gets separate logs:

/var/log/caddy/podcast.example.com-access.log
/var/log/caddy/podcast.example.com-error.log

Plus, you can check the FrankenPHP service logs anytime:

journalctl -u frankenphp

Debug one site without sifting through traffic from all the others.

System Requirements

  • Operating System: Ubuntu LTS or Debian stable (recommended), or Fedora (latest)
  • Root access: You'll need sudo
  • DNS configured: Your domain must point to your server's IP
  • Ports open: 80 and 443 for SSL certificate generation
  • Stable internet: The script upgrades system packages (may take a few minutes)

That's it. No special hardware. No complicated prerequisites.

FAQ

Can I install just the dependencies without any instances?

Yes! Run the script without domain names:

sudo ./install-castopod.sh -y

This installs Nginx, PHP, MariaDB, Redis, FFmpeg, and Certbot—but doesn't create any Castopod instances. Add instances later by running the script again with domain names.

Does this work on existing servers?

Yes. The script is idempotent—it won't reinstall packages that already exist. It's safe to run on servers with other websites.

What if I already have a web server installed?

FrankenPHP runs as its own service on ports 80 and 443. If you have Nginx or Apache already running on those ports, you'll need to either:

  • Stop the existing web server: sudo systemctl stop nginx
  • Or configure FrankenPHP to use different ports and set up a reverse proxy

For fresh servers, FrankenPHP handles everything. Your existing MariaDB databases won't be affected.

Can I customize the installation?

Yes. The script is thoroughly commented—almost every line has an explanation. You can modify it to fit your needs, or use the --env-file option to provide custom configuration values.

What if something goes wrong?

Check the installation log (path shown at the end of the script). It contains every command executed and all error messages. The per-instance logs also include debugging commands for common issues.

If you're stuck, the logs are extremely helpful for getting support.

How do I uninstall an instance?

The script doesn't include an uninstaller (yet), but each instance log documents what was created:

# Example cleanup for podcast.example.com

# Remove domain from Caddyfile
sudo nano /etc/caddy/Caddyfile  # Remove the podcast.example.com { ... } block
sudo systemctl reload frankenphp

# Remove installation directory
sudo rm -rf /var/www/castopod/podcast.example.com

# Remove database
sudo mysql -e "DROP DATABASE IF EXISTS \`podcast-example-com\`;"
sudo mysql -e "DROP USER IF EXISTS 'podcast-example-com'@'localhost';"

# Remove cron jobs
sudo rm /etc/cron.d/castopod-podcast-example-com

# SSL certificates are managed automatically by Caddy - no manual cleanup needed

Is this production-ready?

Yes. The script:

  • Uses stable package versions
  • Implements security best practices
  • Generates detailed logs for auditing
  • Has been tested on multiple distributions
  • Handles errors gracefully

Get Started

Download and Run

# Method 1: Direct from the web (easiest)
curl -fsSL https://castopod.org/install | sudo bash

# Method 2: Download first, review, then run (recommended for security)
wget https://castopod.org/install-castopod.sh
less install-castopod.sh  # Review the script first!
chmod +x install-castopod.sh
sudo ./install-castopod.sh

Security reminder: Method 2 is recommended. Always review scripts before running them with sudo.

What to Expect

Time: 5-15 minutes depending on your server's internet speed and whether it needs to upgrade packages.

User interaction (interactive mode):

  • Confirm you want to proceed
  • Enter domain names
  • Optionally provide a default .env file path

User interaction (non-interactive mode): Zero. Perfect for automation.

Result: A fully configured Castopod installation ready for your content.

Why We Built This

Self-hosting shouldn't be hard. Podcasters should spend time creating content, not wrestling with server configurations.

We wanted anyone with a domain name and a server to be able to install Castopod in minutes—not hours or days. We wanted the installation to be secure by default, not after you read 17 blog posts about hardening PHP applications.

This script is our answer: One command. Five minutes. Done.

Resources

Try It Today

Got a server and a domain? You're 5 minutes away from running Castopod:

curl -fsSL https://castopod.org/install | sudo bash

That's all it takes. (But remember to review the script first if you're security-conscious!)

Benjamin Bellamy

Podcasts, e-commerce & open-source. Father of Castopod. CEO of Ad Aures.