Pitch: Conectei um SSD na minha TV Box e ela virou um servidor de mídia (e o que aprendi tentando rodar um canal de TV 24h)

Quem acompanhou o artigo anterior sabe que peguei uma TV Box que estava parada e transformei ela em um homelab de observabilidade, rodando Grafana, Prometheus e companhia via Docker. O projeto deu certo e a Box vem rodando firme há um tempo. Acontece que eu tinha um SSD de 1TB parado, sem uso, e quando bateu a ideia desse novo projeto, acabei comprando um case (uma gaveta USB) no AliExpress justamente pra conseguir ligar esse SSD na Box por uma porta USB 3.0 que ela tem disponível. Com o SSD finalmente conectado, voltou aquela velha pergunta: "Será que dá pra fazer mais alguma coisa com isso aqui?".
A ideia inicial era simples: ter um servidor de arquivos na rede. Mas aí veio a parte mais ambiciosa — e que eu mais queria — "será que eu consigo colocar séries e desenhos rodando 24h, tipo um canal de TV, pra eu assistir do celular ou da própria TV?". Neste artigo vou contar como ficou essa segunda saga, dessa vez tentando transformar a mesma Box limitada num servidor de mídia caseiro.
Primeiro, um banho de realidade no hardware
Antes de sair instalando coisa, parei pra olhar com calma o que eu tinha em mãos. A Box continua sendo aquele hardware modesto: CPU Amlogic ARM64 (4 núcleos Cortex-A55) e 4GB de RAM — dos quais o Armbian só enxerga uns 3,2GB utilizáveis (o resto fica reservado pra GPU e firmware, coisa comum nessas placas ARM). Agora somou-se a isso um SSD Goldenfir de 1TB, ligado por uma gaveta USB 3.0.
O disco em si me surpreendeu pra bem — medindo a leitura, ele entregou 348 MB/s, o que é mais que suficiente. O problema estava em outro lugar: a Box vive conectada por Wi-Fi, e quando fui olhar a qualidade do link, a taxa de recepção estava em meros 6,5 Mbit/s. Ou seja: o gargalo do projeto não ia ser o disco nem a CPU, e sim a rede. Existe uma porta de rede RJ45 na Box, mas eu ainda não tenho como passar um cabo até lá. Então resolvi seguir mesmo assim, ciente de que tudo precisava funcionar dentro dessa limitação.
A lição que definiu o projeto inteiro: nada de transcodificação
Essa foi a parte que eu precisei entender antes de qualquer coisa, e é o conselho mais importante que deixo pra quem for tentar algo parecido nesse tipo de hardware.
Servidores de mídia como Jellyfin ou Plex costumam transcodificar o vídeo em tempo real — converter o arquivo na hora pra um formato que o aparelho que está assistindo consiga reproduzir. O problema é que isso é brutalmente pesado pra CPU, e o Cortex-A55 simplesmente não dá conta de fazer isso ao vivo. Cheguei a verificar se dava pra usar aceleração por hardware, mas a GPU Mali da Box só faz 3D, e o decodificador de vídeo do chip só decodifica — não existe um encoder utilizável ali. Insistir em transcodificar seria garantia de travamento e, pior, de superaquecimento.
A solução foi virar a lógica do avesso: a Box não converte nada. Eu padronizo toda a biblioteca num formato que qualquer aparelho toca direto — H.264 1080p/720p com áudio AAC —, e aí a Box só precisa ler o arquivo do disco e jogar na rede. Quem faz o trabalho de decodificar é o celular ou a TV. Se algum arquivo estiver fora desse padrão, eu converto uma vez, no PC, antes de mandar pra Box. Com isso, o consumo de CPU do servidor cai pra praticamente zero.
As ferramentas escolhidas
Definida a regra de ouro, montei a stack com quatro peças, todas em Docker, claro:
- Jellyfin para a mídia sob demanda — aquele estilo "Netflix pessoal", onde eu escolho o que assistir.
- ErsatzTV para a parte que eu mais queria: o canal 24h. Ele pega a biblioteca, monta uma grade de programação e expõe um canal de TV (no formato IPTV, com guia de programação) que eu sintonizo no celular ou na TV. É basicamente ter um canal só meu passando desenho o dia inteiro.
- Samba para acessar os arquivos pela rede de forma nativa, como uma pasta compartilhada.
- Filebrowser para gerenciar os arquivos pelo navegador, de qualquer aparelho.

Uma decisão de arquitetura importante
Aqui entrou uma sutileza que vale comentar. A minha homelab de observabilidade atualmente tem um deploy automático: quando eu faço merge na branch principal, a Box atualiza a stack sozinha. Se eu jogasse esses serviços de mídia no mesmo docker-compose.yml, dois problemas apareceriam — o deploy tentaria baixar mais de 1GB de imagens pela minha Wi-Fi lenta a cada atualização, e poderia até derrubar os containers de mídia sem querer.
Por isso, separei a stack de mídia num projeto Docker Compose próprio, com ciclo de vida independente. O arquivo continua versionado no repositório, mas eu subo e atualizo essa parte na mão, quando quero. Foi a forma de adicionar tudo isso sem bagunçar o que já estava funcionando.
A Saga do ErsatzTV (porque nunca é tão simples assim)
Subi os quatro containers, vi todos respondendo e, confesso, achei que tinha sido fácil demais. Não tinha.
Pouco depois, ao monitorar a Box, percebi que ela estava esquentando mais do que o normal e que a carga do sistema tinha disparado — só que, estranhamente, nenhum container aparecia consumindo CPU. Algo não batia. Fui investigar e descobri que o ErsatzTV reiniciava continuamente: subia, quebrava e começava tudo de novo, mantendo a Box quente.
O motivo estava nos logs: um belo System.OutOfMemoryException. No primeiro boot, o ErsatzTV roda as migrações do banco de dados, e uma delas monta o modelo inteiro de uma vez na memória. Acontece que eu tinha definido um mem_limit de 512m pra ele, e esse pico de migração estourava o limite, matando o processo antes mesmo de terminar de subir.
O detalhe sacana é que o consumo em regime do ErsatzTV é baixinho, uns 150MB. O problema era só aquele pico momentâneo do primeiro boot. A correção foi subir o teto de memória pra dar fôlego pra migração passar:
ersatztv:
...
# 1280m por causa do PICO de memória do 1º boot (migração do banco).
# Em regime ele usa ~150MB; o teto alto só serve pra esse momento.
mem_limit: 1280m
Com isso a migração completou de primeira, o container estabilizou e a Box voltou a respirar. A lição que fica: o consumo de pico durante a inicialização pode ser bem diferente do consumo do dia a dia — e num hardware apertado de RAM, é justamente esse pico que te derruba.

Hora de popular a biblioteca — e um perrengue inesperado
Com a stack de pé, faltava o principal: jogar as séries e os desenhos pra dentro. E aí esbarrei no gargalo que eu já sabia que existia — a Wi-Fi de 6,5 Mbit/s. Mandar dezenas de GB por aquela rede ia levar uma eternidade. A saída óbvia foi tirar o SSD da Box e plugar direto num computador pra copiar tudo na velocidade do USB 3.0.
Parece simples, mas teve duas pegadinhas.
A primeira: o SSD está formatado em ext4 (o sistema de arquivos do Linux). No meu Mac Mini ele nem monta — o macOS não lê ext4 nativamente, e pra escrever com segurança só com um driver pago (Paragon) que ainda exige baixar a segurança do sistema. No Windows também não é nativo (só via WSL2). Quem salvou foi meu ThinkPad com Fedora: no Linux o ext4 é nativo, leitura e escrita em velocidade total, sem instalar nada. Lição: se o disco do seu servidor é ext4, tenha um Linux à mão pra manutenção.
A segunda foi mais assustadora. Terminada a cópia, reconectei o SSD na Box — que estava ligada — e ela reiniciou na hora. No susto, achei que tinha perdido alguma coisa. Era queda de tensão: o disco puxa um pico de corrente ao energizar, e a fonte fraca da Box não aguenta, resetando tudo. Pra completar, descobri nos logs que a gaveta USB usa um chip JMicron conhecido por brigar com o driver USB do Linux (o tal do "UAS"), gerando travamentos. Os travamentos eu resolvi desativando o UAS pra aquele chip via um parâmetro de boot; já o pico de corrente só tem dois remédios de verdade — alimentar o disco por uma fonte externa ou, o que faço hoje, nunca plugar com a Box ligada (desligo, conecto, ligo). Por sorte o ext4 tem journal e nada corrompeu. Uma curiosidade boa que aprendi no caminho: como os containers usam restart: unless-stopped, depois de um reboot desses a stack volta sozinha — eu não preciso subir nada na mão.
O canal 24h: onde o hardware finalmente disse "não"
Chegou a parte que eu mais queria — e a que mais me ensinou sobre os limites da Box.
Configurei tudo no ErsatzTV com capricho: apontei a biblioteca de séries, criei uma coleção, montei a grade no modo que enche as 24 horas em loop. Tinha até um detalhe legal: meus episódios são dual-áudio (japonês e português), e dava pra forçar o português dizendo ao canal qual idioma eu prefiro — e funcionou, ele escolhe a faixa certa sozinho. No papel, estava tudo pronto.

Aí fui assistir. Tela preta.
Comecei a investigar e abri uma toca de coelho. O ErsatzTV, no modo padrão, re-encoda o vídeo pra montar o canal — exatamente o que a regra de ouro mandava evitar. E pior: como meus desenhos antigos são em definição padrão (960x720, 4:3) e o canal estava configurado pra 1080p, ele ainda fazia um upscale por cima. Resultado: o ffmpeg da Box a 162% de CPU, sem nunca conseguir entregar o vídeo em tempo real.
Descobri que o ErsatzTV tem um modo de cópia pura (sem transcodificar) e fui atrás. E aqui veio a parte fascinante: nesse modo a Box serviu o stream só copiando o arquivo, a 4% de CPU, com o áudio em português certinho. Tecnicamente, funcionava! Só que o player não tocava.
Esse modo de cópia gera uma lista de reprodução com um único "pedaço" do tamanho do episódio inteiro, e o VLC (e todo player que testei) trava tentando baixar esse pedaço gigante antes de começar. Tentei ainda um terceiro modo, de stream contínuo — esse voltava a transcodificar e demorava tanto pra iniciar que o player desistia.
E tinha um agravante que eu nem tinha notado: quando eu tentava assistir pelo próprio Jellyfin (que também sintoniza canais IPTV), ele re-transcodava o stream do ErsatzTV — ou seja, transcodificação dupla, ErsatzTV e Jellyfin ao mesmo tempo, jogando a carga da Box pra 16 e a temperatura pra 82°C.

Foi aí que a ficha caiu. O problema não era um botão errado em lugar nenhum. Era que a Box não tem folga: parada, com a observabilidade e o kiosk já rodando, ela vive perto do limite de CPU e quente. Não sobra fôlego pra servir um canal ao vivo — nem transcodificando (que ela não dá conta), nem copiando (que os players não engolem nesse formato).
Conclusão
Decidi parar por aqui, e com honestidade. O servidor de mídia sob demanda foi um sucesso: assisto minhas séries pelo Jellyfin, em Direct Play (sem transcodificação), e roda liso no celular, no PC e na TV — é exatamente pra isso que esse hardware serve bem. O Samba e o Filebrowser também cumprem o papel de servidor de arquivos sem reclamar, consumindo só uns 350MB de RAM a mais em regime.
O canal de TV 24h ao vivo, por outro lado, foi a única peça que essa TV Box não conseguiu entregar de forma usável — e está tudo bem, faz parte respeitar os limites do hardware. A ideia não morreu: a beleza do ErsatzTV é que ele é só mais um container. Na hora que eu colocar a mão num hardware um pouco mais parrudo (um mini-PC ou um Raspberry Pi 4/5), subo ele lá apontando pra mesma biblioteca da Box pela rede, e o canal passa a rodar — sem tirar nada do que já funciona aqui. Por enquanto, pauso o canal e sigo com o resto.
Fica a maior lição de todas: num hardware tão simples, descobrir o que ele NÃO consegue fazer é tão valioso quanto descobrir o que ele consegue. Ainda tenho próximos passos na lista — passar um cabo de rede pra matar o gargalo da Wi-Fi e configurar o Tailscale pra acessar tudo de fora de casa com segurança (de propósito, deixei o acesso externo por VPN, e não exposto na internet). Mas a base está de pé: a mesma TV Box que monitora uma API agora também guarda e serve minha biblioteca de mídia. O canal de desenho 24h fica pra próxima — e, quando vier, já sei exatamente onde vai rodar.
Os arquivos completos desta parte de mídia — o Docker Compose, os limites de recursos e um tutorial passo a passo pra você montar a sua (incluindo como transferir arquivos e assistir do Mac, Windows, Linux e Smartphones) — estão no repositório deste projeto: https://github.com/kevinjh07/homelab-media-box.
Curtiu a ideia de reaproveitar uma TV Box parada desse jeito? Deixe um comentário — e se quiser ver como ficou a base de observabilidade que começou tudo, o código está no repositório do projeto anterior.