← Blog

Self-host do Castopod pra publicar um podcast

Railyard

6 de setembro de 2026 · 7 min de leitura

Siga isso no seu próprio servidor. Comece grátis, sem cartão

A maioria dos hosts de podcast alugam um feed RSS pra você: pare de pagar e os seus episódios, as suas análises e a sua própria URL de feed podem todos desaparecer junto com a conta. O Castopod é um host de podcast open-source, construído sobre o CodeIgniter, que te dá o mesmo workflow de publicação, análises reais de ouvintes, e funcionalidades do Podcasting 2.0 como transcrições e capítulos, com o feed morando no seu próprio domínio em vez do de um fornecedor. No final deste guia você vai ter ele rodando com MariaDB e Redis atrás de HTTPS, o seu primeiro programa publicado, e um backup que de fato cobre os arquivos de áudio, não só o banco de dados.

O que você vai precisar

  • Um servidor com Docker e o plugin Compose. Um Hetzner CX23 (2 vCPU, 4 GB de RAM, 40 GB NVMe, €5,99/mês sem IVA) roda o Castopod confortavelmente; planeje disco real além dos 40 GB padrão quando você estiver hospedando mais do que um punhado de episódios, arquivos de áudio se acumulam mais rápido do que a maioria das aplicações self-hosted.
  • Um nome de domínio pro qual você pode apontar um registro A. O Castopod exige HTTPS pra federar corretamente com o fediverso (seguidores e comentários de podcast), então isso não é opcional do jeito que poderia ser pra uma ferramenta interna.
  • Acesso SSH ao servidor.

Passo 1: aponte o DNS e abra o firewall

ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw --force enable

Crie um registro A pro seu domínio apontando pro IP público do servidor, e dê alguns minutos antes do Passo 5.

Passo 2: gere os secrets

openssl rand -hex 24   # MYSQL_ROOT_PASSWORD
openssl rand -hex 24   # MYSQL_PASSWORD
openssl rand -hex 24   # REDIS_PASSWORD
openssl rand -hex 16   # CP_ANALYTICS_SALT
mkdir -p /opt/castopod
cat > /opt/castopod/.env <<'EOF'
MYSQL_ROOT_PASSWORD=paste-the-first-value-here
MYSQL_PASSWORD=paste-the-second-value-here
REDIS_PASSWORD=paste-the-third-value-here
CP_ANALYTICS_SALT=paste-the-fourth-value-here
EOF
chmod 600 /opt/castopod/.env

CP_ANALYTICS_SALT é o que o Castopod usa pra fazer hash dos endereços IP dos ouvintes antes de contar um download, te dando análises agregadas reais sem armazenar IPs crus; isso precisa manter o mesmo valor pela vida da instância, mudar ele reseta como ouvintes recorrentes são deduplicados.

Passo 3: escreva o arquivo Compose

services:
  castopod:
    image: castopod/castopod:1.15.5
    restart: unless-stopped
    env_file: .env
    security_opt:
      - no-new-privileges:false
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      CP_BASEURL: https://podcast.yourdomain.com
      CP_DATABASE_HOSTNAME: db
      CP_DATABASE_NAME: castopod
      CP_DATABASE_USERNAME: castopod
      CP_DATABASE_PASSWORD: ${MYSQL_PASSWORD}
      CP_REDIS_HOST: redis
      CP_REDIS_PASSWORD: ${REDIS_PASSWORD}
    volumes:
      - castopod_media:/app/public/media
    expose:
      - "8080"

  db:
    image: mariadb:12.1
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: castopod
      MYSQL_USER: castopod
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - castopod_db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7.2.5-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - castopod_redis_data:/data

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - castopod

volumes:
  castopod_media:
  castopod_db_data:
  castopod_redis_data:
  caddy_data:
  caddy_config:

Este guia fixa 1.15.5, o release estável atual no momento em que isso foi escrito, e mariadb:12.1, o motor de banco de dados que a própria documentação do Castopod documenta em vez do Postgres. security_opt: no-new-privileges:false é obrigatório, não opcional: a imagem do Castopod roda como um usuário não-root www-data e precisa fazer chown em /run na própria inicialização dela, que a proteção padrão no-new-privileges do kernel bloqueia, fazendo o container falhar fatalmente com s6-overlay-suexec: child failed with exit code 100 se deixado ativo. O próprio script de entrypoint do Castopod, não um arquivo de configuração que você escreve, gera o .env real dele internamente a partir das variáveis CP_* que este guia define, em toda inicialização.

Cheque se o arquivo é interpretado:

docker compose config

Passo 4: adicione o Caddy e inicie a stack

podcast.yourdomain.com {
	reverse_proxy castopod:8080
}
docker compose up -d
docker compose ps
curl -I https://podcast.yourdomain.com

Você deve ver todo serviço Up (db como healthy) e o curl retornar HTTP/2 200.

Passo 5: crie a sua conta e o primeiro podcast

Visite https://podcast.yourdomain.com/cp-install e siga o assistente de configuração pra criar a primeira conta de administrador, nada vem pré-populado. No painel de admin, crie um podcast, preencha os metadados dele, e publique o seu primeiro episódio enviando um arquivo de áudio; o Castopod transcodifica e gera a entrada RSS automaticamente. O seu feed é publicado em https://podcast.yourdomain.com/@<your-podcast-handle>/feed.xml, a URL pra enviar pro Apple Podcasts, Spotify e qualquer outro diretório.

Passo 6: faça backup do banco de dados e do volume de mídia

Arquivos de áudio e um banco de dados MariaDB crescendo os dois importam aqui, e o volume de mídia geralmente é o maior:

cat > /opt/castopod/backup.sh <<'EOF'
#!/bin/sh
cd /opt/castopod
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
docker compose exec -T db mariadb-dump -u root -p"${MYSQL_ROOT_PASSWORD}" castopod | gzip > backups/db-$TIMESTAMP.sql.gz
find backups -mtime +14 -delete
EOF
mkdir -p /opt/castopod/backups
chmod +x /opt/castopod/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/castopod/backup.sh") | crontab -

Rode manualmente uma vez e cheque se ls -lh backups mostra um arquivo com data. O dump do banco de dados por si só não cobre o áudio enviado em castopod_media; faça backup disso com qualquer ferramenta de snapshot de disco completo que o seu provedor de servidor oferece, ou um rsync periódico pra outra máquina, já que um tar de mídia sendo servida ativamente enquanto episódios estão no ar é o tipo de coisa que vale agendar pra horários de baixo tráfego.

Passo 7: faça o upgrade

docker compose pull castopod
docker compose up -d castopod

O Castopod roda as próprias migrations de banco de dados automaticamente na inicialização quando a versão da imagem muda. Cheque as notas de release da versão pra qual você está indo, e confirme que docker compose logs castopod mostra uma inicialização limpa sem erros de migration antes de considerar o upgrade concluído.

O caminho mais rápido: faça isso com a Railyard

Tudo acima, MariaDB e Redis conectados automaticamente, a correção de no-new-privileges que a própria imagem do Castopod precisa pra subir corretamente, um volume dedicado pro áudio enviado, também é o que o template de stack Castopod da Railyard configura pra você. Conecte um servidor, depois escolha Castopod no catálogo de templates: app.railyard.run/deploy?stack=castopod. O HTTPS é tratado do mesmo jeito que o Passo 4 faz manualmente, que também satisfaz o próprio requisito do Castopod pra federação no fediverso.

Veja deploy with a button e databases and backups pra como templates e dados persistentes funcionam na plataforma de forma mais geral.

Resolução de problemas

O container sai com s6-overlay-suexec: child failed with exit code 100. no-new-privileges ainda está ativado, cheque se security_opt no Passo 3 está de fato definido como no-new-privileges:false especificamente no serviço castopod, esse é o único padrão de hardening que precisa ser relaxado pra essa imagem.

O MariaDB registra “Host X is not allowed to connect” só na primeira inicialização. Isso aparece se a CPU do servidor está pesadamente carregada exatamente quando o MariaDB está criando os usuários e grants iniciais dele, interrompendo esse processo. Pare a stack, remova o volume castopod_db_data, e rode docker compose up -d de novo num momento mais tranquilo; não existe dado real pra perder ainda se isso acontecer numa instalação genuinamente nova.

Áudio enviado falha com um erro de permissão. Cheque a propriedade dentro do container com docker compose exec castopod ls -la /app/public/media; isso aparece mais frequentemente depois de restaurar um backup como root quando o container roda como www-data.

HTTPS não é emitido. docker compose logs caddy mostrando erros de ACME repetidos quase sempre significa que o DNS do Passo 1 ainda não se propagou ou aponta pro IP errado.

O disco enchendo mais rápido do que esperado. Arquivos de áudio se acumulam rápido entre episódios; du -sh castopod_media (ou o caminho equivalente de docker volume inspect) mostra o que de fato está consumindo espaço, e redimensionar o disco do servidor antes de acabar é mais simples do que migrar um volume completo sob pressão.

Perguntas frequentes

O Castopod exige um provedor de SMTP como algumas outras aplicações self-hosted exigem? Não pra publicar um podcast ou pra moderação central funcionar; o SMTP só é necessário se você ativar notificações de email opcionais, que é um requisito muito mais leve do que aplicações onde a própria confirmação de conta depende disso.

Eu consigo migrar o feed RSS de um podcast existente pro Castopod? Sim, o Castopod suporta importar episódios de uma URL de feed existente durante a criação do podcast, que preserva o histórico de episódios em vez de começar do zero.

A integração com o fediverso é obrigatória? Não, podcasts funcionam como um feed RSS e player web normal sem ela; a federação (seguidores e comentários de apps estilo Mastodon) é uma funcionalidade adicional em cima, não um requisito pra publicar.

Como funciona análise real sem rastrear ouvintes individualmente? O Castopod implementa a especificação de analytics do Podcasting 2.0, fazendo hash de dados identificadores como endereços IP com o salt do Passo 2 antes de contar um download, então você tem números reais de download e geográficos sem armazenar nada que identifique um ouvinte individual.

Qual é o crescimento realista de disco pra um programa ativo? Um episódio semanal de uma hora num bitrate razoável é dezenas de megabytes por episódio; isso se acumula lentamente pra maioria dos programas, mas vídeo ou áudio de bitrate muito alto muda essa conta rápido, cheque os tamanhos reais de arquivo contra o servidor de “O que você vai precisar” antes de assumir que os 40 GB padrão são suficientes pra um ano.

Depois que MariaDB, Redis e HTTPS estão no lugar, publicar um episódio é os mesmos poucos cliques que seria numa plataforma hospedada, o trabalho contínuo real é o hábito de backup do Passo 6 e ficar de olho no espaço em disco conforme o programa cresce.

Por que escolhem o Railyard para isso

  • Sua própria conta de cloud. Faça deploy no DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou em qualquer servidor Linux via SSH, cobrado direto pelo seu provedor, sem markup.
  • Um preço fixo por equipe. O Free é grátis para sempre: um servidor mais um servidor de banco de dados, três apps, sem cartão. O Pro custa US$19 por mês para até cinco servidores e apps ilimitados. O Team custa US$49 por mês com servidores ilimitados. Sem cobrança por app, por deploy, por assento ou por gigabyte.
  • Templates de um clique. Faça deploy de apps open source, como bancos de dados, analytics e ferramentas de dev, no seu próprio servidor em um clique.
  • Bancos de dados gerenciados. Postgres, MySQL e Redis com backups programados, verificados por restauração, e recuperação point-in-time.
  • Push-to-deploy. Faça deploy a partir do GitHub, GitLab ou da CLI, com HTTPS automático e releases sem downtime.

Faça deploy do Castopod em um clique

Escolha Castopod no catálogo de templates da Railyard e tenha MariaDB e Redis já conectados pro seu programa.

Plano grátis, sem cartão, e 14 dias de Pro para cada equipe nova. Um preço fixo por equipe.