Imported from hadagalberto/media-stack (
AGENTS.md). Install upstream withnpx skills add hadagalberto/media-stack. Copyright stays with the author.
AGENTS.md — guia pra IA instalar essa stack
Instruções pra qualquer agente de IA (Claude Code, Cursor, Aider, ChatGPT com execução de comandos, etc.) ajudar um humano a instalar e configurar esta stack. Leia isto inteiro antes de rodar qualquer comando.
O que é isso
Stack Docker Compose de streaming privado: Jellyfin + Radarr/Sonarr/Prowlarr
(automação de download) + qBittorrent atrás de VPN (gluetun) + Caddy (HTTPS
automático via Let's Encrypt, opcional). Um único docker-compose.yml, com
duas escolhas independentes via Docker Compose profiles — não são
arquivos separados, é o mesmo compose ligando/desligando serviços:
- Pacote de serviços —
COMPOSE_PROFILESincluifullou não:- Básica (sem
full): Jellyfin, Radarr, Sonarr, Prowlarr, gluetun+qBittorrent, unpackerr — 7 serviços, o essencial. - Completa (
full): Básica + Bazarr, Jellyseerr, Homepage, recyclarr, decluttarr, Jellystat, Dozzle, Uptime Kuma, FileBrowser, Apprise.
- Básica (sem
- Como acessar —
COMPOSE_PROFILESincluiproxyou não:- Com domínio (
proxy): sobe o Caddy, HTTPS automático via subdomínios (radarr.seudominio.com, etc.). - Sem domínio (sem
proxy): Caddy não sobe. Acesso direto porIP:porta, usando o arquivo extradocker-compose.ports.yml(ver seção 5 abaixo). Sem HTTPS.
- Com domínio (
As duas combinam com vírgula: COMPOSE_PROFILES=full,proxy,
COMPOSE_PROFILES=proxy, COMPOSE_PROFILES=full, ou vazio (Básica sem
domínio). Pergunte ao humano antes de começar as duas escolhas (ou deixe
o ./configure.sh perguntar — ver abaixo). A pergunta "tem domínio?" importa
bastante pra segurança — ver seção "Nunca faça isso sem confirmação".
Ordem de execução (não pule etapas)
-
Pré-requisitos — confirme com o humano, não assuma:
- Conta de VPN WireGuard (ex.: ProtonVPN) — precisa da chave privada. Obrigatório nos dois modos.
- Espaço em disco num único filesystem pra
DATA_ROOT(hardlink entre torrents e biblioteca depende disso — se forem discos diferentes, importar duplica o espaço em vez de linkar). - Pergunte: "você tem um domínio apontando pro servidor?"
- Se sim: precisa de DNS wildcard (
A seudominio.comeA *.seudominio.com) e portas 80/443 livres (Caddy usa as duas). - Se não: nada disso é necessário — modo IP + portas (ver passo 5). Pergunte também se o servidor tem IP público — isso muda o que você recomenda no passo 5.
- Se sim: precisa de DNS wildcard (
-
./setup.sh— instala Docker/Compose (idempotente, detecta se já tem), cria${DATA_ROOT}/{torrents,media}, copia os.example→ arquivos reais (.env,Caddyfile,config/) se ainda não existirem (não sobrescreve nada que já foi editado). -
Preencha o
.env— duas formas, escolha UMA:- Interativo com o humano: rode
./configure.she deixe ele responder as perguntas (Básica/Completa, domínio/IP, VPN, etc.). É um wizardread -pcomum — se você (a IA) estiver rodando em modo não-interativo sem conseguir repassar stdin ao humano, use a segunda opção. - Você preenche direto: pergunte ao humano os valores (domínio se
tiver, e-mail, provedor VPN + chave WireGuard, timezone) e edite o
.envvocê mesma com base no.env.example— os comentários explicam cada variável, incluindoCOMPOSE_PROFILES(combinefulle/ouproxycom vírgula). Nunca invente valores de segredo (chave VPN, senhas) — pergunte, não adivinhe.
- Interativo com o humano: rode
-
Cole o hash da senha no
Caddyfile— só se o humano tem domínio (pulou pra IP + portas? pule este passo,Caddyfilenão é usado). Gere com:docker run --rm caddy:latest caddy hash-password --plaintext 'SENHA_ESCOLHIDA_PELO_HUMANO'Cole o hash em
(adminauth)noCaddyfile(não no.env). Nunca escolha a senha por conta própria — pergunte ao humano ou gere e mostre pra ele guardar. -
Suba a stack — comando muda conforme domínio ou não:
# Com domínio (COMPOSE_PROFILES tem "proxy"): docker compose up -d # Sem domínio (IP + portas): docker compose -f docker-compose.yml -f docker-compose.ports.yml up -dDepois:
docker compose logs -f(Ctrl+Csai sem derrubar containers).No modo sem domínio, antes de rodar isso: se o humano confirmou que o servidor tem IP público (não é só rede local/VPN pessoal), avise que Radarr/Sonarr/Prowlarr/Bazarr não têm login por padrão nesse modo (o Caddy que fazia Basic Auth não sobe) — recomende ativar
Settings → General → Security → Authentication → "Forms (Login Page)"em cada um antes ou logo depois de subir. Não decida sozinha por ele, mas não deixe de avisar. -
Verifique a saúde antes de dizer que terminou:
docker compose ps # todos "Up", gluetun "healthy" docker compose logs gluetun --tail 30 # VPN conectou? erro de chave?Se
gluetunnão ficarhealthy, oqbittorrentnão sobe (kill switch arquitetural, não é bug) — o problema quase sempre é a chave WireGuard errada no.env. Se for modo com domínio, confira tambémdocker compose logs caddy --tail 30(certificado emitido?). -
Interligação dos serviços — isso não é automático, precisa entrar em cada UI web e conectar. Siga a seção "🔌 Interligação dos serviços" do
README.mdna ordem: qBittorrent → Prowlarr → Radarr/Sonarr → Jellyfin → (Completa: Jellyseerr → Bazarr → extras). URLs de acesso: com domínio,servico.seudominio.com; sem domínio,http://IP:PORTA(tabela na seção "Sem domínio" doREADME.md). Cada API key copiada de um serviço vai pro.envou direto na UI do outro — peça pro humano copiar/colar as API keys vendo a tela dele, ou navegue você mesma se tiver acesso de browser/API.
Nunca faça isso sem confirmação explícita
docker compose down -v— o-vapaga os volumes (bancos do Jellystat, configs).docker compose downsozinho é seguro (mantém dados).- Editar/apagar qualquer coisa dentro de
${DATA_ROOT}sem perguntar — é a biblioteca de mídia do humano. - Inventar ou gerar sozinha: senha de admin do Caddy, chave VPN, domínio. Esses valores têm que vir do humano.
- Commitar
.env,Caddyfile(sem ser o.example) ouconfig/— já estão no.gitignore, não forcegit add -fneles. - Trocar
RADARR_API_KEY/SONARR_API_KEYregenerando a chave na UI depois de já interligado — quebra Prowlarr/Jellyseerr/Homepage que já usam a chave antiga. - Subir o modo IP + portas num servidor com IP público sem antes avisar que Radarr/Sonarr/Prowlarr/Bazarr ficam sem login (não tem o Basic Auth que o Caddy dava) — pergunte se é rede local/VPN pessoal antes de tratar isso como "pronto, terminou".
Erros comuns (confira antes de reportar "quebrado")
| Sintoma | Causa provável | Onde olhar |
|---|---|---|
qbittorrent não sobe |
gluetun unhealthy |
docker compose logs gluetun — chave WireGuard |
| Caddy não emite HTTPS | DNS não aponta pro IP, ou portas 80/443 ocupadas | dig seudominio.com, sudo ss -tlnp | grep -E ':80|:443' |
caddy nem aparece no docker compose ps |
Modo IP + portas (proposital) OU esqueceram proxy no COMPOSE_PROFILES |
confira .env — se o humano quer domínio, falta proxy no COMPOSE_PROFILES |
| Subdomínio da Completa dá 502 | Instalação é Básica, serviço não existe | confira docker compose ps — normal se for Básica de propósito |
http://IP:porta não abre no modo sem domínio |
Esqueceu o -f docker-compose.ports.yml no up -d, ou firewall bloqueando a porta |
rode com os dois -f; confira sudo ufw status / regras de firewall do provedor |
| Download não importa | Root folder errado no Radarr/Sonarr, ou API key do qBit não bate | Settings → Media Management / Download Clients |
docker compose up -d falha por permissão |
Usuário não está no grupo docker ainda |
newgrp docker ou logout/login (o setup.sh já tentou adicionar) |
Onde ficam as coisas
.env— todos os segredos e escolha de perfil (COMPOSE_PROFILES:full= pacote completo,proxy= tem domínio/Caddy).docker-compose.ports.yml— override só usado no modo sem domínio (-f docker-compose.yml -f docker-compose.ports.yml).Caddyfile— hash da senha de admin + mapeamento de subdomínios (só existe/importa no modo com domínio).config/<serviço>/— dados/config persistente de cada container.${DATA_ROOT}/torrents/e${DATA_ROOT}/media/— mídia de verdade (fora do repo, definido no.env).README.md— documentação completa pra humano; use como referência mais detalhada quando este arquivo não cobrir o caso.